##// END OF EJS Templates
Fix missing comma
Thomas Kluyver -
Show More
@@ -1,282 +1,282 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([
68 default_preprocessors = List([
69 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
69 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
70 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
70 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
71 'IPython.nbconvert.preprocessors.coalesce_streams',
71 'IPython.nbconvert.preprocessors.coalesce_streams',
72 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
72 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
73 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
73 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
74 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
74 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
75 'IPython.nbconvert.preprocessors.LatexPreprocessor',
75 'IPython.nbconvert.preprocessors.LatexPreprocessor',
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor',
77 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
77 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
78 ],
78 ],
79 config=True,
79 config=True,
80 help="""List of preprocessors available by default, by name, namespace,
80 help="""List of preprocessors available by default, by name, namespace,
81 instance, or type.""")
81 instance, or type.""")
82
82
83
83
84 def __init__(self, config=None, **kw):
84 def __init__(self, config=None, **kw):
85 """
85 """
86 Public constructor
86 Public constructor
87
87
88 Parameters
88 Parameters
89 ----------
89 ----------
90 config : config
90 config : config
91 User configuration instance.
91 User configuration instance.
92 """
92 """
93 with_default_config = self.default_config
93 with_default_config = self.default_config
94 if config:
94 if config:
95 with_default_config.merge(config)
95 with_default_config.merge(config)
96
96
97 super(Exporter, self).__init__(config=with_default_config, **kw)
97 super(Exporter, self).__init__(config=with_default_config, **kw)
98
98
99 self._init_preprocessors()
99 self._init_preprocessors()
100
100
101
101
102 @property
102 @property
103 def default_config(self):
103 def default_config(self):
104 return Config()
104 return Config()
105
105
106 def from_notebook_node(self, nb, resources=None, **kw):
106 def from_notebook_node(self, nb, resources=None, **kw):
107 """
107 """
108 Convert a notebook from a notebook node instance.
108 Convert a notebook from a notebook node instance.
109
109
110 Parameters
110 Parameters
111 ----------
111 ----------
112 nb : :class:`~IPython.nbformat.NotebookNode`
112 nb : :class:`~IPython.nbformat.NotebookNode`
113 Notebook node (dict-like with attr-access)
113 Notebook node (dict-like with attr-access)
114 resources : dict
114 resources : dict
115 Additional resources that can be accessed read/write by
115 Additional resources that can be accessed read/write by
116 preprocessors and filters.
116 preprocessors and filters.
117 **kw
117 **kw
118 Ignored (?)
118 Ignored (?)
119 """
119 """
120 nb_copy = copy.deepcopy(nb)
120 nb_copy = copy.deepcopy(nb)
121 resources = self._init_resources(resources)
121 resources = self._init_resources(resources)
122
122
123 if 'language' in nb['metadata']:
123 if 'language' in nb['metadata']:
124 resources['language'] = nb['metadata']['language'].lower()
124 resources['language'] = nb['metadata']['language'].lower()
125
125
126 # Preprocess
126 # Preprocess
127 nb_copy, resources = self._preprocess(nb_copy, resources)
127 nb_copy, resources = self._preprocess(nb_copy, resources)
128
128
129 return nb_copy, resources
129 return nb_copy, resources
130
130
131
131
132 def from_filename(self, filename, resources=None, **kw):
132 def from_filename(self, filename, resources=None, **kw):
133 """
133 """
134 Convert a notebook from a notebook file.
134 Convert a notebook from a notebook file.
135
135
136 Parameters
136 Parameters
137 ----------
137 ----------
138 filename : str
138 filename : str
139 Full filename of the notebook file to open and convert.
139 Full filename of the notebook file to open and convert.
140 """
140 """
141
141
142 # Pull the metadata from the filesystem.
142 # Pull the metadata from the filesystem.
143 if resources is None:
143 if resources is None:
144 resources = ResourcesDict()
144 resources = ResourcesDict()
145 if not 'metadata' in resources or resources['metadata'] == '':
145 if not 'metadata' in resources or resources['metadata'] == '':
146 resources['metadata'] = ResourcesDict()
146 resources['metadata'] = ResourcesDict()
147 path, basename = os.path.split(filename)
147 path, basename = os.path.split(filename)
148 notebook_name = basename[:basename.rfind('.')]
148 notebook_name = basename[:basename.rfind('.')]
149 resources['metadata']['name'] = notebook_name
149 resources['metadata']['name'] = notebook_name
150 resources['metadata']['path'] = path
150 resources['metadata']['path'] = path
151
151
152 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
152 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
153 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
153 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
154
154
155 with io.open(filename, encoding='utf-8') as f:
155 with io.open(filename, encoding='utf-8') as f:
156 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
156 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
157
157
158
158
159 def from_file(self, file_stream, resources=None, **kw):
159 def from_file(self, file_stream, resources=None, **kw):
160 """
160 """
161 Convert a notebook from a notebook file.
161 Convert a notebook from a notebook file.
162
162
163 Parameters
163 Parameters
164 ----------
164 ----------
165 file_stream : file-like object
165 file_stream : file-like object
166 Notebook file-like object to convert.
166 Notebook file-like object to convert.
167 """
167 """
168 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
168 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
169
169
170
170
171 def register_preprocessor(self, preprocessor, enabled=False):
171 def register_preprocessor(self, preprocessor, enabled=False):
172 """
172 """
173 Register a preprocessor.
173 Register a preprocessor.
174 Preprocessors are classes that act upon the notebook before it is
174 Preprocessors are classes that act upon the notebook before it is
175 passed into the Jinja templating engine. preprocessors are also
175 passed into the Jinja templating engine. preprocessors are also
176 capable of passing additional information to the Jinja
176 capable of passing additional information to the Jinja
177 templating engine.
177 templating engine.
178
178
179 Parameters
179 Parameters
180 ----------
180 ----------
181 preprocessor : preprocessor
181 preprocessor : preprocessor
182 """
182 """
183 if preprocessor is None:
183 if preprocessor is None:
184 raise TypeError('preprocessor')
184 raise TypeError('preprocessor')
185 isclass = isinstance(preprocessor, type)
185 isclass = isinstance(preprocessor, type)
186 constructed = not isclass
186 constructed = not isclass
187
187
188 # Handle preprocessor's registration based on it's type
188 # Handle preprocessor's registration based on it's type
189 if constructed and isinstance(preprocessor, py3compat.string_types):
189 if constructed and isinstance(preprocessor, py3compat.string_types):
190 # Preprocessor is a string, import the namespace and recursively call
190 # Preprocessor is a string, import the namespace and recursively call
191 # this register_preprocessor method
191 # this register_preprocessor method
192 preprocessor_cls = import_item(preprocessor)
192 preprocessor_cls = import_item(preprocessor)
193 return self.register_preprocessor(preprocessor_cls, enabled)
193 return self.register_preprocessor(preprocessor_cls, enabled)
194
194
195 if constructed and hasattr(preprocessor, '__call__'):
195 if constructed and hasattr(preprocessor, '__call__'):
196 # Preprocessor is a function, no need to construct it.
196 # Preprocessor is a function, no need to construct it.
197 # Register and return the preprocessor.
197 # Register and return the preprocessor.
198 if enabled:
198 if enabled:
199 preprocessor.enabled = True
199 preprocessor.enabled = True
200 self._preprocessors.append(preprocessor)
200 self._preprocessors.append(preprocessor)
201 return preprocessor
201 return preprocessor
202
202
203 elif isclass and isinstance(preprocessor, MetaHasTraits):
203 elif isclass and isinstance(preprocessor, MetaHasTraits):
204 # Preprocessor is configurable. Make sure to pass in new default for
204 # Preprocessor is configurable. Make sure to pass in new default for
205 # the enabled flag if one was specified.
205 # the enabled flag if one was specified.
206 self.register_preprocessor(preprocessor(parent=self), enabled)
206 self.register_preprocessor(preprocessor(parent=self), enabled)
207
207
208 elif isclass:
208 elif isclass:
209 # Preprocessor is not configurable, construct it
209 # Preprocessor is not configurable, construct it
210 self.register_preprocessor(preprocessor(), enabled)
210 self.register_preprocessor(preprocessor(), enabled)
211
211
212 else:
212 else:
213 # Preprocessor is an instance of something without a __call__
213 # Preprocessor is an instance of something without a __call__
214 # attribute.
214 # attribute.
215 raise TypeError('preprocessor')
215 raise TypeError('preprocessor')
216
216
217
217
218 def _init_preprocessors(self):
218 def _init_preprocessors(self):
219 """
219 """
220 Register all of the preprocessors needed for this exporter, disabled
220 Register all of the preprocessors needed for this exporter, disabled
221 unless specified explicitly.
221 unless specified explicitly.
222 """
222 """
223 self._preprocessors = []
223 self._preprocessors = []
224
224
225 # Load default preprocessors (not necessarly enabled by default).
225 # Load default preprocessors (not necessarly enabled by default).
226 for preprocessor in self.default_preprocessors:
226 for preprocessor in self.default_preprocessors:
227 self.register_preprocessor(preprocessor)
227 self.register_preprocessor(preprocessor)
228
228
229 # Load user-specified preprocessors. Enable by default.
229 # Load user-specified preprocessors. Enable by default.
230 for preprocessor in self.preprocessors:
230 for preprocessor in self.preprocessors:
231 self.register_preprocessor(preprocessor, enabled=True)
231 self.register_preprocessor(preprocessor, enabled=True)
232
232
233
233
234 def _init_resources(self, resources):
234 def _init_resources(self, resources):
235
235
236 #Make sure the resources dict is of ResourcesDict type.
236 #Make sure the resources dict is of ResourcesDict type.
237 if resources is None:
237 if resources is None:
238 resources = ResourcesDict()
238 resources = ResourcesDict()
239 if not isinstance(resources, ResourcesDict):
239 if not isinstance(resources, ResourcesDict):
240 new_resources = ResourcesDict()
240 new_resources = ResourcesDict()
241 new_resources.update(resources)
241 new_resources.update(resources)
242 resources = new_resources
242 resources = new_resources
243
243
244 #Make sure the metadata extension exists in resources
244 #Make sure the metadata extension exists in resources
245 if 'metadata' in resources:
245 if 'metadata' in resources:
246 if not isinstance(resources['metadata'], ResourcesDict):
246 if not isinstance(resources['metadata'], ResourcesDict):
247 new_metadata = ResourcesDict()
247 new_metadata = ResourcesDict()
248 new_metadata.update(resources['metadata'])
248 new_metadata.update(resources['metadata'])
249 resources['metadata'] = new_metadata
249 resources['metadata'] = new_metadata
250 else:
250 else:
251 resources['metadata'] = ResourcesDict()
251 resources['metadata'] = ResourcesDict()
252 if not resources['metadata']['name']:
252 if not resources['metadata']['name']:
253 resources['metadata']['name'] = 'Notebook'
253 resources['metadata']['name'] = 'Notebook'
254
254
255 #Set the output extension
255 #Set the output extension
256 resources['output_extension'] = self.file_extension
256 resources['output_extension'] = self.file_extension
257 return resources
257 return resources
258
258
259
259
260 def _preprocess(self, nb, resources):
260 def _preprocess(self, nb, resources):
261 """
261 """
262 Preprocess the notebook before passing it into the Jinja engine.
262 Preprocess the notebook before passing it into the Jinja engine.
263 To preprocess the notebook is to apply all of the
263 To preprocess the notebook is to apply all of the
264
264
265 Parameters
265 Parameters
266 ----------
266 ----------
267 nb : notebook node
267 nb : notebook node
268 notebook that is being exported.
268 notebook that is being exported.
269 resources : a dict of additional resources that
269 resources : a dict of additional resources that
270 can be accessed read/write by preprocessors
270 can be accessed read/write by preprocessors
271 """
271 """
272
272
273 # Do a copy.deepcopy first,
273 # Do a copy.deepcopy first,
274 # we are never safe enough with what the preprocessors could do.
274 # we are never safe enough with what the preprocessors could do.
275 nbc = copy.deepcopy(nb)
275 nbc = copy.deepcopy(nb)
276 resc = copy.deepcopy(resources)
276 resc = copy.deepcopy(resources)
277
277
278 #Run each preprocessor on the notebook. Carry the output along
278 #Run each preprocessor on the notebook. Carry the output along
279 #to each preprocessor
279 #to each preprocessor
280 for preprocessor in self._preprocessors:
280 for preprocessor in self._preprocessors:
281 nbc, resc = preprocessor(nbc, resc)
281 nbc, resc = preprocessor(nbc, resc)
282 return nbc, resc
282 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now