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