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