##// END OF EJS Templates
fixes #7360 by setting resources.metadata
Nicholas Bollweg -
Show More
@@ -1,151 +1,162 b''
1 """Tornado handlers for nbconvert."""
1 """Tornado handlers for nbconvert."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import io
6 import io
7 import os
7 import os
8 import zipfile
8 import zipfile
9 import collections
9
10
10 from tornado import web
11 from tornado import web
11
12
12 from ..base.handlers import (
13 from ..base.handlers import (
13 IPythonHandler, FilesRedirectHandler,
14 IPythonHandler, FilesRedirectHandler,
14 path_regex,
15 path_regex,
15 )
16 )
16 from IPython.nbformat import from_dict
17 from IPython.nbformat import from_dict
17
18
18 from IPython.utils.py3compat import cast_bytes
19 from IPython.utils.py3compat import cast_bytes
20 from IPython.utils import text
19
21
20 def find_resource_files(output_files_dir):
22 def find_resource_files(output_files_dir):
21 files = []
23 files = []
22 for dirpath, dirnames, filenames in os.walk(output_files_dir):
24 for dirpath, dirnames, filenames in os.walk(output_files_dir):
23 files.extend([os.path.join(dirpath, f) for f in filenames])
25 files.extend([os.path.join(dirpath, f) for f in filenames])
24 return files
26 return files
25
27
26 def respond_zip(handler, name, output, resources):
28 def respond_zip(handler, name, output, resources):
27 """Zip up the output and resource files and respond with the zip file.
29 """Zip up the output and resource files and respond with the zip file.
28
30
29 Returns True if it has served a zip file, False if there are no resource
31 Returns True if it has served a zip file, False if there are no resource
30 files, in which case we serve the plain output file.
32 files, in which case we serve the plain output file.
31 """
33 """
32 # Check if we have resource files we need to zip
34 # Check if we have resource files we need to zip
33 output_files = resources.get('outputs', None)
35 output_files = resources.get('outputs', None)
34 if not output_files:
36 if not output_files:
35 return False
37 return False
36
38
37 # Headers
39 # Headers
38 zip_filename = os.path.splitext(name)[0] + '.zip'
40 zip_filename = os.path.splitext(name)[0] + '.zip'
39 handler.set_header('Content-Disposition',
41 handler.set_header('Content-Disposition',
40 'attachment; filename="%s"' % zip_filename)
42 'attachment; filename="%s"' % zip_filename)
41 handler.set_header('Content-Type', 'application/zip')
43 handler.set_header('Content-Type', 'application/zip')
42
44
43 # Prepare the zip file
45 # Prepare the zip file
44 buffer = io.BytesIO()
46 buffer = io.BytesIO()
45 zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
47 zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
46 output_filename = os.path.splitext(name)[0] + resources['output_extension']
48 output_filename = os.path.splitext(name)[0] + resources['output_extension']
47 zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
49 zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
48 for filename, data in output_files.items():
50 for filename, data in output_files.items():
49 zipf.writestr(os.path.basename(filename), data)
51 zipf.writestr(os.path.basename(filename), data)
50 zipf.close()
52 zipf.close()
51
53
52 handler.finish(buffer.getvalue())
54 handler.finish(buffer.getvalue())
53 return True
55 return True
54
56
55 def get_exporter(format, **kwargs):
57 def get_exporter(format, **kwargs):
56 """get an exporter, raising appropriate errors"""
58 """get an exporter, raising appropriate errors"""
57 # if this fails, will raise 500
59 # if this fails, will raise 500
58 try:
60 try:
59 from IPython.nbconvert.exporters.export import exporter_map
61 from IPython.nbconvert.exporters.export import exporter_map
60 except ImportError as e:
62 except ImportError as e:
61 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
63 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
62
64
63 try:
65 try:
64 Exporter = exporter_map[format]
66 Exporter = exporter_map[format]
65 except KeyError:
67 except KeyError:
66 # should this be 400?
68 # should this be 400?
67 raise web.HTTPError(404, u"No exporter for format: %s" % format)
69 raise web.HTTPError(404, u"No exporter for format: %s" % format)
68
70
69 try:
71 try:
70 return Exporter(**kwargs)
72 return Exporter(**kwargs)
71 except Exception as e:
73 except Exception as e:
72 raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
74 raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
73
75
74 class NbconvertFileHandler(IPythonHandler):
76 class NbconvertFileHandler(IPythonHandler):
75
77
76 SUPPORTED_METHODS = ('GET',)
78 SUPPORTED_METHODS = ('GET',)
77
79
78 @web.authenticated
80 @web.authenticated
79 def get(self, format, path):
81 def get(self, format, path):
80
82
81 exporter = get_exporter(format, config=self.config, log=self.log)
83 exporter = get_exporter(format, config=self.config, log=self.log)
82
84
83 path = path.strip('/')
85 path = path.strip('/')
84 model = self.contents_manager.get(path=path)
86 model = self.contents_manager.get(path=path)
85 name = model['name']
87 name = model['name']
86 if model['type'] != 'notebook':
88 if model['type'] != 'notebook':
87 raise web.HTTPError(400, "Not a notebook: %s" % path)
89 raise web.HTTPError(400, "Not a notebook: %s" % path)
88
90
89 self.set_header('Last-Modified', model['last_modified'])
91 self.set_header('Last-Modified', model['last_modified'])
90
92
91 try:
93 try:
92 output, resources = exporter.from_notebook_node(model['content'])
94 output, resources = exporter.from_notebook_node(
95 model['content'],
96 resources={
97 "metadata": {
98 "name": name[:name.rfind('.')],
99 "modified_date": (model['last_modified']
100 .strftime(text.date_format))
101 }
102 }
103 )
93 except Exception as e:
104 except Exception as e:
94 raise web.HTTPError(500, "nbconvert failed: %s" % e)
105 raise web.HTTPError(500, "nbconvert failed: %s" % e)
95
106
96 if respond_zip(self, name, output, resources):
107 if respond_zip(self, name, output, resources):
97 return
108 return
98
109
99 # Force download if requested
110 # Force download if requested
100 if self.get_argument('download', 'false').lower() == 'true':
111 if self.get_argument('download', 'false').lower() == 'true':
101 filename = os.path.splitext(name)[0] + resources['output_extension']
112 filename = os.path.splitext(name)[0] + resources['output_extension']
102 self.set_header('Content-Disposition',
113 self.set_header('Content-Disposition',
103 'attachment; filename="%s"' % filename)
114 'attachment; filename="%s"' % filename)
104
115
105 # MIME type
116 # MIME type
106 if exporter.output_mimetype:
117 if exporter.output_mimetype:
107 self.set_header('Content-Type',
118 self.set_header('Content-Type',
108 '%s; charset=utf-8' % exporter.output_mimetype)
119 '%s; charset=utf-8' % exporter.output_mimetype)
109
120
110 self.finish(output)
121 self.finish(output)
111
122
112 class NbconvertPostHandler(IPythonHandler):
123 class NbconvertPostHandler(IPythonHandler):
113 SUPPORTED_METHODS = ('POST',)
124 SUPPORTED_METHODS = ('POST',)
114
125
115 @web.authenticated
126 @web.authenticated
116 def post(self, format):
127 def post(self, format):
117 exporter = get_exporter(format, config=self.config)
128 exporter = get_exporter(format, config=self.config)
118
129
119 model = self.get_json_body()
130 model = self.get_json_body()
120 name = model.get('name', 'notebook.ipynb')
131 name = model.get('name', 'notebook.ipynb')
121 nbnode = from_dict(model['content'])
132 nbnode = from_dict(model['content'])
122
133
123 try:
134 try:
124 output, resources = exporter.from_notebook_node(nbnode)
135 output, resources = exporter.from_notebook_node(nbnode)
125 except Exception as e:
136 except Exception as e:
126 raise web.HTTPError(500, "nbconvert failed: %s" % e)
137 raise web.HTTPError(500, "nbconvert failed: %s" % e)
127
138
128 if respond_zip(self, name, output, resources):
139 if respond_zip(self, name, output, resources):
129 return
140 return
130
141
131 # MIME type
142 # MIME type
132 if exporter.output_mimetype:
143 if exporter.output_mimetype:
133 self.set_header('Content-Type',
144 self.set_header('Content-Type',
134 '%s; charset=utf-8' % exporter.output_mimetype)
145 '%s; charset=utf-8' % exporter.output_mimetype)
135
146
136 self.finish(output)
147 self.finish(output)
137
148
138
149
139 #-----------------------------------------------------------------------------
150 #-----------------------------------------------------------------------------
140 # URL to handler mappings
151 # URL to handler mappings
141 #-----------------------------------------------------------------------------
152 #-----------------------------------------------------------------------------
142
153
143 _format_regex = r"(?P<format>\w+)"
154 _format_regex = r"(?P<format>\w+)"
144
155
145
156
146 default_handlers = [
157 default_handlers = [
147 (r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
158 (r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
148 (r"/nbconvert/%s%s" % (_format_regex, path_regex),
159 (r"/nbconvert/%s%s" % (_format_regex, path_regex),
149 NbconvertFileHandler),
160 NbconvertFileHandler),
150 (r"/nbconvert/html%s" % path_regex, FilesRedirectHandler),
161 (r"/nbconvert/html%s" % path_regex, FilesRedirectHandler),
151 ]
162 ]
@@ -1,277 +1,279 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 from __future__ import print_function, absolute_import
6 from __future__ import print_function, absolute_import
7
7
8 import io
8 import io
9 import os
9 import os
10 import copy
10 import copy
11 import collections
11 import collections
12 import datetime
12 import datetime
13
13
14 from IPython.config.configurable import LoggingConfigurable
14 from IPython.config.configurable import LoggingConfigurable
15 from IPython.config import Config
15 from IPython.config import Config
16 from IPython import nbformat
16 from IPython import nbformat
17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, TraitError
17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, TraitError
18 from IPython.utils.importstring import import_item
18 from IPython.utils.importstring import import_item
19 from IPython.utils import text, py3compat
19 from IPython.utils import text, py3compat
20
20
21
21
22 class ResourcesDict(collections.defaultdict):
22 class ResourcesDict(collections.defaultdict):
23 def __missing__(self, key):
23 def __missing__(self, key):
24 return ''
24 return ''
25
25
26
26
27 class FilenameExtension(Unicode):
27 class FilenameExtension(Unicode):
28 """A trait for filename extensions."""
28 """A trait for filename extensions."""
29
29
30 default_value = u''
30 default_value = u''
31 info_text = 'a filename extension, beginning with a dot'
31 info_text = 'a filename extension, beginning with a dot'
32
32
33 def validate(self, obj, value):
33 def validate(self, obj, value):
34 # cast to proper unicode
34 # cast to proper unicode
35 value = super(FilenameExtension, self).validate(obj, value)
35 value = super(FilenameExtension, self).validate(obj, value)
36
36
37 # check that it starts with a dot
37 # check that it starts with a dot
38 if value and not value.startswith('.'):
38 if value and not value.startswith('.'):
39 msg = "FileExtension trait '{}' does not begin with a dot: {!r}"
39 msg = "FileExtension trait '{}' does not begin with a dot: {!r}"
40 raise TraitError(msg.format(self.name, value))
40 raise TraitError(msg.format(self.name, value))
41
41
42 return value
42 return value
43
43
44
44
45 class Exporter(LoggingConfigurable):
45 class Exporter(LoggingConfigurable):
46 """
46 """
47 Class containing methods that sequentially run a list of preprocessors on a
47 Class containing methods that sequentially run a list of preprocessors on a
48 NotebookNode object and then return the modified NotebookNode object and
48 NotebookNode object and then return the modified NotebookNode object and
49 accompanying resources dict.
49 accompanying resources dict.
50 """
50 """
51
51
52 file_extension = FilenameExtension(
52 file_extension = FilenameExtension(
53 '.txt', config=True,
53 '.txt', config=True,
54 help="Extension of the file that should be written to disk"
54 help="Extension of the file that should be written to disk"
55 )
55 )
56
56
57 # MIME type of the result file, for HTTP response headers.
57 # MIME type of the result file, for HTTP response headers.
58 # This is *not* a traitlet, because we want to be able to access it from
58 # This is *not* a traitlet, because we want to be able to access it from
59 # the class, not just on instances.
59 # the class, not just on instances.
60 output_mimetype = ''
60 output_mimetype = ''
61
61
62 #Configurability, allows the user to easily add filters and preprocessors.
62 #Configurability, allows the user to easily add filters and preprocessors.
63 preprocessors = List(config=True,
63 preprocessors = List(config=True,
64 help="""List of preprocessors, by name or namespace, to enable.""")
64 help="""List of preprocessors, by name or namespace, to enable.""")
65
65
66 _preprocessors = List()
66 _preprocessors = List()
67
67
68 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
68 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
69 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
69 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
70 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
70 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
71 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
71 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
72 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
73 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 'IPython.nbconvert.preprocessors.LatexPreprocessor',
74 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
75 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
75 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
77 config=True,
77 config=True,
78 help="""List of preprocessors available by default, by name, namespace,
78 help="""List of preprocessors available by default, by name, namespace,
79 instance, or type.""")
79 instance, or type.""")
80
80
81
81
82 def __init__(self, config=None, **kw):
82 def __init__(self, config=None, **kw):
83 """
83 """
84 Public constructor
84 Public constructor
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 config : config
88 config : config
89 User configuration instance.
89 User configuration instance.
90 """
90 """
91 with_default_config = self.default_config
91 with_default_config = self.default_config
92 if config:
92 if config:
93 with_default_config.merge(config)
93 with_default_config.merge(config)
94
94
95 super(Exporter, self).__init__(config=with_default_config, **kw)
95 super(Exporter, self).__init__(config=with_default_config, **kw)
96
96
97 self._init_preprocessors()
97 self._init_preprocessors()
98
98
99
99
100 @property
100 @property
101 def default_config(self):
101 def default_config(self):
102 return Config()
102 return Config()
103
103
104 def from_notebook_node(self, nb, resources=None, **kw):
104 def from_notebook_node(self, nb, resources=None, **kw):
105 """
105 """
106 Convert a notebook from a notebook node instance.
106 Convert a notebook from a notebook node instance.
107
107
108 Parameters
108 Parameters
109 ----------
109 ----------
110 nb : :class:`~IPython.nbformat.NotebookNode`
110 nb : :class:`~IPython.nbformat.NotebookNode`
111 Notebook node (dict-like with attr-access)
111 Notebook node (dict-like with attr-access)
112 resources : dict
112 resources : dict
113 Additional resources that can be accessed read/write by
113 Additional resources that can be accessed read/write by
114 preprocessors and filters.
114 preprocessors and filters.
115 **kw
115 **kw
116 Ignored (?)
116 Ignored (?)
117 """
117 """
118 nb_copy = copy.deepcopy(nb)
118 nb_copy = copy.deepcopy(nb)
119 resources = self._init_resources(resources)
119 resources = self._init_resources(resources)
120
120
121 if 'language' in nb['metadata']:
121 if 'language' in nb['metadata']:
122 resources['language'] = nb['metadata']['language'].lower()
122 resources['language'] = nb['metadata']['language'].lower()
123
123
124 # Preprocess
124 # Preprocess
125 nb_copy, resources = self._preprocess(nb_copy, resources)
125 nb_copy, resources = self._preprocess(nb_copy, resources)
126
126
127 return nb_copy, resources
127 return nb_copy, resources
128
128
129
129
130 def from_filename(self, filename, resources=None, **kw):
130 def from_filename(self, filename, resources=None, **kw):
131 """
131 """
132 Convert a notebook from a notebook file.
132 Convert a notebook from a notebook file.
133
133
134 Parameters
134 Parameters
135 ----------
135 ----------
136 filename : str
136 filename : str
137 Full filename of the notebook file to open and convert.
137 Full filename of the notebook file to open and convert.
138 """
138 """
139
139
140 # Pull the metadata from the filesystem.
140 # Pull the metadata from the filesystem.
141 if resources is None:
141 if resources is None:
142 resources = ResourcesDict()
142 resources = ResourcesDict()
143 if not 'metadata' in resources or resources['metadata'] == '':
143 if not 'metadata' in resources or resources['metadata'] == '':
144 resources['metadata'] = ResourcesDict()
144 resources['metadata'] = ResourcesDict()
145 basename = os.path.basename(filename)
145 basename = os.path.basename(filename)
146 notebook_name = basename[:basename.rfind('.')]
146 notebook_name = basename[:basename.rfind('.')]
147 resources['metadata']['name'] = notebook_name
147 resources['metadata']['name'] = notebook_name
148
148
149 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
149 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
150 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
150 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
151
151
152 with io.open(filename, encoding='utf-8') as f:
152 with io.open(filename, encoding='utf-8') as f:
153 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
153 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
154
154
155
155
156 def from_file(self, file_stream, resources=None, **kw):
156 def from_file(self, file_stream, resources=None, **kw):
157 """
157 """
158 Convert a notebook from a notebook file.
158 Convert a notebook from a notebook file.
159
159
160 Parameters
160 Parameters
161 ----------
161 ----------
162 file_stream : file-like object
162 file_stream : file-like object
163 Notebook file-like object to convert.
163 Notebook file-like object to convert.
164 """
164 """
165 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
165 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
166
166
167
167
168 def register_preprocessor(self, preprocessor, enabled=False):
168 def register_preprocessor(self, preprocessor, enabled=False):
169 """
169 """
170 Register a preprocessor.
170 Register a preprocessor.
171 Preprocessors are classes that act upon the notebook before it is
171 Preprocessors are classes that act upon the notebook before it is
172 passed into the Jinja templating engine. preprocessors are also
172 passed into the Jinja templating engine. preprocessors are also
173 capable of passing additional information to the Jinja
173 capable of passing additional information to the Jinja
174 templating engine.
174 templating engine.
175
175
176 Parameters
176 Parameters
177 ----------
177 ----------
178 preprocessor : preprocessor
178 preprocessor : preprocessor
179 """
179 """
180 if preprocessor is None:
180 if preprocessor is None:
181 raise TypeError('preprocessor')
181 raise TypeError('preprocessor')
182 isclass = isinstance(preprocessor, type)
182 isclass = isinstance(preprocessor, type)
183 constructed = not isclass
183 constructed = not isclass
184
184
185 # Handle preprocessor's registration based on it's type
185 # Handle preprocessor's registration based on it's type
186 if constructed and isinstance(preprocessor, py3compat.string_types):
186 if constructed and isinstance(preprocessor, py3compat.string_types):
187 # Preprocessor is a string, import the namespace and recursively call
187 # Preprocessor is a string, import the namespace and recursively call
188 # this register_preprocessor method
188 # this register_preprocessor method
189 preprocessor_cls = import_item(preprocessor)
189 preprocessor_cls = import_item(preprocessor)
190 return self.register_preprocessor(preprocessor_cls, enabled)
190 return self.register_preprocessor(preprocessor_cls, enabled)
191
191
192 if constructed and hasattr(preprocessor, '__call__'):
192 if constructed and hasattr(preprocessor, '__call__'):
193 # Preprocessor is a function, no need to construct it.
193 # Preprocessor is a function, no need to construct it.
194 # Register and return the preprocessor.
194 # Register and return the preprocessor.
195 if enabled:
195 if enabled:
196 preprocessor.enabled = True
196 preprocessor.enabled = True
197 self._preprocessors.append(preprocessor)
197 self._preprocessors.append(preprocessor)
198 return preprocessor
198 return preprocessor
199
199
200 elif isclass and isinstance(preprocessor, MetaHasTraits):
200 elif isclass and isinstance(preprocessor, MetaHasTraits):
201 # Preprocessor is configurable. Make sure to pass in new default for
201 # Preprocessor is configurable. Make sure to pass in new default for
202 # the enabled flag if one was specified.
202 # the enabled flag if one was specified.
203 self.register_preprocessor(preprocessor(parent=self), enabled)
203 self.register_preprocessor(preprocessor(parent=self), enabled)
204
204
205 elif isclass:
205 elif isclass:
206 # Preprocessor is not configurable, construct it
206 # Preprocessor is not configurable, construct it
207 self.register_preprocessor(preprocessor(), enabled)
207 self.register_preprocessor(preprocessor(), enabled)
208
208
209 else:
209 else:
210 # Preprocessor is an instance of something without a __call__
210 # Preprocessor is an instance of something without a __call__
211 # attribute.
211 # attribute.
212 raise TypeError('preprocessor')
212 raise TypeError('preprocessor')
213
213
214
214
215 def _init_preprocessors(self):
215 def _init_preprocessors(self):
216 """
216 """
217 Register all of the preprocessors needed for this exporter, disabled
217 Register all of the preprocessors needed for this exporter, disabled
218 unless specified explicitly.
218 unless specified explicitly.
219 """
219 """
220 self._preprocessors = []
220 self._preprocessors = []
221
221
222 # Load default preprocessors (not necessarly enabled by default).
222 # Load default preprocessors (not necessarly enabled by default).
223 for preprocessor in self.default_preprocessors:
223 for preprocessor in self.default_preprocessors:
224 self.register_preprocessor(preprocessor)
224 self.register_preprocessor(preprocessor)
225
225
226 # Load user-specified preprocessors. Enable by default.
226 # Load user-specified preprocessors. Enable by default.
227 for preprocessor in self.preprocessors:
227 for preprocessor in self.preprocessors:
228 self.register_preprocessor(preprocessor, enabled=True)
228 self.register_preprocessor(preprocessor, enabled=True)
229
229
230
230
231 def _init_resources(self, resources):
231 def _init_resources(self, resources):
232
232
233 #Make sure the resources dict is of ResourcesDict type.
233 #Make sure the resources dict is of ResourcesDict type.
234 if resources is None:
234 if resources is None:
235 resources = ResourcesDict()
235 resources = ResourcesDict()
236 if not isinstance(resources, ResourcesDict):
236 if not isinstance(resources, ResourcesDict):
237 new_resources = ResourcesDict()
237 new_resources = ResourcesDict()
238 new_resources.update(resources)
238 new_resources.update(resources)
239 resources = new_resources
239 resources = new_resources
240
240
241 #Make sure the metadata extension exists in resources
241 #Make sure the metadata extension exists in resources
242 if 'metadata' in resources:
242 if 'metadata' in resources:
243 if not isinstance(resources['metadata'], ResourcesDict):
243 if not isinstance(resources['metadata'], ResourcesDict):
244 resources['metadata'] = ResourcesDict(resources['metadata'])
244 new_metadata = ResourcesDict()
245 new_metadata.update(resources['metadata'])
246 resources['metadata'] = new_metadata
245 else:
247 else:
246 resources['metadata'] = ResourcesDict()
248 resources['metadata'] = ResourcesDict()
247 if not resources['metadata']['name']:
249 if not resources['metadata']['name']:
248 resources['metadata']['name'] = 'Notebook'
250 resources['metadata']['name'] = 'Notebook'
249
251
250 #Set the output extension
252 #Set the output extension
251 resources['output_extension'] = self.file_extension
253 resources['output_extension'] = self.file_extension
252 return resources
254 return resources
253
255
254
256
255 def _preprocess(self, nb, resources):
257 def _preprocess(self, nb, resources):
256 """
258 """
257 Preprocess the notebook before passing it into the Jinja engine.
259 Preprocess the notebook before passing it into the Jinja engine.
258 To preprocess the notebook is to apply all of the
260 To preprocess the notebook is to apply all of the
259
261
260 Parameters
262 Parameters
261 ----------
263 ----------
262 nb : notebook node
264 nb : notebook node
263 notebook that is being exported.
265 notebook that is being exported.
264 resources : a dict of additional resources that
266 resources : a dict of additional resources that
265 can be accessed read/write by preprocessors
267 can be accessed read/write by preprocessors
266 """
268 """
267
269
268 # Do a copy.deepcopy first,
270 # Do a copy.deepcopy first,
269 # we are never safe enough with what the preprocessors could do.
271 # we are never safe enough with what the preprocessors could do.
270 nbc = copy.deepcopy(nb)
272 nbc = copy.deepcopy(nb)
271 resc = copy.deepcopy(resources)
273 resc = copy.deepcopy(resources)
272
274
273 #Run each preprocessor on the notebook. Carry the output along
275 #Run each preprocessor on the notebook. Carry the output along
274 #to each preprocessor
276 #to each preprocessor
275 for preprocessor in self._preprocessors:
277 for preprocessor in self._preprocessors:
276 nbc, resc = preprocessor(nbc, resc)
278 nbc, resc = preprocessor(nbc, resc)
277 return nbc, resc
279 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now