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