##// END OF EJS Templates
don't reload template on every export...
MinRK -
Show More
@@ -1,482 +1,498 b''
1 """This module defines Exporter, a highly configurable converter
1 """This module defines Exporter, a highly configurable converter
2 that uses Jinja2 to export notebook files into different formats.
2 that uses Jinja2 to export notebook files into different formats.
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from __future__ import print_function, absolute_import
17 from __future__ import print_function, absolute_import
18
18
19 # Stdlib imports
19 # Stdlib imports
20 import io
20 import io
21 import os
21 import os
22 import inspect
22 import inspect
23 import copy
23 import copy
24 import collections
24 import collections
25 import datetime
25 import datetime
26
26
27 # other libs/dependencies
27 # other libs/dependencies
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
29
29
30 # IPython imports
30 # IPython imports
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import LoggingConfigurable
32 from IPython.config import Config
32 from IPython.config import Config
33 from IPython.nbformat import current as nbformat
33 from IPython.nbformat import current as nbformat
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict, Any
35 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent
36 from IPython.utils.text import indent
37 from IPython.utils import py3compat
37 from IPython.utils import py3compat
38
38
39 from IPython.nbconvert import transformers as nbtransformers
39 from IPython.nbconvert import transformers as nbtransformers
40 from IPython.nbconvert import filters
40 from IPython.nbconvert import filters
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals and constants
43 # Globals and constants
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #Jinja2 extensions to load.
46 #Jinja2 extensions to load.
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48
48
49 default_filters = {
49 default_filters = {
50 'indent': indent,
50 'indent': indent,
51 'markdown2html': filters.markdown2html,
51 'markdown2html': filters.markdown2html,
52 'ansi2html': filters.ansi2html,
52 'ansi2html': filters.ansi2html,
53 'filter_data_type': filters.DataTypeFilter,
53 'filter_data_type': filters.DataTypeFilter,
54 'get_lines': filters.get_lines,
54 'get_lines': filters.get_lines,
55 'highlight2html': filters.highlight2html,
55 'highlight2html': filters.highlight2html,
56 'highlight2latex': filters.highlight2latex,
56 'highlight2latex': filters.highlight2latex,
57 'ipython2python': filters.ipython2python,
57 'ipython2python': filters.ipython2python,
58 'markdown2latex': filters.markdown2latex,
58 'markdown2latex': filters.markdown2latex,
59 'markdown2rst': filters.markdown2rst,
59 'markdown2rst': filters.markdown2rst,
60 'comment_lines': filters.comment_lines,
60 'comment_lines': filters.comment_lines,
61 'strip_ansi': filters.strip_ansi,
61 'strip_ansi': filters.strip_ansi,
62 'strip_dollars': filters.strip_dollars,
62 'strip_dollars': filters.strip_dollars,
63 'strip_files_prefix': filters.strip_files_prefix,
63 'strip_files_prefix': filters.strip_files_prefix,
64 'html2text' : filters.html2text,
64 'html2text' : filters.html2text,
65 'add_anchor': filters.add_anchor,
65 'add_anchor': filters.add_anchor,
66 'ansi2latex': filters.ansi2latex,
66 'ansi2latex': filters.ansi2latex,
67 'strip_math_space': filters.strip_math_space,
67 'strip_math_space': filters.strip_math_space,
68 'wrap_text': filters.wrap_text,
68 'wrap_text': filters.wrap_text,
69 'escape_latex': filters.escape_latex
69 'escape_latex': filters.escape_latex
70 }
70 }
71
71
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73 # Class
73 # Class
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75
75
76 class ResourcesDict(collections.defaultdict):
76 class ResourcesDict(collections.defaultdict):
77 def __missing__(self, key):
77 def __missing__(self, key):
78 return ''
78 return ''
79
79
80
80
81 class Exporter(Configurable):
81 class Exporter(LoggingConfigurable):
82 """
82 """
83 Exports notebooks into other file formats. Uses Jinja 2 templating engine
83 Exports notebooks into other file formats. Uses Jinja 2 templating engine
84 to output new formats. Inherit from this class if you are creating a new
84 to output new formats. Inherit from this class if you are creating a new
85 template type along with new filters/transformers. If the filters/
85 template type along with new filters/transformers. If the filters/
86 transformers provided by default suffice, there is no need to inherit from
86 transformers provided by default suffice, there is no need to inherit from
87 this class. Instead, override the template_file and file_extension
87 this class. Instead, override the template_file and file_extension
88 traits via a config file.
88 traits via a config file.
89
89
90 {filters}
90 {filters}
91 """
91 """
92
92
93 # finish the docstring
93 # finish the docstring
94 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
94 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
95
95
96
96
97 template_file = Unicode(u'default',
97 template_file = Unicode(u'default',
98 config=True,
98 config=True,
99 help="Name of the template file to use")
99 help="Name of the template file to use")
100 def _template_file_changed(self, name, old, new):
100 def _template_file_changed(self, name, old, new):
101 if new=='default':
101 if new=='default':
102 self.template_file = self.default_template
102 self.template_file = self.default_template
103 else:
103 else:
104 self.template_file = new
104 self.template_file = new
105 self._load_template()
106
105 default_template = Unicode(u'')
107 default_template = Unicode(u'')
108 template = Any()
109 environment = Any()
106
110
107 file_extension = Unicode(
111 file_extension = Unicode(
108 'txt', config=True,
112 'txt', config=True,
109 help="Extension of the file that should be written to disk"
113 help="Extension of the file that should be written to disk"
110 )
114 )
111
115
112 template_path = List(['.'], config=True)
116 template_path = List(['.'], config=True)
117 def _template_path_changed(self, name, old, new):
118 self._load_template()
113
119
114 default_template_path = Unicode(
120 default_template_path = Unicode(
115 os.path.join("..", "templates"),
121 os.path.join("..", "templates"),
116 help="Path where the template files are located.")
122 help="Path where the template files are located.")
117
123
118 template_skeleton_path = Unicode(
124 template_skeleton_path = Unicode(
119 os.path.join("..", "templates", "skeleton"),
125 os.path.join("..", "templates", "skeleton"),
120 help="Path where the template skeleton files are located.")
126 help="Path where the template skeleton files are located.")
121
127
122 #Jinja block definitions
128 #Jinja block definitions
123 jinja_comment_block_start = Unicode("", config=True)
129 jinja_comment_block_start = Unicode("", config=True)
124 jinja_comment_block_end = Unicode("", config=True)
130 jinja_comment_block_end = Unicode("", config=True)
125 jinja_variable_block_start = Unicode("", config=True)
131 jinja_variable_block_start = Unicode("", config=True)
126 jinja_variable_block_end = Unicode("", config=True)
132 jinja_variable_block_end = Unicode("", config=True)
127 jinja_logic_block_start = Unicode("", config=True)
133 jinja_logic_block_start = Unicode("", config=True)
128 jinja_logic_block_end = Unicode("", config=True)
134 jinja_logic_block_end = Unicode("", config=True)
129
135
130 #Extension that the template files use.
136 #Extension that the template files use.
131 template_extension = Unicode(".tpl", config=True)
137 template_extension = Unicode(".tpl", config=True)
132
138
133 #Configurability, allows the user to easily add filters and transformers.
139 #Configurability, allows the user to easily add filters and transformers.
134 transformers = List(config=True,
140 transformers = List(config=True,
135 help="""List of transformers, by name or namespace, to enable.""")
141 help="""List of transformers, by name or namespace, to enable.""")
136
142
137 filters = Dict(config=True,
143 filters = Dict(config=True,
138 help="""Dictionary of filters, by name and namespace, to add to the Jinja
144 help="""Dictionary of filters, by name and namespace, to add to the Jinja
139 environment.""")
145 environment.""")
140
146
141 default_transformers = List([nbtransformers.coalesce_streams,
147 default_transformers = List([nbtransformers.coalesce_streams,
142 nbtransformers.SVG2PDFTransformer,
148 nbtransformers.SVG2PDFTransformer,
143 nbtransformers.ExtractOutputTransformer,
149 nbtransformers.ExtractOutputTransformer,
144 nbtransformers.CSSHTMLHeaderTransformer,
150 nbtransformers.CSSHTMLHeaderTransformer,
145 nbtransformers.RevealHelpTransformer,
151 nbtransformers.RevealHelpTransformer,
146 nbtransformers.LatexTransformer,
152 nbtransformers.LatexTransformer,
147 nbtransformers.SphinxTransformer],
153 nbtransformers.SphinxTransformer],
148 config=True,
154 config=True,
149 help="""List of transformers available by default, by name, namespace,
155 help="""List of transformers available by default, by name, namespace,
150 instance, or type.""")
156 instance, or type.""")
151
157
152
158
153 def __init__(self, config=None, extra_loaders=None, **kw):
159 def __init__(self, config=None, extra_loaders=None, **kw):
154 """
160 """
155 Public constructor
161 Public constructor
156
162
157 Parameters
163 Parameters
158 ----------
164 ----------
159 config : config
165 config : config
160 User configuration instance.
166 User configuration instance.
161 extra_loaders : list[of Jinja Loaders]
167 extra_loaders : list[of Jinja Loaders]
162 ordered list of Jinja loder to find templates. Will be tried in order
168 ordered list of Jinja loder to find templates. Will be tried in order
163 before the default FileSysteme ones.
169 before the default FileSysteme ones.
164 template : str (optional, kw arg)
170 template : str (optional, kw arg)
165 Template to use when exporting.
171 Template to use when exporting.
166 """
172 """
167
173
168 #Call the base class constructor
174 #Call the base class constructor
169 c = self.default_config
175 c = self.default_config
170 if config:
176 if config:
171 c.merge(config)
177 c.merge(config)
172
178
173 super(Exporter, self).__init__(config=c, **kw)
179 super(Exporter, self).__init__(config=c, **kw)
174
180
175 #Init
181 #Init
176 self._init_template(**kw)
182 self._init_template(**kw)
177 self._init_environment(extra_loaders=extra_loaders)
183 self._init_environment(extra_loaders=extra_loaders)
178 self._init_transformers()
184 self._init_transformers()
179 self._init_filters()
185 self._init_filters()
180
186
181
187
182 @property
188 @property
183 def default_config(self):
189 def default_config(self):
184 return Config()
190 return Config()
185
191
192 def _load_template(self):
193 if self.template is not None:
194 return
195 # called too early, do nothing
196 if self.environment is None:
197 return
198 # Try different template names during conversion. First try to load the
199 # template by name with extension added, then try loading the template
200 # as if the name is explicitly specified, then try the name as a
201 # 'flavor', and lastly just try to load the template by module name.
202 module_name = self.__module__.rsplit('.', 1)[-1]
203 try_names = [self.template_file + self.template_extension,
204 self.template_file,
205 module_name + '_' + self.template_file + self.template_extension,
206 module_name + self.template_extension]
207 for try_name in try_names:
208 self.log.debug("Attempting to load template %s", try_name)
209 try:
210 self.template = self.environment.get_template(try_name)
211 except TemplateNotFound:
212 pass
213 else:
214 self.log.info("Loaded template %s", try_name)
215 break
186
216
187 def from_notebook_node(self, nb, resources=None, **kw):
217 def from_notebook_node(self, nb, resources=None, **kw):
188 """
218 """
189 Convert a notebook from a notebook node instance.
219 Convert a notebook from a notebook node instance.
190
220
191 Parameters
221 Parameters
192 ----------
222 ----------
193 nb : Notebook node
223 nb : Notebook node
194 resources : dict (**kw)
224 resources : dict (**kw)
195 of additional resources that can be accessed read/write by
225 of additional resources that can be accessed read/write by
196 transformers and filters.
226 transformers and filters.
197 """
227 """
198 nb_copy = copy.deepcopy(nb)
228 nb_copy = copy.deepcopy(nb)
199 resources = self._init_resources(resources)
229 resources = self._init_resources(resources)
200
230
201 # Preprocess
231 # Preprocess
202 nb_copy, resources = self._transform(nb_copy, resources)
232 nb_copy, resources = self._transform(nb_copy, resources)
203
233
204 # Try different template names during conversion. First try to load the
234 self._load_template()
205 # template by name with extension added, then try loading the template
206 # as if the name is explicitly specified, then try the name as a
207 # 'flavor', and lastly just try to load the template by module name.
208 module_name = self.__module__.split('.')[-1]
209 try_names = [self.template_file + self.template_extension,
210 self.template_file,
211 module_name + '_' + self.template_file + self.template_extension,
212 module_name + self.template_extension]
213 for try_name in try_names:
214 try:
215 self.template = self.environment.get_template(try_name)
216 break
217 except TemplateNotFound:
218 pass
219
235
220 if hasattr(self, 'template'):
236 if self.template is not None:
221 output = self.template.render(nb=nb_copy, resources=resources)
237 output = self.template.render(nb=nb_copy, resources=resources)
222 else:
238 else:
223 raise IOError('template file "%s" could not be found' % self.template_file)
239 raise IOError('template file "%s" could not be found' % self.template_file)
224 return output, resources
240 return output, resources
225
241
226
242
227 def from_filename(self, filename, resources=None, **kw):
243 def from_filename(self, filename, resources=None, **kw):
228 """
244 """
229 Convert a notebook from a notebook file.
245 Convert a notebook from a notebook file.
230
246
231 Parameters
247 Parameters
232 ----------
248 ----------
233 filename : str
249 filename : str
234 Full filename of the notebook file to open and convert.
250 Full filename of the notebook file to open and convert.
235 """
251 """
236
252
237 #Pull the metadata from the filesystem.
253 #Pull the metadata from the filesystem.
238 if resources is None:
254 if resources is None:
239 resources = ResourcesDict()
255 resources = ResourcesDict()
240 if not 'metadata' in resources or resources['metadata'] == '':
256 if not 'metadata' in resources or resources['metadata'] == '':
241 resources['metadata'] = ResourcesDict()
257 resources['metadata'] = ResourcesDict()
242 basename = os.path.basename(filename)
258 basename = os.path.basename(filename)
243 notebook_name = basename[:basename.rfind('.')]
259 notebook_name = basename[:basename.rfind('.')]
244 resources['metadata']['name'] = notebook_name
260 resources['metadata']['name'] = notebook_name
245
261
246 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
262 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
247 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
263 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
248
264
249 with io.open(filename) as f:
265 with io.open(filename) as f:
250 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
266 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
251
267
252
268
253 def from_file(self, file_stream, resources=None, **kw):
269 def from_file(self, file_stream, resources=None, **kw):
254 """
270 """
255 Convert a notebook from a notebook file.
271 Convert a notebook from a notebook file.
256
272
257 Parameters
273 Parameters
258 ----------
274 ----------
259 file_stream : file-like object
275 file_stream : file-like object
260 Notebook file-like object to convert.
276 Notebook file-like object to convert.
261 """
277 """
262 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
278 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
263
279
264
280
265 def register_transformer(self, transformer, enabled=False):
281 def register_transformer(self, transformer, enabled=False):
266 """
282 """
267 Register a transformer.
283 Register a transformer.
268 Transformers are classes that act upon the notebook before it is
284 Transformers are classes that act upon the notebook before it is
269 passed into the Jinja templating engine. Transformers are also
285 passed into the Jinja templating engine. Transformers are also
270 capable of passing additional information to the Jinja
286 capable of passing additional information to the Jinja
271 templating engine.
287 templating engine.
272
288
273 Parameters
289 Parameters
274 ----------
290 ----------
275 transformer : transformer
291 transformer : transformer
276 """
292 """
277 if transformer is None:
293 if transformer is None:
278 raise TypeError('transformer')
294 raise TypeError('transformer')
279 isclass = isinstance(transformer, type)
295 isclass = isinstance(transformer, type)
280 constructed = not isclass
296 constructed = not isclass
281
297
282 #Handle transformer's registration based on it's type
298 #Handle transformer's registration based on it's type
283 if constructed and isinstance(transformer, py3compat.string_types):
299 if constructed and isinstance(transformer, py3compat.string_types):
284 #Transformer is a string, import the namespace and recursively call
300 #Transformer is a string, import the namespace and recursively call
285 #this register_transformer method
301 #this register_transformer method
286 transformer_cls = import_item(transformer)
302 transformer_cls = import_item(transformer)
287 return self.register_transformer(transformer_cls, enabled)
303 return self.register_transformer(transformer_cls, enabled)
288
304
289 if constructed and hasattr(transformer, '__call__'):
305 if constructed and hasattr(transformer, '__call__'):
290 #Transformer is a function, no need to construct it.
306 #Transformer is a function, no need to construct it.
291 #Register and return the transformer.
307 #Register and return the transformer.
292 if enabled:
308 if enabled:
293 transformer.enabled = True
309 transformer.enabled = True
294 self._transformers.append(transformer)
310 self._transformers.append(transformer)
295 return transformer
311 return transformer
296
312
297 elif isclass and isinstance(transformer, MetaHasTraits):
313 elif isclass and isinstance(transformer, MetaHasTraits):
298 #Transformer is configurable. Make sure to pass in new default for
314 #Transformer is configurable. Make sure to pass in new default for
299 #the enabled flag if one was specified.
315 #the enabled flag if one was specified.
300 self.register_transformer(transformer(parent=self), enabled)
316 self.register_transformer(transformer(parent=self), enabled)
301
317
302 elif isclass:
318 elif isclass:
303 #Transformer is not configurable, construct it
319 #Transformer is not configurable, construct it
304 self.register_transformer(transformer(), enabled)
320 self.register_transformer(transformer(), enabled)
305
321
306 else:
322 else:
307 #Transformer is an instance of something without a __call__
323 #Transformer is an instance of something without a __call__
308 #attribute.
324 #attribute.
309 raise TypeError('transformer')
325 raise TypeError('transformer')
310
326
311
327
312 def register_filter(self, name, jinja_filter):
328 def register_filter(self, name, jinja_filter):
313 """
329 """
314 Register a filter.
330 Register a filter.
315 A filter is a function that accepts and acts on one string.
331 A filter is a function that accepts and acts on one string.
316 The filters are accesible within the Jinja templating engine.
332 The filters are accesible within the Jinja templating engine.
317
333
318 Parameters
334 Parameters
319 ----------
335 ----------
320 name : str
336 name : str
321 name to give the filter in the Jinja engine
337 name to give the filter in the Jinja engine
322 filter : filter
338 filter : filter
323 """
339 """
324 if jinja_filter is None:
340 if jinja_filter is None:
325 raise TypeError('filter')
341 raise TypeError('filter')
326 isclass = isinstance(jinja_filter, type)
342 isclass = isinstance(jinja_filter, type)
327 constructed = not isclass
343 constructed = not isclass
328
344
329 #Handle filter's registration based on it's type
345 #Handle filter's registration based on it's type
330 if constructed and isinstance(jinja_filter, py3compat.string_types):
346 if constructed and isinstance(jinja_filter, py3compat.string_types):
331 #filter is a string, import the namespace and recursively call
347 #filter is a string, import the namespace and recursively call
332 #this register_filter method
348 #this register_filter method
333 filter_cls = import_item(jinja_filter)
349 filter_cls = import_item(jinja_filter)
334 return self.register_filter(name, filter_cls)
350 return self.register_filter(name, filter_cls)
335
351
336 if constructed and hasattr(jinja_filter, '__call__'):
352 if constructed and hasattr(jinja_filter, '__call__'):
337 #filter is a function, no need to construct it.
353 #filter is a function, no need to construct it.
338 self.environment.filters[name] = jinja_filter
354 self.environment.filters[name] = jinja_filter
339 return jinja_filter
355 return jinja_filter
340
356
341 elif isclass and isinstance(jinja_filter, MetaHasTraits):
357 elif isclass and isinstance(jinja_filter, MetaHasTraits):
342 #filter is configurable. Make sure to pass in new default for
358 #filter is configurable. Make sure to pass in new default for
343 #the enabled flag if one was specified.
359 #the enabled flag if one was specified.
344 filter_instance = jinja_filter(parent=self)
360 filter_instance = jinja_filter(parent=self)
345 self.register_filter(name, filter_instance )
361 self.register_filter(name, filter_instance )
346
362
347 elif isclass:
363 elif isclass:
348 #filter is not configurable, construct it
364 #filter is not configurable, construct it
349 filter_instance = jinja_filter()
365 filter_instance = jinja_filter()
350 self.register_filter(name, filter_instance)
366 self.register_filter(name, filter_instance)
351
367
352 else:
368 else:
353 #filter is an instance of something without a __call__
369 #filter is an instance of something without a __call__
354 #attribute.
370 #attribute.
355 raise TypeError('filter')
371 raise TypeError('filter')
356
372
357
373
358 def _init_template(self, **kw):
374 def _init_template(self, **kw):
359 """
375 """
360 Make sure a template name is specified. If one isn't specified, try to
376 Make sure a template name is specified. If one isn't specified, try to
361 build one from the information we know.
377 build one from the information we know.
362 """
378 """
363 self._template_file_changed('template_file', self.template_file, self.template_file)
379 self._template_file_changed('template_file', self.template_file, self.template_file)
364 if 'template' in kw:
380 if 'template' in kw:
365 self.template_file = kw['template']
381 self.template_file = kw['template']
366
382
367
383
368 def _init_environment(self, extra_loaders=None):
384 def _init_environment(self, extra_loaders=None):
369 """
385 """
370 Create the Jinja templating environment.
386 Create the Jinja templating environment.
371 """
387 """
372 here = os.path.dirname(os.path.realpath(__file__))
388 here = os.path.dirname(os.path.realpath(__file__))
373 loaders = []
389 loaders = []
374 if extra_loaders:
390 if extra_loaders:
375 loaders.extend(extra_loaders)
391 loaders.extend(extra_loaders)
376
392
377 paths = self.template_path
393 paths = self.template_path
378 paths.extend([os.path.join(here, self.default_template_path),
394 paths.extend([os.path.join(here, self.default_template_path),
379 os.path.join(here, self.template_skeleton_path)])
395 os.path.join(here, self.template_skeleton_path)])
380 loaders.append(FileSystemLoader(paths))
396 loaders.append(FileSystemLoader(paths))
381
397
382 self.environment = Environment(
398 self.environment = Environment(
383 loader= ChoiceLoader(loaders),
399 loader= ChoiceLoader(loaders),
384 extensions=JINJA_EXTENSIONS
400 extensions=JINJA_EXTENSIONS
385 )
401 )
386
402
387 #Set special Jinja2 syntax that will not conflict with latex.
403 #Set special Jinja2 syntax that will not conflict with latex.
388 if self.jinja_logic_block_start:
404 if self.jinja_logic_block_start:
389 self.environment.block_start_string = self.jinja_logic_block_start
405 self.environment.block_start_string = self.jinja_logic_block_start
390 if self.jinja_logic_block_end:
406 if self.jinja_logic_block_end:
391 self.environment.block_end_string = self.jinja_logic_block_end
407 self.environment.block_end_string = self.jinja_logic_block_end
392 if self.jinja_variable_block_start:
408 if self.jinja_variable_block_start:
393 self.environment.variable_start_string = self.jinja_variable_block_start
409 self.environment.variable_start_string = self.jinja_variable_block_start
394 if self.jinja_variable_block_end:
410 if self.jinja_variable_block_end:
395 self.environment.variable_end_string = self.jinja_variable_block_end
411 self.environment.variable_end_string = self.jinja_variable_block_end
396 if self.jinja_comment_block_start:
412 if self.jinja_comment_block_start:
397 self.environment.comment_start_string = self.jinja_comment_block_start
413 self.environment.comment_start_string = self.jinja_comment_block_start
398 if self.jinja_comment_block_end:
414 if self.jinja_comment_block_end:
399 self.environment.comment_end_string = self.jinja_comment_block_end
415 self.environment.comment_end_string = self.jinja_comment_block_end
400
416
401
417
402 def _init_transformers(self):
418 def _init_transformers(self):
403 """
419 """
404 Register all of the transformers needed for this exporter, disabled
420 Register all of the transformers needed for this exporter, disabled
405 unless specified explicitly.
421 unless specified explicitly.
406 """
422 """
407 self._transformers = []
423 self._transformers = []
408
424
409 #Load default transformers (not necessarly enabled by default).
425 #Load default transformers (not necessarly enabled by default).
410 if self.default_transformers:
426 if self.default_transformers:
411 for transformer in self.default_transformers:
427 for transformer in self.default_transformers:
412 self.register_transformer(transformer)
428 self.register_transformer(transformer)
413
429
414 #Load user transformers. Enable by default.
430 #Load user transformers. Enable by default.
415 if self.transformers:
431 if self.transformers:
416 for transformer in self.transformers:
432 for transformer in self.transformers:
417 self.register_transformer(transformer, enabled=True)
433 self.register_transformer(transformer, enabled=True)
418
434
419
435
420 def _init_filters(self):
436 def _init_filters(self):
421 """
437 """
422 Register all of the filters required for the exporter.
438 Register all of the filters required for the exporter.
423 """
439 """
424
440
425 #Add default filters to the Jinja2 environment
441 #Add default filters to the Jinja2 environment
426 for key, value in default_filters.items():
442 for key, value in default_filters.items():
427 self.register_filter(key, value)
443 self.register_filter(key, value)
428
444
429 #Load user filters. Overwrite existing filters if need be.
445 #Load user filters. Overwrite existing filters if need be.
430 if self.filters:
446 if self.filters:
431 for key, user_filter in self.filters.items():
447 for key, user_filter in self.filters.items():
432 self.register_filter(key, user_filter)
448 self.register_filter(key, user_filter)
433
449
434
450
435 def _init_resources(self, resources):
451 def _init_resources(self, resources):
436
452
437 #Make sure the resources dict is of ResourcesDict type.
453 #Make sure the resources dict is of ResourcesDict type.
438 if resources is None:
454 if resources is None:
439 resources = ResourcesDict()
455 resources = ResourcesDict()
440 if not isinstance(resources, ResourcesDict):
456 if not isinstance(resources, ResourcesDict):
441 new_resources = ResourcesDict()
457 new_resources = ResourcesDict()
442 new_resources.update(resources)
458 new_resources.update(resources)
443 resources = new_resources
459 resources = new_resources
444
460
445 #Make sure the metadata extension exists in resources
461 #Make sure the metadata extension exists in resources
446 if 'metadata' in resources:
462 if 'metadata' in resources:
447 if not isinstance(resources['metadata'], ResourcesDict):
463 if not isinstance(resources['metadata'], ResourcesDict):
448 resources['metadata'] = ResourcesDict(resources['metadata'])
464 resources['metadata'] = ResourcesDict(resources['metadata'])
449 else:
465 else:
450 resources['metadata'] = ResourcesDict()
466 resources['metadata'] = ResourcesDict()
451 if not resources['metadata']['name']:
467 if not resources['metadata']['name']:
452 resources['metadata']['name'] = 'Notebook'
468 resources['metadata']['name'] = 'Notebook'
453
469
454 #Set the output extension
470 #Set the output extension
455 resources['output_extension'] = self.file_extension
471 resources['output_extension'] = self.file_extension
456 return resources
472 return resources
457
473
458
474
459 def _transform(self, nb, resources):
475 def _transform(self, nb, resources):
460 """
476 """
461 Preprocess the notebook before passing it into the Jinja engine.
477 Preprocess the notebook before passing it into the Jinja engine.
462 To preprocess the notebook is to apply all of the
478 To preprocess the notebook is to apply all of the
463
479
464 Parameters
480 Parameters
465 ----------
481 ----------
466 nb : notebook node
482 nb : notebook node
467 notebook that is being exported.
483 notebook that is being exported.
468 resources : a dict of additional resources that
484 resources : a dict of additional resources that
469 can be accessed read/write by transformers
485 can be accessed read/write by transformers
470 and filters.
486 and filters.
471 """
487 """
472
488
473 # Do a copy.deepcopy first,
489 # Do a copy.deepcopy first,
474 # we are never safe enough with what the transformers could do.
490 # we are never safe enough with what the transformers could do.
475 nbc = copy.deepcopy(nb)
491 nbc = copy.deepcopy(nb)
476 resc = copy.deepcopy(resources)
492 resc = copy.deepcopy(resources)
477
493
478 #Run each transformer on the notebook. Carry the output along
494 #Run each transformer on the notebook. Carry the output along
479 #to each transformer
495 #to each transformer
480 for transformer in self._transformers:
496 for transformer in self._transformers:
481 nbc, resc = transformer(nbc, resc)
497 nbc, resc = transformer(nbc, resc)
482 return nbc, resc
498 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now