##// END OF EJS Templates
Fixed case where no resources are provided
Jonathan Frederic -
Show More
@@ -1,405 +1,407 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 not 'metadata' in resources:
201 if resources is None:
202 resources = ResourcesDict()
203 if not 'metadata' in resources or resources['metadata'] == '':
202 resources['metadata'] = ResourcesDict()
204 resources['metadata'] = ResourcesDict()
203 basename = os.path.basename(filename)
205 basename = os.path.basename(filename)
204 notebook_name = basename[:basename.rfind('.')]
206 notebook_name = basename[:basename.rfind('.')]
205 resources['metadata']['name'] = notebook_name
207 resources['metadata']['name'] = notebook_name
206
208
207 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
209 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
208 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
210 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
209
211
210 with io.open(filename) as f:
212 with io.open(filename) as f:
211 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)
212
214
213
215
214 def from_file(self, file_stream, resources=None, **kw):
216 def from_file(self, file_stream, resources=None, **kw):
215 """
217 """
216 Convert a notebook from a notebook file.
218 Convert a notebook from a notebook file.
217
219
218 Parameters
220 Parameters
219 ----------
221 ----------
220 file_stream : file-like object
222 file_stream : file-like object
221 Notebook file-like object to convert.
223 Notebook file-like object to convert.
222 """
224 """
223 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)
224
226
225
227
226 def register_transformer(self, transformer, enabled=None):
228 def register_transformer(self, transformer, enabled=None):
227 """
229 """
228 Register a transformer.
230 Register a transformer.
229 Transformers are classes that act upon the notebook before it is
231 Transformers are classes that act upon the notebook before it is
230 passed into the Jinja templating engine. Transformers are also
232 passed into the Jinja templating engine. Transformers are also
231 capable of passing additional information to the Jinja
233 capable of passing additional information to the Jinja
232 templating engine.
234 templating engine.
233
235
234 Parameters
236 Parameters
235 ----------
237 ----------
236 transformer : transformer
238 transformer : transformer
237 """
239 """
238
240
239 #Handle transformer's registration based on it's type
241 #Handle transformer's registration based on it's type
240 if inspect.isfunction(transformer):
242 if inspect.isfunction(transformer):
241 #Transformer is a function, no need to construct it.
243 #Transformer is a function, no need to construct it.
242 self._transformers.append(transformer)
244 self._transformers.append(transformer)
243 return transformer
245 return transformer
244
246
245 elif isinstance(transformer, types.StringTypes):
247 elif isinstance(transformer, types.StringTypes):
246 #Transformer is a string, import the namespace and recursively call
248 #Transformer is a string, import the namespace and recursively call
247 #this register_transformer method
249 #this register_transformer method
248 transformer_cls = import_item(DottedObjectName(transformer))
250 transformer_cls = import_item(DottedObjectName(transformer))
249 return self.register_transformer(transformer_cls, enabled=None)
251 return self.register_transformer(transformer_cls, enabled=None)
250
252
251 elif isinstance(transformer, MetaHasTraits):
253 elif isinstance(transformer, MetaHasTraits):
252 #Transformer is configurable. Make sure to pass in new default for
254 #Transformer is configurable. Make sure to pass in new default for
253 #the enabled flag if one was specified.
255 #the enabled flag if one was specified.
254 transformer_instance = transformer(parent=self)
256 transformer_instance = transformer(parent=self)
255 if enabled is not None:
257 if enabled is not None:
256 transformer_instance.enabled = True
258 transformer_instance.enabled = True
257
259
258 else:
260 else:
259 #Transformer is not configurable, construct it
261 #Transformer is not configurable, construct it
260 transformer_instance = transformer()
262 transformer_instance = transformer()
261
263
262 #Register and return the transformer.
264 #Register and return the transformer.
263 self._transformers.append(transformer_instance)
265 self._transformers.append(transformer_instance)
264 return transformer_instance
266 return transformer_instance
265
267
266
268
267 def register_filter(self, name, filter):
269 def register_filter(self, name, filter):
268 """
270 """
269 Register a filter.
271 Register a filter.
270 A filter is a function that accepts and acts on one string.
272 A filter is a function that accepts and acts on one string.
271 The filters are accesible within the Jinja templating engine.
273 The filters are accesible within the Jinja templating engine.
272
274
273 Parameters
275 Parameters
274 ----------
276 ----------
275 name : str
277 name : str
276 name to give the filter in the Jinja engine
278 name to give the filter in the Jinja engine
277 filter : filter
279 filter : filter
278 """
280 """
279 if inspect.isfunction(filter):
281 if inspect.isfunction(filter):
280 self.environment.filters[name] = filter
282 self.environment.filters[name] = filter
281 elif isinstance(filter, types.StringTypes):
283 elif isinstance(filter, types.StringTypes):
282 filter_cls = import_item(DottedObjectName(filter))
284 filter_cls = import_item(DottedObjectName(filter))
283 self.register_filter(name, filter_cls)
285 self.register_filter(name, filter_cls)
284 elif isinstance(filter, MetaHasTraits):
286 elif isinstance(filter, MetaHasTraits):
285 self.environment.filters[name] = filter(config=self.config)
287 self.environment.filters[name] = filter(config=self.config)
286 else:
288 else:
287 self.environment.filters[name] = filter()
289 self.environment.filters[name] = filter()
288 return self.environment.filters[name]
290 return self.environment.filters[name]
289
291
290
292
291 def _init_environment(self, extra_loaders=None):
293 def _init_environment(self, extra_loaders=None):
292 """
294 """
293 Create the Jinja templating environment.
295 Create the Jinja templating environment.
294 """
296 """
295 here = os.path.dirname(os.path.realpath(__file__))
297 here = os.path.dirname(os.path.realpath(__file__))
296 loaders = []
298 loaders = []
297 if extra_loaders:
299 if extra_loaders:
298 loaders.extend(extra_loaders)
300 loaders.extend(extra_loaders)
299
301
300 loaders.append(FileSystemLoader([
302 loaders.append(FileSystemLoader([
301 os.path.join(here, self.template_path),
303 os.path.join(here, self.template_path),
302 os.path.join(here, self.template_skeleton_path),
304 os.path.join(here, self.template_skeleton_path),
303 ]))
305 ]))
304
306
305 self.environment = Environment(
307 self.environment = Environment(
306 loader= ChoiceLoader(loaders),
308 loader= ChoiceLoader(loaders),
307 extensions=JINJA_EXTENSIONS
309 extensions=JINJA_EXTENSIONS
308 )
310 )
309
311
310 #Set special Jinja2 syntax that will not conflict with latex.
312 #Set special Jinja2 syntax that will not conflict with latex.
311 if self.jinja_logic_block_start:
313 if self.jinja_logic_block_start:
312 self.environment.block_start_string = self.jinja_logic_block_start
314 self.environment.block_start_string = self.jinja_logic_block_start
313 if self.jinja_logic_block_end:
315 if self.jinja_logic_block_end:
314 self.environment.block_end_string = self.jinja_logic_block_end
316 self.environment.block_end_string = self.jinja_logic_block_end
315 if self.jinja_variable_block_start:
317 if self.jinja_variable_block_start:
316 self.environment.variable_start_string = self.jinja_variable_block_start
318 self.environment.variable_start_string = self.jinja_variable_block_start
317 if self.jinja_variable_block_end:
319 if self.jinja_variable_block_end:
318 self.environment.variable_end_string = self.jinja_variable_block_end
320 self.environment.variable_end_string = self.jinja_variable_block_end
319 if self.jinja_comment_block_start:
321 if self.jinja_comment_block_start:
320 self.environment.comment_start_string = self.jinja_comment_block_start
322 self.environment.comment_start_string = self.jinja_comment_block_start
321 if self.jinja_comment_block_end:
323 if self.jinja_comment_block_end:
322 self.environment.comment_end_string = self.jinja_comment_block_end
324 self.environment.comment_end_string = self.jinja_comment_block_end
323
325
324
326
325 def _init_transformers(self):
327 def _init_transformers(self):
326 """
328 """
327 Register all of the transformers needed for this exporter, disabled
329 Register all of the transformers needed for this exporter, disabled
328 unless specified explicitly.
330 unless specified explicitly.
329 """
331 """
330 self._transformers = []
332 self._transformers = []
331
333
332 #Load default transformers (not necessarly enabled by default).
334 #Load default transformers (not necessarly enabled by default).
333 if self.default_transformers:
335 if self.default_transformers:
334 for transformer in self.default_transformers:
336 for transformer in self.default_transformers:
335 self.register_transformer(transformer)
337 self.register_transformer(transformer)
336
338
337 #Load user transformers. Enable by default.
339 #Load user transformers. Enable by default.
338 if self.transformers:
340 if self.transformers:
339 for transformer in self.transformers:
341 for transformer in self.transformers:
340 self.register_transformer(transformer, enabled=True)
342 self.register_transformer(transformer, enabled=True)
341
343
342
344
343 def _init_filters(self):
345 def _init_filters(self):
344 """
346 """
345 Register all of the filters required for the exporter.
347 Register all of the filters required for the exporter.
346 """
348 """
347
349
348 #Add default filters to the Jinja2 environment
350 #Add default filters to the Jinja2 environment
349 for key, value in default_filters.iteritems():
351 for key, value in default_filters.iteritems():
350 self.register_filter(key, value)
352 self.register_filter(key, value)
351
353
352 #Load user filters. Overwrite existing filters if need be.
354 #Load user filters. Overwrite existing filters if need be.
353 if self.filters:
355 if self.filters:
354 for key, user_filter in self.filters.iteritems():
356 for key, user_filter in self.filters.iteritems():
355 self.register_filter(key, user_filter)
357 self.register_filter(key, user_filter)
356
358
357
359
358 def _init_resources(self, resources):
360 def _init_resources(self, resources):
359
361
360 #Make sure the resources dict is of ResourcesDict type.
362 #Make sure the resources dict is of ResourcesDict type.
361 if resources is None:
363 if resources is None:
362 resources = ResourcesDict()
364 resources = ResourcesDict()
363 if not isinstance(resources, ResourcesDict):
365 if not isinstance(resources, ResourcesDict):
364 new_resources = ResourcesDict()
366 new_resources = ResourcesDict()
365 new_resources.update(resources)
367 new_resources.update(resources)
366 resources = new_resources
368 resources = new_resources
367
369
368 #Make sure the metadata extension exists in resources
370 #Make sure the metadata extension exists in resources
369 if 'metadata' in resources:
371 if 'metadata' in resources:
370 if not isinstance(resources['metadata'], ResourcesDict):
372 if not isinstance(resources['metadata'], ResourcesDict):
371 resources['metadata'] = ResourcesDict(resources['metadata'])
373 resources['metadata'] = ResourcesDict(resources['metadata'])
372 else:
374 else:
373 resources['metadata'] = ResourcesDict()
375 resources['metadata'] = ResourcesDict()
374 if not resources['metadata']['name']:
376 if not resources['metadata']['name']:
375 resources['metadata']['name'] = 'Notebook'
377 resources['metadata']['name'] = 'Notebook'
376
378
377 #Set the output extension
379 #Set the output extension
378 resources['output_extension'] = self.file_extension
380 resources['output_extension'] = self.file_extension
379 return resources
381 return resources
380
382
381
383
382 def _transform(self, nb, resources):
384 def _transform(self, nb, resources):
383 """
385 """
384 Preprocess the notebook before passing it into the Jinja engine.
386 Preprocess the notebook before passing it into the Jinja engine.
385 To preprocess the notebook is to apply all of the
387 To preprocess the notebook is to apply all of the
386
388
387 Parameters
389 Parameters
388 ----------
390 ----------
389 nb : notebook node
391 nb : notebook node
390 notebook that is being exported.
392 notebook that is being exported.
391 resources : a dict of additional resources that
393 resources : a dict of additional resources that
392 can be accessed read/write by transformers
394 can be accessed read/write by transformers
393 and filters.
395 and filters.
394 """
396 """
395
397
396 # Do a copy.deepcopy first,
398 # Do a copy.deepcopy first,
397 # we are never safe enough with what the transformers could do.
399 # we are never safe enough with what the transformers could do.
398 nbc = copy.deepcopy(nb)
400 nbc = copy.deepcopy(nb)
399 resc = copy.deepcopy(resources)
401 resc = copy.deepcopy(resources)
400
402
401 #Run each transformer on the notebook. Carry the output along
403 #Run each transformer on the notebook. Carry the output along
402 #to each transformer
404 #to each transformer
403 for transformer in self._transformers:
405 for transformer in self._transformers:
404 nbc, resc = transformer(nbc, resc)
406 nbc, resc = transformer(nbc, resc)
405 return nbc, resc
407 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now