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