##// END OF EJS Templates
Removed "profiles"... Templates that are shipped with nbconvert by default should...
Jonathan Frederic -
Show More
@@ -1,306 +1,387 b''
1 1 """Exporter for the notebook conversion pipeline.
2 2
3 3 This module defines Exporter, a highly configurable converter
4 4 that uses Jinja2 to export notebook files into different format.
5 5
6 6 You can register both pre-transformers that will act on the notebook format
7 7 befor conversion and jinja filter that would then be availlable in the templates
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (c) 2013, the IPython Development Team.
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21 from __future__ import print_function, absolute_import
22 22
23 23 # Stdlib imports
24 24 import io
25 25 import os
26 26 import re
27 27
28 28 # IPython imports
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.nbformat import current as nbformat
31 31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Bool
32 32 from IPython.utils.text import indent
33 33
34 34 # other libs/dependencies
35 35 from jinja2 import Environment, FileSystemLoader
36 36 from markdown import markdown
37 37
38 38 # local import (pre-transformers)
39 39 from exceptions import ConversionException
40 40 from . import transformers as trans #TODO
41 41 from .latex_transformer import (LatexTransformer) #TODO
42 42 from .utils import markdown2rst #TODO
43 43 from .utils import markdown2latex #TODO
44 44 from .utils import highlight2latex #TODO
45 45 from .utils import get_lines #TODO
46 46 from .utils import remove_ansi #TODO
47 47 from .utils import highlight, ansi2html #TODO
48 48 from .latex_transformer import rm_math_space #TODO
49 49 import .utils.strings as strings
50 50
51 51 #Jinja2 filters
52 52 from .jinja_filters import (python_comment,
53 53 rm_fake,
54 54 escape_tex, FilterDataType,
55 55 rm_dollars
56 56 )
57 57
58 58 #Try to import the Sphinx exporter. If the user doesn't have Sphinx isntalled
59 59 #on his/her machine, fail silently.
60 60 try:
61 61 from .sphinx_transformer import (SphinxTransformer) #TODO
62 62 except ImportError:
63 63 SphinxTransformer = None
64 64
65 65 #-----------------------------------------------------------------------------
66 66 # Globals and constants
67 67 #-----------------------------------------------------------------------------
68 68
69 69 #Standard Jinja2 environment constants
70 70 TEMPLATE_PATH = "/../templates/"
71 71 TEMPLATE_SKELETON_PATH = "/../templates/skeleton/"
72 72 TEMPLATE_EXTENSION = ".tpl"
73 73
74 74 #Latex Jinja2 constants
75 75 LATEX_TEMPLATE_PATH = "/../templates/tex/"
76 76 LATEX_TEMPLATE_SKELETON_PATH = "/../templates/tex/skeleton/"
77 77 LATEX_TEMPLATE_EXTENSION = ".tplx"
78 78
79 79 #Special Jinja2 syntax that will not conflict when exporting latex.
80 80 LATEX_JINJA_COMMENT_BLOCK = ["((=", "=))"]
81 81 LATEX_JINJA_VARIABLE_BLOCK = ["(((", ")))"]
82 82 LATEX_JINJA_LOGIC_BLOCK = ["((*", "*))"]
83 83
84 84 #Jinja2 extensions to load.
85 85 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
86 86
87 87 #Latex substitutions for escaping latex.
88 88 LATEX_SUBS = (
89 89 (re.compile(r'\\'), r'\\textbackslash'),
90 90 (re.compile(r'([{}_#%&$])'), r'\\\1'),
91 91 (re.compile(r'~'), r'\~{}'),
92 92 (re.compile(r'\^'), r'\^{}'),
93 93 (re.compile(r'"'), r"''"),
94 94 (re.compile(r'\.\.\.+'), r'\\ldots'),
95 95 )
96 96
97 97 #-----------------------------------------------------------------------------
98 98 # Classes and functions
99 99 #-----------------------------------------------------------------------------
100 100 class Exporter(Configurable):
101 101 """ A Jinja2 base converter templates
102 102
103 103 Preprocess the ipynb files, feed it throug jinja templates,
104 104 and spit an converted files and a data object with other data
105 105 should be mostly configurable
106 106 """
107 107
108 108 pre_transformer_order = List(['haspyout_transformer'],
109 109 config=True,
110 110 help= """
111 111 An ordered list of pre transformer to apply to the ipynb
112 112 file before running through templates
113 113 """
114 114 )
115 115
116 116 tex_environement = Bool(
117 117 False,
118 118 config=True,
119 119 help=" Whether or not the user is exporting to latex.")
120 120
121 121 template_file = Unicode(
122 122 '', config=True,
123 123 help="Name of the template file to use")
124 124
125 fileext = Unicode(
126 'txt', config=True,
127 help="Extension of the file that should be written to disk"
128 )
129
130 stdout = Bool(
131 True, config=True,
132 help="""Whether to print the converted ipynb file to stdout
133 "use full do diff files without actually writing a new file"""
134 )
135
136 write = Bool(
137 False, config=True,
138 help="""Should the converted notebook file be written to disk
139 along with potential extracted resources."""
140 )
141
125 142 #Processors that process the input data prior to the export, set in the
126 143 #constructor for this class.
127 144 preprocessors = []
128 145
129 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
146 def __init__(self, preprocessors={}, jinja_filters={}, config=None, export_format, **kw):
130 147 """ Init a new converter.
131 148
132 149 config: the Configurable config object to pass around.
133 150
134 151 preprocessors: dict of **availlable** key/value function to run on
135 152 ipynb json data before conversion to extract/inline file.
136 153 See `transformer.py` and `ConfigurableTransformers`
137 154
138 155 set the order in which the transformers should apply
139 156 with the `pre_transformer_order` trait of this class
140 157
141 158 transformers registerd by this key will take precedence on
142 159 default one.
143 160
144 161 jinja_filters: dict of supplementary jinja filter that should be made
145 162 availlable in template. If those are of Configurable Class type,
146 163 they will be instanciated with the config object as argument.
147 164
148 165 user defined filter will overwrite the one availlable by default.
149 166 """
150 super(ConverterTemplate, self).__init__(config=config, **kw)
167
168 #Merge default config options with user specific override options.
169 default_config = self._get_default_options()
170 if not config == None:
171 default_config._merge(config)
172 config = default_config
173
174 #Call the base class constructor
175 super(Exporter, self).__init__(config=config, **kw)
151 176
152 177 #Create a Latex environment if the user is exporting latex.
153 178 if self.tex_environement:
154 179 self.ext = LATEX_TEMPLATE_EXTENSION
155 180 self.env = Environment(
156 181 loader=FileSystemLoader([
157 182 os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_PATH,
158 183 os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_SKELETON_PATH,
159 184 ]),
160 185 extensions=JINJA_EXTENSIONS
161 186 )
162 187
163 188 #Set special Jinja2 syntax that will not conflict with latex.
164 189 self.env.block_start_string = LATEX_JINJA_LOGIC_BLOCK[0]
165 190 self.env.block_end_string = LATEX_JINJA_LOGIC_BLOCK[1]
166 191 self.env.variable_start_string = LATEX_JINJA_VARIABLE_BLOCK[0]
167 192 self.env.variable_end_string = LATEX_JINJA_VARIABLE_BLOCK[1]
168 193 self.env.comment_start_string = LATEX_JINJA_COMMENT_BLOCK[0]
169 194 self.env.comment_end_string = LATEX_JINJA_COMMENT_BLOCK[1]
170 195
171 196 else: #Standard environment
172 197 self.ext = TEMPLATE_EXTENSION
173 198 self.env = Environment(
174 199 loader=FileSystemLoader([
175 200 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH,
176 201 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH,
177 202 ]),
178 203 extensions=JINJA_EXTENSIONS
179 204 )
180 205
181 206 for name in self.pre_transformer_order:
182 207 # get the user-defined transformer first
183 208 transformer = preprocessors.get(name, getattr(trans, name, None))
184 209 if isinstance(transformer, MetaHasTraits):
185 210 transformer = transformer(config=config)
186 211 self.preprocessors.append(transformer)
187 212
188 213 #For compatibility, TODO: remove later.
189 214 self.preprocessors.append(trans.coalesce_streams)
190 215 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
191 216 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
192 217 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
193 218 self.preprocessors.append(LatexTransformer(config=config))
194 219
195 220 #Only load the sphinx transformer if the file reference worked
196 221 #(Sphinx dependencies exist on the user's machine.)
197 222 if SphinxTransformer:
198 223 self.preprocessors.append(SphinxTransformer(config=config))
199 224
200 225 #Add filters to the Jinja2 environment
201 226 self.env.filters['filter_data_type'] = FilterDataType(config=config)
202 227 self.env.filters['pycomment'] = _python_comment
203 228 self.env.filters['indent'] = indent
204 229 self.env.filters['rm_fake'] = _rm_fake
205 230 self.env.filters['rm_ansi'] = remove_ansi
206 231 self.env.filters['markdown'] = markdown
207 232 self.env.filters['ansi2html'] = ansi2html
208 233 self.env.filters['markdown2latex'] = markdown2latex
209 234 self.env.filters['markdown2rst'] = markdown2rst
210 235 self.env.filters['get_lines'] = get_lines
211 236 self.env.filters['wrap'] = strings.wrap
212 237 self.env.filters['rm_dollars'] = strings.strip_dollars
213 238 self.env.filters['rm_math_space'] = rm_math_space
214 239 self.env.filters['highlight2html'] = highlight
215 240 self.env.filters['highlight2latex'] = highlight2latex
216 241
217 242 #Latex specific filters
218 243 if self.tex_environement:
219 244 self.env.filters['escape_tex'] = _escape_tex
220 245 self.env.filters['highlight'] = highlight2latex
221 246 else:
222 247 self.env.filters['highlight'] = highlight
223 248
224 249 #Load user filters. Overwrite existing filters if need be.
225 250 for key, user_filter in jinja_filters.iteritems():
226 251 if isinstance(user_filter, MetaHasTraits):
227 252 self.env.filters[key] = user_filter(config=config)
228 253 else:
229 254 self.env.filters[key] = user_filter
230 255
231 256 #Load the template file.
232 257 self.template = self.env.get_template(self.template_file+self.ext)
233 258
234 259
235 260 def export(self, nb):
236 261 """Export notebook object
237 262
238 263 nb: Notebook object to export.
239 264
240 265 Returns both the converted ipynb file and a dict containing the
241 266 resources created along the way via the transformers and Jinja2
242 267 processing.
243 268 """
244 269
245 270 nb, resources = self._preprocess(nb)
246 271 return self.template.render(nb=nb, resources=resources), resources
247 272
248 273
249 274 def from_filename(self, filename):
250 275 """Read and export a notebook from a filename
251 276
252 277 filename: Filename of the notebook file to export.
253 278
254 279 Returns both the converted ipynb file and a dict containing the
255 280 resources created along the way via the transformers and Jinja2
256 281 processing.
257 282 """
258 283 with io.open(filename) as f:
259 284 return self.export(nbformat.read(f, 'json'))
260 285
261 286
262 287 def from_file(self, file_stream):
263 288 """Read and export a notebook from a filename
264 289
265 290 file_stream: File handle of file that contains notebook data.
266 291
267 292 Returns both the converted ipynb file and a dict containing the
268 293 resources created along the way via the transformers and Jinja2
269 294 processing.
270 295 """
271 296
272 297 return self.export(nbformat.read(file_stream, 'json'))
273 298
274 299
275 300 def _preprocess(self, nb):
276 301 """ Preprocess the notebook using the transformers specific
277 302 for the current export format.
278 303
279 304 nb: Notebook to preprocess
280 305 """
281 306
282 307 #Dict of 'resources' that can be filled by the preprocessors.
283 308 resources = {}
284 309
285 310 #Run each transformer on the notebook. Carry the output along
286 311 #to each transformer
287 312 for transformer in self.preprocessors:
288 313 nb, resources = transformer(nb, resources)
289 314 return nb, resources
290 315
291 316
317 def _get_default_options(self, export_format):
318 """ Load the default options for built in formats.
319
320 export_format: Format being exported to.
321 """
322
323 c = get_config()
324
325 #Set default data extraction priorities.
326 c.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text']
327 c.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text']
328 c.ConverterTemplate.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text']
329
330 #For most (or all cases), the template file name matches the format name.
331 c.ConverterTemplate.template_file = export_format
332
333 if export_format == "basichtml" or "fullhtml" or "reveal":
334 c.CSSHtmlHeaderTransformer.enabled=True
335 if export_format == 'reveal'
336 c.NbconvertApp.fileext='reveal.html'
337 else:
338 c.NbconvertApp.fileext='html'
339
340 elif export_format == "latex_sphinx_howto" or export_format == "latex_sphinx_manual":
341
342 #Turn on latex environment
343 c.ConverterTemplate.tex_environement=True
344
345 #Standard latex extension
346 c.NbconvertApp.fileext='tex'
347
348 #Prioritize latex extraction for latex exports.
349 c.GlobalConfigurable.display_data_priority =['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text']
350 c.ExtractFigureTransformer.display_data_priority=['latex', 'svg', 'png', 'jpg', 'jpeg']
351 c.ExtractFigureTransformer.extra_ext_map={'svg':'pdf'}
352 c.ExtractFigureTransformer.enabled=True
353
354 # Enable latex transformers (make markdown2latex work with math $.)
355 c.LatexTransformer.enabled=True
356 c.SphinxTransformer.enabled = True
357
358 elif export_format == 'markdown':
359 c.NbconvertApp.fileext='md'
360 c.ExtractFigureTransformer.enabled=True
361
362 elif export_format == 'python':
363 c.NbconvertApp.fileext='py'
364
365
366 elif export_format == 'rst':
367 c.NbconvertApp.fileext='rst'
368 c.ExtractFigureTransformer.enabled=True
369 return c
370
371
292 372 #TODO: Comment me.
293 373 def _rm_fake(strng):
294 374 return strng.replace('/files/', '')
295 375
376
296 377 #TODO: Comment me.
297 378 def _python_comment(string):
298 379 return '# '+'\n# '.join(string.split('\n'))
299 380
300 381
301 382 #TODO: Comment me.
302 383 def _escape_tex(value):
303 384 newval = value
304 385 for pattern, replacement in LATEX_SUBS:
305 386 newval = pattern.sub(replacement, newval)
306 387 return newval No newline at end of file
@@ -1,201 +1,154 b''
1 1 #!/usr/bin/env python
2 2
3 3 """NBConvert is a utility for conversion of IPYNB files.
4 4
5 5 Commandline interface for the NBConvert conversion utility. Read the
6 6 readme.rst for usage information
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 #Copyright (c) 2013, the IPython Development Team.
10 10 #
11 11 #Distributed under the terms of the Modified BSD License.
12 12 #
13 13 #The full license is in the file COPYING.txt, distributed with this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 #Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #Stdlib imports
21 21 from __future__ import print_function
22 22 import sys
23 23 import io
24 24 import os
25 25
26 26 #From IPython
27 27 #All the stuff needed for the configurable things
28 28 from IPython.config.application import Application
29 29 from IPython.config.loader import ConfigFileNotFound
30 30 from IPython.utils.traitlets import Unicode, Bool
31 31
32 32 #Local imports
33 33 from api.exporter import Exporter
34 34 from converters.config import GlobalConfigurable #TODO
35 35 from converters.transformers import (ExtractFigureTransformer) #TODO
36 36
37 37 #-----------------------------------------------------------------------------
38 38 #Globals and constants
39 39 #-----------------------------------------------------------------------------
40 40 NBCONVERT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
41 41
42 42 #'Keys in resources' user prompt.
43 43 KEYS_PROMPT_HEAD = "====================== Keys in Resources =================================="
44 44 KEYS_PROMPT_BODY = """
45 45 ===========================================================================
46 46 You are responsible for writting these files into the appropriate
47 47 directorie(s) if need be. If you do not want to see this message, enable
48 48 the 'write' (boolean) flag of the converter.
49 49 ===========================================================================
50 50 """
51 51
52 52 #Error Messages
53 53 ERROR_CONFIG_NOT_FOUND = "Config file for profile '%s' not found, giving up."
54 54
55 55 #-----------------------------------------------------------------------------
56 56 #Classes and functions
57 57 #-----------------------------------------------------------------------------
58 58 class NbconvertApp(Application):
59 59 """A basic application to convert ipynb files"""
60 60
61 stdout = Bool(
62 True, config=True,
63 help="""Whether to print the converted ipynb file to stdout
64 "use full do diff files without actually writing a new file"""
65 )
66
67 write = Bool(
68 False, config=True,
69 help="""Should the converted notebook file be written to disk
70 along with potential extracted resources."""
71 )
72
73 fileext = Unicode(
74 'txt', config=True,
75 help="Extension of the file that should be written to disk"
76 )
77
78 61 aliases = {
79 62 'stdout':'NbconvertApp.stdout',
80 63 'write':'NbconvertApp.write'
81 64 }
82 65
83 66 flags = {}
84 67 flags['no-stdout'] = (
85 68 {'NbconvertApp' : {'stdout' : False}},
86 69 """Do not print converted file to stdout, equivalent to
87 70 --stdout=False"""
88 71 )
89 72
90 73 def __init__(self, **kwargs):
91 74 """Public constructor"""
92 75
93 76 #Call base class
94 77 super(NbconvertApp, self).__init__(**kwargs)
95 78
96 79 #Register class here to have help with help all
97 80 self.classes.insert(0, ConverterTemplate)
98 81 self.classes.insert(0, ExtractFigureTransformer)
99 82 self.classes.insert(0, GlobalConfigurable)
100 83
101 84
102 def load_config_file(self, profile_name):
103 """Load a config file from the config file dir
104
105 profile_name : {string} name of the profile file to load without file
106 extension.
107 """
108
109 #Try to load the config file. If the file isn't found, catch the
110 #exception.
111 try:
112 Application.load_config_file(
113 self,
114 profile_name + '.py',
115 path=[os.path.join(NBCONVERT_DIR, 'profile')]
116 )
117 return True
118
119 except ConfigFileNotFound:
120 self.log.warn(ERROR_CONFIG_NOT_FOUND, profile_name)
121 return False
122
123
124 85 def start(self, argv=None):
125 86 """Convert a notebook in one step"""
126 87
127 88 #Parse the commandline options.
128 89 self.parse_command_line(argv)
129 90
130 #Load an addition config file if specified by the user via the
131 #commandline.
132 cl_config = self.config
133 profile_file = argv[1]
134 if not self.load_config_file(profile_file):
135 exit(1)
136 self.update_config(cl_config)
137
138 91 #Call base
139 92 super(NbconvertApp, self).start()
140 93
141 94 #The last arguments in chain of arguments will be used as conversion
142 95 ipynb_file = (self.extra_args or [None])[2]
143 96
144 97 #If you are writting a custom transformer, append it to the dictionary
145 98 #below.
146 99 userpreprocessors = {}
147 100
148 101 #Create the Jinja template exporter. TODO: Add ability to
149 102 #import in IPYNB aswell
150 exporter = Exporter(config=self.config, preprocessors=userpreprocessors)
103 exporter = Exporter(config=self.config, preprocessors=userpreprocessors,export_format=export_format)
151 104
152 105 #Export
153 106 output, resources = exporter.from_filename(ipynb_file)
154 if self.stdout :
107 if exporter.stdout :
155 108 print(output.encode('utf-8'))
156 109
157 110 #Get the file name without the '.ipynb' (6 chars) extension and then
158 111 #remove any addition periods and spaces. The resulting name will
159 112 #be used to create the directory that the files will be exported
160 113 #into.
161 114 out_root = ipynb_file[:-6].replace('.', '_').replace(' ', '_')
162 115
163 116 #Write file output from conversion.
164 if self.write :
165 with io.open(os.path.join(out_root+'.'+self.fileext), 'w') as f:
117 if exporter.write :
118 with io.open(os.path.join(out_root+'.'+exporter.fileext), 'w') as f:
166 119 f.write(output)
167 120
168 121 #Output any associate figures into the same "root" directory.
169 122 binkeys = resources.get('figures', {}).get('binary',{}).keys()
170 123 textkeys = resources.get('figures', {}).get('text',{}).keys()
171 124 if binkeys or textkeys :
172 if self.write:
125 if exporter.write:
173 126 files_dir = out_root+'_files'
174 127 if not os.path.exists(out_root+'_files'):
175 128 os.mkdir(files_dir)
176 129 for key in binkeys:
177 130 with io.open(os.path.join(files_dir, key), 'wb') as f:
178 131 f.write(resources['figures']['binary'][key])
179 132 for key in textkeys:
180 133 with io.open(os.path.join(files_dir, key), 'w') as f:
181 134 f.write(resources['figures']['text'][key])
182 135
183 136 #Figures that weren't exported which will need to be created by the
184 137 #user. Tell the user what figures these are.
185 elif self.stdout:
138 elif exporter.stdout:
186 139 print(KEYS_PROMPT_HEAD)
187 140 print(resources['figures'].keys())
188 141 print(KEYS_PROMPT_BODY)
189 142
190 143 #-----------------------------------------------------------------------------
191 144 #Script main
192 145 #-----------------------------------------------------------------------------
193 146 def main():
194 147 """Convert a notebook in one step"""
195 148
196 149 app = NbconvertApp.instance()
197 150 app.description = __doc__
198 151 app.run(argv=sys.argv)
199 152
200 153 if __name__ == '__main__':
201 154 main() No newline at end of file
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now