|
@@
-1,275
+1,276
b''
|
|
1
|
"""This module defines a base Exporter class. For Jinja template-based export,
|
|
1
|
"""This module defines a base Exporter class. For Jinja template-based export,
|
|
2
|
see templateexporter.py.
|
|
2
|
see templateexporter.py.
|
|
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 copy
|
|
22
|
import copy
|
|
23
|
import collections
|
|
23
|
import collections
|
|
24
|
import datetime
|
|
24
|
import datetime
|
|
25
|
|
|
25
|
|
|
26
|
|
|
26
|
|
|
27
|
# IPython imports
|
|
27
|
# IPython imports
|
|
28
|
from IPython.config.configurable import LoggingConfigurable
|
|
28
|
from IPython.config.configurable import LoggingConfigurable
|
|
29
|
from IPython.config import Config
|
|
29
|
from IPython.config import Config
|
|
30
|
from IPython.nbformat import current as nbformat
|
|
30
|
from IPython.nbformat import current as nbformat
|
|
31
|
from IPython.utils.traitlets import MetaHasTraits, Unicode, List
|
|
31
|
from IPython.utils.traitlets import MetaHasTraits, Unicode, List
|
|
32
|
from IPython.utils.importstring import import_item
|
|
32
|
from IPython.utils.importstring import import_item
|
|
33
|
from IPython.utils import text, py3compat
|
|
33
|
from IPython.utils import text, py3compat
|
|
34
|
|
|
34
|
|
|
35
|
#-----------------------------------------------------------------------------
|
|
35
|
#-----------------------------------------------------------------------------
|
|
36
|
# Class
|
|
36
|
# Class
|
|
37
|
#-----------------------------------------------------------------------------
|
|
37
|
#-----------------------------------------------------------------------------
|
|
38
|
|
|
38
|
|
|
39
|
class ResourcesDict(collections.defaultdict):
|
|
39
|
class ResourcesDict(collections.defaultdict):
|
|
40
|
def __missing__(self, key):
|
|
40
|
def __missing__(self, key):
|
|
41
|
return ''
|
|
41
|
return ''
|
|
42
|
|
|
42
|
|
|
43
|
|
|
43
|
|
|
44
|
class Exporter(LoggingConfigurable):
|
|
44
|
class Exporter(LoggingConfigurable):
|
|
45
|
"""
|
|
45
|
"""
|
|
46
|
Class containing methods that sequentially run a list of preprocessors on a
|
|
46
|
Class containing methods that sequentially run a list of preprocessors on a
|
|
47
|
NotebookNode object and then return the modified NotebookNode object and
|
|
47
|
NotebookNode object and then return the modified NotebookNode object and
|
|
48
|
accompanying resources dict.
|
|
48
|
accompanying resources dict.
|
|
49
|
"""
|
|
49
|
"""
|
|
50
|
|
|
50
|
|
|
51
|
file_extension = Unicode(
|
|
51
|
file_extension = Unicode(
|
|
52
|
'txt', config=True,
|
|
52
|
'txt', config=True,
|
|
53
|
help="Extension of the file that should be written to disk"
|
|
53
|
help="Extension of the file that should be written to disk"
|
|
54
|
)
|
|
54
|
)
|
|
55
|
|
|
55
|
|
|
56
|
# MIME type of the result file, for HTTP response headers.
|
|
56
|
# MIME type of the result file, for HTTP response headers.
|
|
57
|
# This is *not* a traitlet, because we want to be able to access it from
|
|
57
|
# This is *not* a traitlet, because we want to be able to access it from
|
|
58
|
# the class, not just on instances.
|
|
58
|
# the class, not just on instances.
|
|
59
|
output_mimetype = ''
|
|
59
|
output_mimetype = ''
|
|
60
|
|
|
60
|
|
|
61
|
#Configurability, allows the user to easily add filters and preprocessors.
|
|
61
|
#Configurability, allows the user to easily add filters and preprocessors.
|
|
62
|
preprocessors = List(config=True,
|
|
62
|
preprocessors = List(config=True,
|
|
63
|
help="""List of preprocessors, by name or namespace, to enable.""")
|
|
63
|
help="""List of preprocessors, by name or namespace, to enable.""")
|
|
64
|
|
|
64
|
|
|
65
|
_preprocessors = List()
|
|
65
|
_preprocessors = List()
|
|
66
|
|
|
66
|
|
|
67
|
default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
|
|
67
|
default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
|
|
68
|
'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
|
|
68
|
'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
|
|
69
|
'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
|
|
69
|
'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
|
|
70
|
'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
|
|
70
|
'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
|
|
71
|
'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
|
|
71
|
'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
|
|
72
|
'IPython.nbconvert.preprocessors.LatexPreprocessor',
|
|
72
|
'IPython.nbconvert.preprocessors.LatexPreprocessor',
|
|
|
|
|
73
|
'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
|
|
73
|
'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
|
|
74
|
'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
|
|
74
|
config=True,
|
|
75
|
config=True,
|
|
75
|
help="""List of preprocessors available by default, by name, namespace,
|
|
76
|
help="""List of preprocessors available by default, by name, namespace,
|
|
76
|
instance, or type.""")
|
|
77
|
instance, or type.""")
|
|
77
|
|
|
78
|
|
|
78
|
|
|
79
|
|
|
79
|
def __init__(self, config=None, **kw):
|
|
80
|
def __init__(self, config=None, **kw):
|
|
80
|
"""
|
|
81
|
"""
|
|
81
|
Public constructor
|
|
82
|
Public constructor
|
|
82
|
|
|
83
|
|
|
83
|
Parameters
|
|
84
|
Parameters
|
|
84
|
----------
|
|
85
|
----------
|
|
85
|
config : config
|
|
86
|
config : config
|
|
86
|
User configuration instance.
|
|
87
|
User configuration instance.
|
|
87
|
"""
|
|
88
|
"""
|
|
88
|
with_default_config = self.default_config
|
|
89
|
with_default_config = self.default_config
|
|
89
|
if config:
|
|
90
|
if config:
|
|
90
|
with_default_config.merge(config)
|
|
91
|
with_default_config.merge(config)
|
|
91
|
|
|
92
|
|
|
92
|
super(Exporter, self).__init__(config=with_default_config, **kw)
|
|
93
|
super(Exporter, self).__init__(config=with_default_config, **kw)
|
|
93
|
|
|
94
|
|
|
94
|
self._init_preprocessors()
|
|
95
|
self._init_preprocessors()
|
|
95
|
|
|
96
|
|
|
96
|
|
|
97
|
|
|
97
|
@property
|
|
98
|
@property
|
|
98
|
def default_config(self):
|
|
99
|
def default_config(self):
|
|
99
|
return Config()
|
|
100
|
return Config()
|
|
100
|
|
|
101
|
|
|
101
|
@nbformat.docstring_nbformat_mod
|
|
102
|
@nbformat.docstring_nbformat_mod
|
|
102
|
def from_notebook_node(self, nb, resources=None, **kw):
|
|
103
|
def from_notebook_node(self, nb, resources=None, **kw):
|
|
103
|
"""
|
|
104
|
"""
|
|
104
|
Convert a notebook from a notebook node instance.
|
|
105
|
Convert a notebook from a notebook node instance.
|
|
105
|
|
|
106
|
|
|
106
|
Parameters
|
|
107
|
Parameters
|
|
107
|
----------
|
|
108
|
----------
|
|
108
|
nb : :class:`~{nbformat_mod}.nbbase.NotebookNode`
|
|
109
|
nb : :class:`~{nbformat_mod}.nbbase.NotebookNode`
|
|
109
|
Notebook node
|
|
110
|
Notebook node
|
|
110
|
resources : dict
|
|
111
|
resources : dict
|
|
111
|
Additional resources that can be accessed read/write by
|
|
112
|
Additional resources that can be accessed read/write by
|
|
112
|
preprocessors and filters.
|
|
113
|
preprocessors and filters.
|
|
113
|
**kw
|
|
114
|
**kw
|
|
114
|
Ignored (?)
|
|
115
|
Ignored (?)
|
|
115
|
"""
|
|
116
|
"""
|
|
116
|
nb_copy = copy.deepcopy(nb)
|
|
117
|
nb_copy = copy.deepcopy(nb)
|
|
117
|
resources = self._init_resources(resources)
|
|
118
|
resources = self._init_resources(resources)
|
|
118
|
|
|
119
|
|
|
119
|
if 'language' in nb['metadata']:
|
|
120
|
if 'language' in nb['metadata']:
|
|
120
|
resources['language'] = nb['metadata']['language'].lower()
|
|
121
|
resources['language'] = nb['metadata']['language'].lower()
|
|
121
|
|
|
122
|
|
|
122
|
# Preprocess
|
|
123
|
# Preprocess
|
|
123
|
nb_copy, resources = self._preprocess(nb_copy, resources)
|
|
124
|
nb_copy, resources = self._preprocess(nb_copy, resources)
|
|
124
|
|
|
125
|
|
|
125
|
return nb_copy, resources
|
|
126
|
return nb_copy, resources
|
|
126
|
|
|
127
|
|
|
127
|
|
|
128
|
|
|
128
|
def from_filename(self, filename, resources=None, **kw):
|
|
129
|
def from_filename(self, filename, resources=None, **kw):
|
|
129
|
"""
|
|
130
|
"""
|
|
130
|
Convert a notebook from a notebook file.
|
|
131
|
Convert a notebook from a notebook file.
|
|
131
|
|
|
132
|
|
|
132
|
Parameters
|
|
133
|
Parameters
|
|
133
|
----------
|
|
134
|
----------
|
|
134
|
filename : str
|
|
135
|
filename : str
|
|
135
|
Full filename of the notebook file to open and convert.
|
|
136
|
Full filename of the notebook file to open and convert.
|
|
136
|
"""
|
|
137
|
"""
|
|
137
|
|
|
138
|
|
|
138
|
# Pull the metadata from the filesystem.
|
|
139
|
# Pull the metadata from the filesystem.
|
|
139
|
if resources is None:
|
|
140
|
if resources is None:
|
|
140
|
resources = ResourcesDict()
|
|
141
|
resources = ResourcesDict()
|
|
141
|
if not 'metadata' in resources or resources['metadata'] == '':
|
|
142
|
if not 'metadata' in resources or resources['metadata'] == '':
|
|
142
|
resources['metadata'] = ResourcesDict()
|
|
143
|
resources['metadata'] = ResourcesDict()
|
|
143
|
basename = os.path.basename(filename)
|
|
144
|
basename = os.path.basename(filename)
|
|
144
|
notebook_name = basename[:basename.rfind('.')]
|
|
145
|
notebook_name = basename[:basename.rfind('.')]
|
|
145
|
resources['metadata']['name'] = notebook_name
|
|
146
|
resources['metadata']['name'] = notebook_name
|
|
146
|
|
|
147
|
|
|
147
|
modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
|
|
148
|
modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
|
|
148
|
resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
|
|
149
|
resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
|
|
149
|
|
|
150
|
|
|
150
|
with io.open(filename, encoding='utf-8') as f:
|
|
151
|
with io.open(filename, encoding='utf-8') as f:
|
|
151
|
return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
|
|
152
|
return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
|
|
152
|
|
|
153
|
|
|
153
|
|
|
154
|
|
|
154
|
def from_file(self, file_stream, resources=None, **kw):
|
|
155
|
def from_file(self, file_stream, resources=None, **kw):
|
|
155
|
"""
|
|
156
|
"""
|
|
156
|
Convert a notebook from a notebook file.
|
|
157
|
Convert a notebook from a notebook file.
|
|
157
|
|
|
158
|
|
|
158
|
Parameters
|
|
159
|
Parameters
|
|
159
|
----------
|
|
160
|
----------
|
|
160
|
file_stream : file-like object
|
|
161
|
file_stream : file-like object
|
|
161
|
Notebook file-like object to convert.
|
|
162
|
Notebook file-like object to convert.
|
|
162
|
"""
|
|
163
|
"""
|
|
163
|
return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
|
|
164
|
return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
|
|
164
|
|
|
165
|
|
|
165
|
|
|
166
|
|
|
166
|
def register_preprocessor(self, preprocessor, enabled=False):
|
|
167
|
def register_preprocessor(self, preprocessor, enabled=False):
|
|
167
|
"""
|
|
168
|
"""
|
|
168
|
Register a preprocessor.
|
|
169
|
Register a preprocessor.
|
|
169
|
Preprocessors are classes that act upon the notebook before it is
|
|
170
|
Preprocessors are classes that act upon the notebook before it is
|
|
170
|
passed into the Jinja templating engine. preprocessors are also
|
|
171
|
passed into the Jinja templating engine. preprocessors are also
|
|
171
|
capable of passing additional information to the Jinja
|
|
172
|
capable of passing additional information to the Jinja
|
|
172
|
templating engine.
|
|
173
|
templating engine.
|
|
173
|
|
|
174
|
|
|
174
|
Parameters
|
|
175
|
Parameters
|
|
175
|
----------
|
|
176
|
----------
|
|
176
|
preprocessor : preprocessor
|
|
177
|
preprocessor : preprocessor
|
|
177
|
"""
|
|
178
|
"""
|
|
178
|
if preprocessor is None:
|
|
179
|
if preprocessor is None:
|
|
179
|
raise TypeError('preprocessor')
|
|
180
|
raise TypeError('preprocessor')
|
|
180
|
isclass = isinstance(preprocessor, type)
|
|
181
|
isclass = isinstance(preprocessor, type)
|
|
181
|
constructed = not isclass
|
|
182
|
constructed = not isclass
|
|
182
|
|
|
183
|
|
|
183
|
# Handle preprocessor's registration based on it's type
|
|
184
|
# Handle preprocessor's registration based on it's type
|
|
184
|
if constructed and isinstance(preprocessor, py3compat.string_types):
|
|
185
|
if constructed and isinstance(preprocessor, py3compat.string_types):
|
|
185
|
# Preprocessor is a string, import the namespace and recursively call
|
|
186
|
# Preprocessor is a string, import the namespace and recursively call
|
|
186
|
# this register_preprocessor method
|
|
187
|
# this register_preprocessor method
|
|
187
|
preprocessor_cls = import_item(preprocessor)
|
|
188
|
preprocessor_cls = import_item(preprocessor)
|
|
188
|
return self.register_preprocessor(preprocessor_cls, enabled)
|
|
189
|
return self.register_preprocessor(preprocessor_cls, enabled)
|
|
189
|
|
|
190
|
|
|
190
|
if constructed and hasattr(preprocessor, '__call__'):
|
|
191
|
if constructed and hasattr(preprocessor, '__call__'):
|
|
191
|
# Preprocessor is a function, no need to construct it.
|
|
192
|
# Preprocessor is a function, no need to construct it.
|
|
192
|
# Register and return the preprocessor.
|
|
193
|
# Register and return the preprocessor.
|
|
193
|
if enabled:
|
|
194
|
if enabled:
|
|
194
|
preprocessor.enabled = True
|
|
195
|
preprocessor.enabled = True
|
|
195
|
self._preprocessors.append(preprocessor)
|
|
196
|
self._preprocessors.append(preprocessor)
|
|
196
|
return preprocessor
|
|
197
|
return preprocessor
|
|
197
|
|
|
198
|
|
|
198
|
elif isclass and isinstance(preprocessor, MetaHasTraits):
|
|
199
|
elif isclass and isinstance(preprocessor, MetaHasTraits):
|
|
199
|
# Preprocessor is configurable. Make sure to pass in new default for
|
|
200
|
# Preprocessor is configurable. Make sure to pass in new default for
|
|
200
|
# the enabled flag if one was specified.
|
|
201
|
# the enabled flag if one was specified.
|
|
201
|
self.register_preprocessor(preprocessor(parent=self), enabled)
|
|
202
|
self.register_preprocessor(preprocessor(parent=self), enabled)
|
|
202
|
|
|
203
|
|
|
203
|
elif isclass:
|
|
204
|
elif isclass:
|
|
204
|
# Preprocessor is not configurable, construct it
|
|
205
|
# Preprocessor is not configurable, construct it
|
|
205
|
self.register_preprocessor(preprocessor(), enabled)
|
|
206
|
self.register_preprocessor(preprocessor(), enabled)
|
|
206
|
|
|
207
|
|
|
207
|
else:
|
|
208
|
else:
|
|
208
|
# Preprocessor is an instance of something without a __call__
|
|
209
|
# Preprocessor is an instance of something without a __call__
|
|
209
|
# attribute.
|
|
210
|
# attribute.
|
|
210
|
raise TypeError('preprocessor')
|
|
211
|
raise TypeError('preprocessor')
|
|
211
|
|
|
212
|
|
|
212
|
|
|
213
|
|
|
213
|
def _init_preprocessors(self):
|
|
214
|
def _init_preprocessors(self):
|
|
214
|
"""
|
|
215
|
"""
|
|
215
|
Register all of the preprocessors needed for this exporter, disabled
|
|
216
|
Register all of the preprocessors needed for this exporter, disabled
|
|
216
|
unless specified explicitly.
|
|
217
|
unless specified explicitly.
|
|
217
|
"""
|
|
218
|
"""
|
|
218
|
self._preprocessors = []
|
|
219
|
self._preprocessors = []
|
|
219
|
|
|
220
|
|
|
220
|
# Load default preprocessors (not necessarly enabled by default).
|
|
221
|
# Load default preprocessors (not necessarly enabled by default).
|
|
221
|
for preprocessor in self.default_preprocessors:
|
|
222
|
for preprocessor in self.default_preprocessors:
|
|
222
|
self.register_preprocessor(preprocessor)
|
|
223
|
self.register_preprocessor(preprocessor)
|
|
223
|
|
|
224
|
|
|
224
|
# Load user-specified preprocessors. Enable by default.
|
|
225
|
# Load user-specified preprocessors. Enable by default.
|
|
225
|
for preprocessor in self.preprocessors:
|
|
226
|
for preprocessor in self.preprocessors:
|
|
226
|
self.register_preprocessor(preprocessor, enabled=True)
|
|
227
|
self.register_preprocessor(preprocessor, enabled=True)
|
|
227
|
|
|
228
|
|
|
228
|
|
|
229
|
|
|
229
|
def _init_resources(self, resources):
|
|
230
|
def _init_resources(self, resources):
|
|
230
|
|
|
231
|
|
|
231
|
#Make sure the resources dict is of ResourcesDict type.
|
|
232
|
#Make sure the resources dict is of ResourcesDict type.
|
|
232
|
if resources is None:
|
|
233
|
if resources is None:
|
|
233
|
resources = ResourcesDict()
|
|
234
|
resources = ResourcesDict()
|
|
234
|
if not isinstance(resources, ResourcesDict):
|
|
235
|
if not isinstance(resources, ResourcesDict):
|
|
235
|
new_resources = ResourcesDict()
|
|
236
|
new_resources = ResourcesDict()
|
|
236
|
new_resources.update(resources)
|
|
237
|
new_resources.update(resources)
|
|
237
|
resources = new_resources
|
|
238
|
resources = new_resources
|
|
238
|
|
|
239
|
|
|
239
|
#Make sure the metadata extension exists in resources
|
|
240
|
#Make sure the metadata extension exists in resources
|
|
240
|
if 'metadata' in resources:
|
|
241
|
if 'metadata' in resources:
|
|
241
|
if not isinstance(resources['metadata'], ResourcesDict):
|
|
242
|
if not isinstance(resources['metadata'], ResourcesDict):
|
|
242
|
resources['metadata'] = ResourcesDict(resources['metadata'])
|
|
243
|
resources['metadata'] = ResourcesDict(resources['metadata'])
|
|
243
|
else:
|
|
244
|
else:
|
|
244
|
resources['metadata'] = ResourcesDict()
|
|
245
|
resources['metadata'] = ResourcesDict()
|
|
245
|
if not resources['metadata']['name']:
|
|
246
|
if not resources['metadata']['name']:
|
|
246
|
resources['metadata']['name'] = 'Notebook'
|
|
247
|
resources['metadata']['name'] = 'Notebook'
|
|
247
|
|
|
248
|
|
|
248
|
#Set the output extension
|
|
249
|
#Set the output extension
|
|
249
|
resources['output_extension'] = self.file_extension
|
|
250
|
resources['output_extension'] = self.file_extension
|
|
250
|
return resources
|
|
251
|
return resources
|
|
251
|
|
|
252
|
|
|
252
|
|
|
253
|
|
|
253
|
def _preprocess(self, nb, resources):
|
|
254
|
def _preprocess(self, nb, resources):
|
|
254
|
"""
|
|
255
|
"""
|
|
255
|
Preprocess the notebook before passing it into the Jinja engine.
|
|
256
|
Preprocess the notebook before passing it into the Jinja engine.
|
|
256
|
To preprocess the notebook is to apply all of the
|
|
257
|
To preprocess the notebook is to apply all of the
|
|
257
|
|
|
258
|
|
|
258
|
Parameters
|
|
259
|
Parameters
|
|
259
|
----------
|
|
260
|
----------
|
|
260
|
nb : notebook node
|
|
261
|
nb : notebook node
|
|
261
|
notebook that is being exported.
|
|
262
|
notebook that is being exported.
|
|
262
|
resources : a dict of additional resources that
|
|
263
|
resources : a dict of additional resources that
|
|
263
|
can be accessed read/write by preprocessors
|
|
264
|
can be accessed read/write by preprocessors
|
|
264
|
"""
|
|
265
|
"""
|
|
265
|
|
|
266
|
|
|
266
|
# Do a copy.deepcopy first,
|
|
267
|
# Do a copy.deepcopy first,
|
|
267
|
# we are never safe enough with what the preprocessors could do.
|
|
268
|
# we are never safe enough with what the preprocessors could do.
|
|
268
|
nbc = copy.deepcopy(nb)
|
|
269
|
nbc = copy.deepcopy(nb)
|
|
269
|
resc = copy.deepcopy(resources)
|
|
270
|
resc = copy.deepcopy(resources)
|
|
270
|
|
|
271
|
|
|
271
|
#Run each preprocessor on the notebook. Carry the output along
|
|
272
|
#Run each preprocessor on the notebook. Carry the output along
|
|
272
|
#to each preprocessor
|
|
273
|
#to each preprocessor
|
|
273
|
for preprocessor in self._preprocessors:
|
|
274
|
for preprocessor in self._preprocessors:
|
|
274
|
nbc, resc = preprocessor(nbc, resc)
|
|
275
|
nbc, resc = preprocessor(nbc, resc)
|
|
275
|
return nbc, resc
|
|
276
|
return nbc, resc
|