##// END OF EJS Templates
Merge pull request #3720 from jstenar/nbconvert-datefmt...
Min RK -
r11762:8d0a6b70 merge
parent child Browse files
Show More
@@ -1,446 +1,446 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
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
29
29
30 # IPython imports
30 # IPython imports
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
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
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(Configurable):
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(
97 template_file = Unicode(
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
100
101 file_extension = Unicode(
101 file_extension = Unicode(
102 'txt', config=True,
102 'txt', config=True,
103 help="Extension of the file that should be written to disk"
103 help="Extension of the file that should be written to disk"
104 )
104 )
105
105
106 template_path = List(['.'], config=True)
106 template_path = List(['.'], config=True)
107
107
108 default_template_path = Unicode(
108 default_template_path = Unicode(
109 os.path.join("..", "templates"),
109 os.path.join("..", "templates"),
110 help="Path where the template files are located.")
110 help="Path where the template files are located.")
111
111
112 template_skeleton_path = Unicode(
112 template_skeleton_path = Unicode(
113 os.path.join("..", "templates", "skeleton"),
113 os.path.join("..", "templates", "skeleton"),
114 help="Path where the template skeleton files are located.")
114 help="Path where the template skeleton files are located.")
115
115
116 #Jinja block definitions
116 #Jinja block definitions
117 jinja_comment_block_start = Unicode("", config=True)
117 jinja_comment_block_start = Unicode("", config=True)
118 jinja_comment_block_end = Unicode("", config=True)
118 jinja_comment_block_end = Unicode("", config=True)
119 jinja_variable_block_start = Unicode("", config=True)
119 jinja_variable_block_start = Unicode("", config=True)
120 jinja_variable_block_end = Unicode("", config=True)
120 jinja_variable_block_end = Unicode("", config=True)
121 jinja_logic_block_start = Unicode("", config=True)
121 jinja_logic_block_start = Unicode("", config=True)
122 jinja_logic_block_end = Unicode("", config=True)
122 jinja_logic_block_end = Unicode("", config=True)
123
123
124 #Extension that the template files use.
124 #Extension that the template files use.
125 template_extension = Unicode(".tpl", config=True)
125 template_extension = Unicode(".tpl", config=True)
126
126
127 #Configurability, allows the user to easily add filters and transformers.
127 #Configurability, allows the user to easily add filters and transformers.
128 transformers = List(config=True,
128 transformers = List(config=True,
129 help="""List of transformers, by name or namespace, to enable.""")
129 help="""List of transformers, by name or namespace, to enable.""")
130
130
131 filters = Dict(config=True,
131 filters = Dict(config=True,
132 help="""Dictionary of filters, by name and namespace, to add to the Jinja
132 help="""Dictionary of filters, by name and namespace, to add to the Jinja
133 environment.""")
133 environment.""")
134
134
135 default_transformers = List([nbtransformers.coalesce_streams,
135 default_transformers = List([nbtransformers.coalesce_streams,
136 nbtransformers.SVG2PDFTransformer,
136 nbtransformers.SVG2PDFTransformer,
137 nbtransformers.ExtractOutputTransformer,
137 nbtransformers.ExtractOutputTransformer,
138 nbtransformers.CSSHTMLHeaderTransformer,
138 nbtransformers.CSSHTMLHeaderTransformer,
139 nbtransformers.RevealHelpTransformer,
139 nbtransformers.RevealHelpTransformer,
140 nbtransformers.LatexTransformer,
140 nbtransformers.LatexTransformer,
141 nbtransformers.SphinxTransformer],
141 nbtransformers.SphinxTransformer],
142 config=True,
142 config=True,
143 help="""List of transformers available by default, by name, namespace,
143 help="""List of transformers available by default, by name, namespace,
144 instance, or type.""")
144 instance, or type.""")
145
145
146
146
147 def __init__(self, config=None, extra_loaders=None, **kw):
147 def __init__(self, config=None, extra_loaders=None, **kw):
148 """
148 """
149 Public constructor
149 Public constructor
150
150
151 Parameters
151 Parameters
152 ----------
152 ----------
153 config : config
153 config : config
154 User configuration instance.
154 User configuration instance.
155 extra_loaders : list[of Jinja Loaders]
155 extra_loaders : list[of Jinja Loaders]
156 ordered list of Jinja loder to find templates. Will be tried in order
156 ordered list of Jinja loder to find templates. Will be tried in order
157 before the default FileSysteme ones.
157 before the default FileSysteme ones.
158 """
158 """
159
159
160 #Call the base class constructor
160 #Call the base class constructor
161 c = self.default_config
161 c = self.default_config
162 if config:
162 if config:
163 c.merge(config)
163 c.merge(config)
164
164
165 super(Exporter, self).__init__(config=c, **kw)
165 super(Exporter, self).__init__(config=c, **kw)
166
166
167 #Init
167 #Init
168 self._init_environment(extra_loaders=extra_loaders)
168 self._init_environment(extra_loaders=extra_loaders)
169 self._init_transformers()
169 self._init_transformers()
170 self._init_filters()
170 self._init_filters()
171
171
172
172
173 @property
173 @property
174 def default_config(self):
174 def default_config(self):
175 return Config()
175 return Config()
176
176
177
177
178 def from_notebook_node(self, nb, resources=None, **kw):
178 def from_notebook_node(self, nb, resources=None, **kw):
179 """
179 """
180 Convert a notebook from a notebook node instance.
180 Convert a notebook from a notebook node instance.
181
181
182 Parameters
182 Parameters
183 ----------
183 ----------
184 nb : Notebook node
184 nb : Notebook node
185 resources : dict (**kw)
185 resources : dict (**kw)
186 of additional resources that can be accessed read/write by
186 of additional resources that can be accessed read/write by
187 transformers and filters.
187 transformers and filters.
188 """
188 """
189 nb_copy = copy.deepcopy(nb)
189 nb_copy = copy.deepcopy(nb)
190 resources = self._init_resources(resources)
190 resources = self._init_resources(resources)
191
191
192 #Preprocess
192 #Preprocess
193 nb_copy, resources = self._transform(nb_copy, resources)
193 nb_copy, resources = self._transform(nb_copy, resources)
194
194
195 #Convert
195 #Convert
196 self.template = self.environment.get_template(self.template_file + self.template_extension)
196 self.template = self.environment.get_template(self.template_file + self.template_extension)
197 output = self.template.render(nb=nb_copy, resources=resources)
197 output = self.template.render(nb=nb_copy, resources=resources)
198 return output, resources
198 return output, resources
199
199
200
200
201 def from_filename(self, filename, resources=None, **kw):
201 def from_filename(self, filename, resources=None, **kw):
202 """
202 """
203 Convert a notebook from a notebook file.
203 Convert a notebook from a notebook file.
204
204
205 Parameters
205 Parameters
206 ----------
206 ----------
207 filename : str
207 filename : str
208 Full filename of the notebook file to open and convert.
208 Full filename of the notebook file to open and convert.
209 """
209 """
210
210
211 #Pull the metadata from the filesystem.
211 #Pull the metadata from the filesystem.
212 if resources is None:
212 if resources is None:
213 resources = ResourcesDict()
213 resources = ResourcesDict()
214 if not 'metadata' in resources or resources['metadata'] == '':
214 if not 'metadata' in resources or resources['metadata'] == '':
215 resources['metadata'] = ResourcesDict()
215 resources['metadata'] = ResourcesDict()
216 basename = os.path.basename(filename)
216 basename = os.path.basename(filename)
217 notebook_name = basename[:basename.rfind('.')]
217 notebook_name = basename[:basename.rfind('.')]
218 resources['metadata']['name'] = notebook_name
218 resources['metadata']['name'] = notebook_name
219
219
220 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
220 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
221 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
221 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
222
222
223 with io.open(filename) as f:
223 with io.open(filename) as f:
224 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
224 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
225
225
226
226
227 def from_file(self, file_stream, resources=None, **kw):
227 def from_file(self, file_stream, resources=None, **kw):
228 """
228 """
229 Convert a notebook from a notebook file.
229 Convert a notebook from a notebook file.
230
230
231 Parameters
231 Parameters
232 ----------
232 ----------
233 file_stream : file-like object
233 file_stream : file-like object
234 Notebook file-like object to convert.
234 Notebook file-like object to convert.
235 """
235 """
236 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
236 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
237
237
238
238
239 def register_transformer(self, transformer, enabled=False):
239 def register_transformer(self, transformer, enabled=False):
240 """
240 """
241 Register a transformer.
241 Register a transformer.
242 Transformers are classes that act upon the notebook before it is
242 Transformers are classes that act upon the notebook before it is
243 passed into the Jinja templating engine. Transformers are also
243 passed into the Jinja templating engine. Transformers are also
244 capable of passing additional information to the Jinja
244 capable of passing additional information to the Jinja
245 templating engine.
245 templating engine.
246
246
247 Parameters
247 Parameters
248 ----------
248 ----------
249 transformer : transformer
249 transformer : transformer
250 """
250 """
251 if transformer is None:
251 if transformer is None:
252 raise TypeError('transformer')
252 raise TypeError('transformer')
253 isclass = isinstance(transformer, type)
253 isclass = isinstance(transformer, type)
254 constructed = not isclass
254 constructed = not isclass
255
255
256 #Handle transformer's registration based on it's type
256 #Handle transformer's registration based on it's type
257 if constructed and isinstance(transformer, py3compat.string_types):
257 if constructed and isinstance(transformer, py3compat.string_types):
258 #Transformer is a string, import the namespace and recursively call
258 #Transformer is a string, import the namespace and recursively call
259 #this register_transformer method
259 #this register_transformer method
260 transformer_cls = import_item(transformer)
260 transformer_cls = import_item(transformer)
261 return self.register_transformer(transformer_cls, enabled)
261 return self.register_transformer(transformer_cls, enabled)
262
262
263 if constructed and hasattr(transformer, '__call__'):
263 if constructed and hasattr(transformer, '__call__'):
264 #Transformer is a function, no need to construct it.
264 #Transformer is a function, no need to construct it.
265 #Register and return the transformer.
265 #Register and return the transformer.
266 if enabled:
266 if enabled:
267 transformer.enabled = True
267 transformer.enabled = True
268 self._transformers.append(transformer)
268 self._transformers.append(transformer)
269 return transformer
269 return transformer
270
270
271 elif isclass and isinstance(transformer, MetaHasTraits):
271 elif isclass and isinstance(transformer, MetaHasTraits):
272 #Transformer is configurable. Make sure to pass in new default for
272 #Transformer is configurable. Make sure to pass in new default for
273 #the enabled flag if one was specified.
273 #the enabled flag if one was specified.
274 self.register_transformer(transformer(parent=self), enabled)
274 self.register_transformer(transformer(parent=self), enabled)
275
275
276 elif isclass:
276 elif isclass:
277 #Transformer is not configurable, construct it
277 #Transformer is not configurable, construct it
278 self.register_transformer(transformer(), enabled)
278 self.register_transformer(transformer(), enabled)
279
279
280 else:
280 else:
281 #Transformer is an instance of something without a __call__
281 #Transformer is an instance of something without a __call__
282 #attribute.
282 #attribute.
283 raise TypeError('transformer')
283 raise TypeError('transformer')
284
284
285
285
286 def register_filter(self, name, jinja_filter):
286 def register_filter(self, name, jinja_filter):
287 """
287 """
288 Register a filter.
288 Register a filter.
289 A filter is a function that accepts and acts on one string.
289 A filter is a function that accepts and acts on one string.
290 The filters are accesible within the Jinja templating engine.
290 The filters are accesible within the Jinja templating engine.
291
291
292 Parameters
292 Parameters
293 ----------
293 ----------
294 name : str
294 name : str
295 name to give the filter in the Jinja engine
295 name to give the filter in the Jinja engine
296 filter : filter
296 filter : filter
297 """
297 """
298 if jinja_filter is None:
298 if jinja_filter is None:
299 raise TypeError('filter')
299 raise TypeError('filter')
300 isclass = isinstance(jinja_filter, type)
300 isclass = isinstance(jinja_filter, type)
301 constructed = not isclass
301 constructed = not isclass
302
302
303 #Handle filter's registration based on it's type
303 #Handle filter's registration based on it's type
304 if constructed and isinstance(jinja_filter, py3compat.string_types):
304 if constructed and isinstance(jinja_filter, py3compat.string_types):
305 #filter is a string, import the namespace and recursively call
305 #filter is a string, import the namespace and recursively call
306 #this register_filter method
306 #this register_filter method
307 filter_cls = import_item(jinja_filter)
307 filter_cls = import_item(jinja_filter)
308 return self.register_filter(name, filter_cls)
308 return self.register_filter(name, filter_cls)
309
309
310 if constructed and hasattr(jinja_filter, '__call__'):
310 if constructed and hasattr(jinja_filter, '__call__'):
311 #filter is a function, no need to construct it.
311 #filter is a function, no need to construct it.
312 self.environment.filters[name] = jinja_filter
312 self.environment.filters[name] = jinja_filter
313 return jinja_filter
313 return jinja_filter
314
314
315 elif isclass and isinstance(jinja_filter, MetaHasTraits):
315 elif isclass and isinstance(jinja_filter, MetaHasTraits):
316 #filter is configurable. Make sure to pass in new default for
316 #filter is configurable. Make sure to pass in new default for
317 #the enabled flag if one was specified.
317 #the enabled flag if one was specified.
318 filter_instance = jinja_filter(parent=self)
318 filter_instance = jinja_filter(parent=self)
319 self.register_filter(name, filter_instance )
319 self.register_filter(name, filter_instance )
320
320
321 elif isclass:
321 elif isclass:
322 #filter is not configurable, construct it
322 #filter is not configurable, construct it
323 filter_instance = jinja_filter()
323 filter_instance = jinja_filter()
324 self.register_filter(name, filter_instance)
324 self.register_filter(name, filter_instance)
325
325
326 else:
326 else:
327 #filter is an instance of something without a __call__
327 #filter is an instance of something without a __call__
328 #attribute.
328 #attribute.
329 raise TypeError('filter')
329 raise TypeError('filter')
330
330
331
331
332 def _init_environment(self, extra_loaders=None):
332 def _init_environment(self, extra_loaders=None):
333 """
333 """
334 Create the Jinja templating environment.
334 Create the Jinja templating environment.
335 """
335 """
336 here = os.path.dirname(os.path.realpath(__file__))
336 here = os.path.dirname(os.path.realpath(__file__))
337 loaders = []
337 loaders = []
338 if extra_loaders:
338 if extra_loaders:
339 loaders.extend(extra_loaders)
339 loaders.extend(extra_loaders)
340
340
341 paths = self.template_path
341 paths = self.template_path
342 paths.extend([os.path.join(here, self.default_template_path),
342 paths.extend([os.path.join(here, self.default_template_path),
343 os.path.join(here, self.template_skeleton_path)])
343 os.path.join(here, self.template_skeleton_path)])
344 loaders.append(FileSystemLoader(paths))
344 loaders.append(FileSystemLoader(paths))
345
345
346 self.environment = Environment(
346 self.environment = Environment(
347 loader= ChoiceLoader(loaders),
347 loader= ChoiceLoader(loaders),
348 extensions=JINJA_EXTENSIONS
348 extensions=JINJA_EXTENSIONS
349 )
349 )
350
350
351 #Set special Jinja2 syntax that will not conflict with latex.
351 #Set special Jinja2 syntax that will not conflict with latex.
352 if self.jinja_logic_block_start:
352 if self.jinja_logic_block_start:
353 self.environment.block_start_string = self.jinja_logic_block_start
353 self.environment.block_start_string = self.jinja_logic_block_start
354 if self.jinja_logic_block_end:
354 if self.jinja_logic_block_end:
355 self.environment.block_end_string = self.jinja_logic_block_end
355 self.environment.block_end_string = self.jinja_logic_block_end
356 if self.jinja_variable_block_start:
356 if self.jinja_variable_block_start:
357 self.environment.variable_start_string = self.jinja_variable_block_start
357 self.environment.variable_start_string = self.jinja_variable_block_start
358 if self.jinja_variable_block_end:
358 if self.jinja_variable_block_end:
359 self.environment.variable_end_string = self.jinja_variable_block_end
359 self.environment.variable_end_string = self.jinja_variable_block_end
360 if self.jinja_comment_block_start:
360 if self.jinja_comment_block_start:
361 self.environment.comment_start_string = self.jinja_comment_block_start
361 self.environment.comment_start_string = self.jinja_comment_block_start
362 if self.jinja_comment_block_end:
362 if self.jinja_comment_block_end:
363 self.environment.comment_end_string = self.jinja_comment_block_end
363 self.environment.comment_end_string = self.jinja_comment_block_end
364
364
365
365
366 def _init_transformers(self):
366 def _init_transformers(self):
367 """
367 """
368 Register all of the transformers needed for this exporter, disabled
368 Register all of the transformers needed for this exporter, disabled
369 unless specified explicitly.
369 unless specified explicitly.
370 """
370 """
371 self._transformers = []
371 self._transformers = []
372
372
373 #Load default transformers (not necessarly enabled by default).
373 #Load default transformers (not necessarly enabled by default).
374 if self.default_transformers:
374 if self.default_transformers:
375 for transformer in self.default_transformers:
375 for transformer in self.default_transformers:
376 self.register_transformer(transformer)
376 self.register_transformer(transformer)
377
377
378 #Load user transformers. Enable by default.
378 #Load user transformers. Enable by default.
379 if self.transformers:
379 if self.transformers:
380 for transformer in self.transformers:
380 for transformer in self.transformers:
381 self.register_transformer(transformer, enabled=True)
381 self.register_transformer(transformer, enabled=True)
382
382
383
383
384 def _init_filters(self):
384 def _init_filters(self):
385 """
385 """
386 Register all of the filters required for the exporter.
386 Register all of the filters required for the exporter.
387 """
387 """
388
388
389 #Add default filters to the Jinja2 environment
389 #Add default filters to the Jinja2 environment
390 for key, value in default_filters.items():
390 for key, value in default_filters.items():
391 self.register_filter(key, value)
391 self.register_filter(key, value)
392
392
393 #Load user filters. Overwrite existing filters if need be.
393 #Load user filters. Overwrite existing filters if need be.
394 if self.filters:
394 if self.filters:
395 for key, user_filter in self.filters.items():
395 for key, user_filter in self.filters.items():
396 self.register_filter(key, user_filter)
396 self.register_filter(key, user_filter)
397
397
398
398
399 def _init_resources(self, resources):
399 def _init_resources(self, resources):
400
400
401 #Make sure the resources dict is of ResourcesDict type.
401 #Make sure the resources dict is of ResourcesDict type.
402 if resources is None:
402 if resources is None:
403 resources = ResourcesDict()
403 resources = ResourcesDict()
404 if not isinstance(resources, ResourcesDict):
404 if not isinstance(resources, ResourcesDict):
405 new_resources = ResourcesDict()
405 new_resources = ResourcesDict()
406 new_resources.update(resources)
406 new_resources.update(resources)
407 resources = new_resources
407 resources = new_resources
408
408
409 #Make sure the metadata extension exists in resources
409 #Make sure the metadata extension exists in resources
410 if 'metadata' in resources:
410 if 'metadata' in resources:
411 if not isinstance(resources['metadata'], ResourcesDict):
411 if not isinstance(resources['metadata'], ResourcesDict):
412 resources['metadata'] = ResourcesDict(resources['metadata'])
412 resources['metadata'] = ResourcesDict(resources['metadata'])
413 else:
413 else:
414 resources['metadata'] = ResourcesDict()
414 resources['metadata'] = ResourcesDict()
415 if not resources['metadata']['name']:
415 if not resources['metadata']['name']:
416 resources['metadata']['name'] = 'Notebook'
416 resources['metadata']['name'] = 'Notebook'
417
417
418 #Set the output extension
418 #Set the output extension
419 resources['output_extension'] = self.file_extension
419 resources['output_extension'] = self.file_extension
420 return resources
420 return resources
421
421
422
422
423 def _transform(self, nb, resources):
423 def _transform(self, nb, resources):
424 """
424 """
425 Preprocess the notebook before passing it into the Jinja engine.
425 Preprocess the notebook before passing it into the Jinja engine.
426 To preprocess the notebook is to apply all of the
426 To preprocess the notebook is to apply all of the
427
427
428 Parameters
428 Parameters
429 ----------
429 ----------
430 nb : notebook node
430 nb : notebook node
431 notebook that is being exported.
431 notebook that is being exported.
432 resources : a dict of additional resources that
432 resources : a dict of additional resources that
433 can be accessed read/write by transformers
433 can be accessed read/write by transformers
434 and filters.
434 and filters.
435 """
435 """
436
436
437 # Do a copy.deepcopy first,
437 # Do a copy.deepcopy first,
438 # we are never safe enough with what the transformers could do.
438 # we are never safe enough with what the transformers could do.
439 nbc = copy.deepcopy(nb)
439 nbc = copy.deepcopy(nb)
440 resc = copy.deepcopy(resources)
440 resc = copy.deepcopy(resources)
441
441
442 #Run each transformer on the notebook. Carry the output along
442 #Run each transformer on the notebook. Carry the output along
443 #to each transformer
443 #to each transformer
444 for transformer in self._transformers:
444 for transformer in self._transformers:
445 nbc, resc = transformer(nbc, resc)
445 nbc, resc = transformer(nbc, resc)
446 return nbc, resc
446 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now