##// END OF EJS Templates
Merge pull request #3539 from minrk/nbconvert_paths...
Jonathan Frederic -
r11190:77487ddf merge
parent child Browse files
Show More
@@ -1,338 +1,338 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 from copy import deepcopy
23 from copy import deepcopy
24
24
25 # other libs/dependencies
25 # other libs/dependencies
26 from jinja2 import Environment, FileSystemLoader
26 from jinja2 import Environment, FileSystemLoader
27 from markdown import markdown
27 from markdown import markdown
28
28
29 # IPython imports
29 # IPython imports
30 from IPython.config.configurable import Configurable
30 from IPython.config.configurable import Configurable
31 from IPython.config import Config
31 from IPython.config import Config
32 from IPython.nbformat import current as nbformat
32 from IPython.nbformat import current as nbformat
33 from IPython.utils.traitlets import MetaHasTraits, Unicode
33 from IPython.utils.traitlets import MetaHasTraits, Unicode
34 from IPython.utils.text import indent
34 from IPython.utils.text import indent
35
35
36 from IPython.nbconvert import filters
36 from IPython.nbconvert import filters
37 from IPython.nbconvert import transformers
37 from IPython.nbconvert import transformers
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Globals and constants
40 # Globals and constants
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 #Jinja2 extensions to load.
43 #Jinja2 extensions to load.
44 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
44 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
45
45
46 default_filters = {
46 default_filters = {
47 'indent': indent,
47 'indent': indent,
48 'markdown': markdown,
48 'markdown': markdown,
49 'ansi2html': filters.ansi2html,
49 'ansi2html': filters.ansi2html,
50 'filter_data_type': filters.DataTypeFilter,
50 'filter_data_type': filters.DataTypeFilter,
51 'get_lines': filters.get_lines,
51 'get_lines': filters.get_lines,
52 'highlight': filters.highlight,
52 'highlight': filters.highlight,
53 'highlight2html': filters.highlight,
53 'highlight2html': filters.highlight,
54 'highlight2latex': filters.highlight2latex,
54 'highlight2latex': filters.highlight2latex,
55 'markdown2latex': filters.markdown2latex,
55 'markdown2latex': filters.markdown2latex,
56 'markdown2rst': filters.markdown2rst,
56 'markdown2rst': filters.markdown2rst,
57 'pycomment': filters.python_comment,
57 'pycomment': filters.python_comment,
58 'rm_ansi': filters.remove_ansi,
58 'rm_ansi': filters.remove_ansi,
59 'rm_dollars': filters.strip_dollars,
59 'rm_dollars': filters.strip_dollars,
60 'rm_fake': filters.rm_fake,
60 'rm_fake': filters.rm_fake,
61 'ansi2latex': filters.ansi2latex,
61 'ansi2latex': filters.ansi2latex,
62 'rm_math_space': filters.rm_math_space,
62 'rm_math_space': filters.rm_math_space,
63 'wrap': filters.wrap
63 'wrap': filters.wrap
64 }
64 }
65
65
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67 # Class
67 # Class
68 #-----------------------------------------------------------------------------
68 #-----------------------------------------------------------------------------
69
69
70 class Exporter(Configurable):
70 class Exporter(Configurable):
71 """
71 """
72 Exports notebooks into other file formats. Uses Jinja 2 templating engine
72 Exports notebooks into other file formats. Uses Jinja 2 templating engine
73 to output new formats. Inherit from this class if you are creating a new
73 to output new formats. Inherit from this class if you are creating a new
74 template type along with new filters/transformers. If the filters/
74 template type along with new filters/transformers. If the filters/
75 transformers provided by default suffice, there is no need to inherit from
75 transformers provided by default suffice, there is no need to inherit from
76 this class. Instead, override the template_file and file_extension
76 this class. Instead, override the template_file and file_extension
77 traits via a config file.
77 traits via a config file.
78
78
79 {filters}
79 {filters}
80 """
80 """
81
81
82 # finish the docstring
82 # finish the docstring
83 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
83 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
84
84
85
85
86 template_file = Unicode(
86 template_file = Unicode(
87 '', config=True,
87 '', config=True,
88 help="Name of the template file to use")
88 help="Name of the template file to use")
89
89
90 file_extension = Unicode(
90 file_extension = Unicode(
91 'txt', config=True,
91 'txt', config=True,
92 help="Extension of the file that should be written to disk"
92 help="Extension of the file that should be written to disk"
93 )
93 )
94
94
95 template_path = Unicode(
95 template_path = Unicode(
96 "/../templates/", config=True,
96 os.path.join("..", "templates"), config=True,
97 help="Path where the template files are located.")
97 help="Path where the template files are located.")
98
98
99 template_skeleton_path = Unicode(
99 template_skeleton_path = Unicode(
100 "/../templates/skeleton/", config=True,
100 os.path.join("..", "templates", "skeleton"), config=True,
101 help="Path where the template skeleton files are located.")
101 help="Path where the template skeleton files are located.")
102
102
103 #Jinja block definitions
103 #Jinja block definitions
104 jinja_comment_block_start = Unicode("", config=True)
104 jinja_comment_block_start = Unicode("", config=True)
105 jinja_comment_block_end = Unicode("", config=True)
105 jinja_comment_block_end = Unicode("", config=True)
106 jinja_variable_block_start = Unicode("", config=True)
106 jinja_variable_block_start = Unicode("", config=True)
107 jinja_variable_block_end = Unicode("", config=True)
107 jinja_variable_block_end = Unicode("", config=True)
108 jinja_logic_block_start = Unicode("", config=True)
108 jinja_logic_block_start = Unicode("", config=True)
109 jinja_logic_block_end = Unicode("", config=True)
109 jinja_logic_block_end = Unicode("", config=True)
110
110
111 #Extension that the template files use.
111 #Extension that the template files use.
112 template_extension = Unicode(".tpl", config=True)
112 template_extension = Unicode(".tpl", config=True)
113
113
114 #Processors that process the input data prior to the export, set in the
114 #Processors that process the input data prior to the export, set in the
115 #constructor for this class.
115 #constructor for this class.
116 transformers = None
116 transformers = None
117
117
118
118
119 def __init__(self, transformers=None, filters=None, config=None, **kw):
119 def __init__(self, transformers=None, filters=None, config=None, **kw):
120 """
120 """
121 Public constructor
121 Public constructor
122
122
123 Parameters
123 Parameters
124 ----------
124 ----------
125 transformers : list[of transformer]
125 transformers : list[of transformer]
126 Custom transformers to apply to the notebook prior to engaging
126 Custom transformers to apply to the notebook prior to engaging
127 the Jinja template engine. Any transformers specified here
127 the Jinja template engine. Any transformers specified here
128 will override existing transformers if a naming conflict
128 will override existing transformers if a naming conflict
129 occurs.
129 occurs.
130 filters : dict[of filter]
130 filters : dict[of filter]
131 filters specified here will override existing filters if a naming
131 filters specified here will override existing filters if a naming
132 conflict occurs. Filters are availlable in jinja template through
132 conflict occurs. Filters are availlable in jinja template through
133 the name of the corresponding key. Cf class docstring for
133 the name of the corresponding key. Cf class docstring for
134 availlable default filters.
134 availlable default filters.
135 config : config
135 config : config
136 User configuration instance.
136 User configuration instance.
137 """
137 """
138
138
139 #Call the base class constructor
139 #Call the base class constructor
140 c = self.default_config
140 c = self.default_config
141 if config:
141 if config:
142 c.merge(config)
142 c.merge(config)
143
143
144 super(Exporter, self).__init__(config=c, **kw)
144 super(Exporter, self).__init__(config=c, **kw)
145
145
146 #Standard environment
146 #Standard environment
147 self._init_environment()
147 self._init_environment()
148
148
149 #Add transformers
149 #Add transformers
150 self._register_transformers()
150 self._register_transformers()
151
151
152 #Add filters to the Jinja2 environment
152 #Add filters to the Jinja2 environment
153 self._register_filters()
153 self._register_filters()
154
154
155 #Load user transformers. Overwrite existing transformers if need be.
155 #Load user transformers. Overwrite existing transformers if need be.
156 if transformers :
156 if transformers :
157 for transformer in transformers:
157 for transformer in transformers:
158 self.register_transformer(transformer)
158 self.register_transformer(transformer)
159
159
160 #Load user filters. Overwrite existing filters if need be.
160 #Load user filters. Overwrite existing filters if need be.
161 if not filters is None:
161 if not filters is None:
162 for key, user_filter in filters.iteritems():
162 for key, user_filter in filters.iteritems():
163 self.register_filter(key, user_filter)
163 self.register_filter(key, user_filter)
164
164
165 @property
165 @property
166 def default_config(self):
166 def default_config(self):
167 return Config()
167 return Config()
168
168
169
169
170
170
171 def from_notebook_node(self, nb, resources=None):
171 def from_notebook_node(self, nb, resources=None):
172 """
172 """
173 Convert a notebook from a notebook node instance.
173 Convert a notebook from a notebook node instance.
174
174
175 Parameters
175 Parameters
176 ----------
176 ----------
177 nb : Notebook node
177 nb : Notebook node
178 resources : a dict of additional resources that
178 resources : a dict of additional resources that
179 can be accessed read/write by transformers
179 can be accessed read/write by transformers
180 and filters.
180 and filters.
181 """
181 """
182 if resources is None:
182 if resources is None:
183 resources = {}
183 resources = {}
184 nb, resources = self._preprocess(nb, resources)
184 nb, resources = self._preprocess(nb, resources)
185
185
186 #Load the template file.
186 #Load the template file.
187 self.template = self.environment.get_template(self.template_file+self.template_extension)
187 self.template = self.environment.get_template(self.template_file+self.template_extension)
188
188
189 return self.template.render(nb=nb, resources=resources), resources
189 return self.template.render(nb=nb, resources=resources), resources
190
190
191
191
192 def from_filename(self, filename):
192 def from_filename(self, filename):
193 """
193 """
194 Convert a notebook from a notebook file.
194 Convert a notebook from a notebook file.
195
195
196 Parameters
196 Parameters
197 ----------
197 ----------
198 filename : str
198 filename : str
199 Full filename of the notebook file to open and convert.
199 Full filename of the notebook file to open and convert.
200 """
200 """
201
201
202 with io.open(filename) as f:
202 with io.open(filename) as f:
203 return self.from_notebook_node(nbformat.read(f, 'json'))
203 return self.from_notebook_node(nbformat.read(f, 'json'))
204
204
205
205
206 def from_file(self, file_stream):
206 def from_file(self, file_stream):
207 """
207 """
208 Convert a notebook from a notebook file.
208 Convert a notebook from a notebook file.
209
209
210 Parameters
210 Parameters
211 ----------
211 ----------
212 file_stream : file-like object
212 file_stream : file-like object
213 Notebook file-like object to convert.
213 Notebook file-like object to convert.
214 """
214 """
215 return self.from_notebook_node(nbformat.read(file_stream, 'json'))
215 return self.from_notebook_node(nbformat.read(file_stream, 'json'))
216
216
217
217
218 def register_transformer(self, transformer):
218 def register_transformer(self, transformer):
219 """
219 """
220 Register a transformer.
220 Register a transformer.
221 Transformers are classes that act upon the notebook before it is
221 Transformers are classes that act upon the notebook before it is
222 passed into the Jinja templating engine. Transformers are also
222 passed into the Jinja templating engine. Transformers are also
223 capable of passing additional information to the Jinja
223 capable of passing additional information to the Jinja
224 templating engine.
224 templating engine.
225
225
226 Parameters
226 Parameters
227 ----------
227 ----------
228 transformer : transformer
228 transformer : transformer
229 """
229 """
230 if self.transformers is None:
230 if self.transformers is None:
231 self.transformers = []
231 self.transformers = []
232
232
233 if inspect.isfunction(transformer):
233 if inspect.isfunction(transformer):
234 self.transformers.append(transformer)
234 self.transformers.append(transformer)
235 return transformer
235 return transformer
236 elif isinstance(transformer, MetaHasTraits):
236 elif isinstance(transformer, MetaHasTraits):
237 transformer_instance = transformer(config=self.config)
237 transformer_instance = transformer(config=self.config)
238 self.transformers.append(transformer_instance)
238 self.transformers.append(transformer_instance)
239 return transformer_instance
239 return transformer_instance
240 else:
240 else:
241 transformer_instance = transformer()
241 transformer_instance = transformer()
242 self.transformers.append(transformer_instance)
242 self.transformers.append(transformer_instance)
243 return transformer_instance
243 return transformer_instance
244
244
245
245
246 def register_filter(self, name, filter):
246 def register_filter(self, name, filter):
247 """
247 """
248 Register a filter.
248 Register a filter.
249 A filter is a function that accepts and acts on one string.
249 A filter is a function that accepts and acts on one string.
250 The filters are accesible within the Jinja templating engine.
250 The filters are accesible within the Jinja templating engine.
251
251
252 Parameters
252 Parameters
253 ----------
253 ----------
254 name : str
254 name : str
255 name to give the filter in the Jinja engine
255 name to give the filter in the Jinja engine
256 filter : filter
256 filter : filter
257 """
257 """
258 if inspect.isfunction(filter):
258 if inspect.isfunction(filter):
259 self.environment.filters[name] = filter
259 self.environment.filters[name] = filter
260 elif isinstance(filter, MetaHasTraits):
260 elif isinstance(filter, MetaHasTraits):
261 self.environment.filters[name] = filter(config=self.config)
261 self.environment.filters[name] = filter(config=self.config)
262 else:
262 else:
263 self.environment.filters[name] = filter()
263 self.environment.filters[name] = filter()
264 return self.environment.filters[name]
264 return self.environment.filters[name]
265
265
266
266
267 def _register_transformers(self):
267 def _register_transformers(self):
268 """
268 """
269 Register all of the transformers needed for this exporter.
269 Register all of the transformers needed for this exporter.
270 """
270 """
271
271
272 self.register_transformer(transformers.coalesce_streams)
272 self.register_transformer(transformers.coalesce_streams)
273
273
274 #Remember the figure extraction transformer so it can be enabled and
274 #Remember the figure extraction transformer so it can be enabled and
275 #disabled easily later.
275 #disabled easily later.
276 self.extract_figure_transformer = self.register_transformer(transformers.ExtractFigureTransformer)
276 self.extract_figure_transformer = self.register_transformer(transformers.ExtractFigureTransformer)
277
277
278
278
279 def _register_filters(self):
279 def _register_filters(self):
280 """
280 """
281 Register all of the filters required for the exporter.
281 Register all of the filters required for the exporter.
282 """
282 """
283 for k, v in default_filters.iteritems():
283 for k, v in default_filters.iteritems():
284 self.register_filter(k, v)
284 self.register_filter(k, v)
285
285
286
286
287 def _init_environment(self):
287 def _init_environment(self):
288 """
288 """
289 Create the Jinja templating environment.
289 Create the Jinja templating environment.
290 """
290 """
291
291 here = os.path.realpath(__file__)
292 self.environment = Environment(
292 self.environment = Environment(
293 loader=FileSystemLoader([
293 loader=FileSystemLoader([
294 os.path.dirname(os.path.realpath(__file__)) + self.template_path,
294 os.path.join(here, self.template_path),
295 os.path.dirname(os.path.realpath(__file__)) + self.template_skeleton_path,
295 os.path.join(here, self.template_skeleton_path),
296 ]),
296 ]),
297 extensions=JINJA_EXTENSIONS
297 extensions=JINJA_EXTENSIONS
298 )
298 )
299
299
300 #Set special Jinja2 syntax that will not conflict with latex.
300 #Set special Jinja2 syntax that will not conflict with latex.
301 if self.jinja_logic_block_start:
301 if self.jinja_logic_block_start:
302 self.environment.block_start_string = self.jinja_logic_block_start
302 self.environment.block_start_string = self.jinja_logic_block_start
303 if self.jinja_logic_block_end:
303 if self.jinja_logic_block_end:
304 self.environment.block_end_string = self.jinja_logic_block_end
304 self.environment.block_end_string = self.jinja_logic_block_end
305 if self.jinja_variable_block_start:
305 if self.jinja_variable_block_start:
306 self.environment.variable_start_string = self.jinja_variable_block_start
306 self.environment.variable_start_string = self.jinja_variable_block_start
307 if self.jinja_variable_block_end:
307 if self.jinja_variable_block_end:
308 self.environment.variable_end_string = self.jinja_variable_block_end
308 self.environment.variable_end_string = self.jinja_variable_block_end
309 if self.jinja_comment_block_start:
309 if self.jinja_comment_block_start:
310 self.environment.comment_start_string = self.jinja_comment_block_start
310 self.environment.comment_start_string = self.jinja_comment_block_start
311 if self.jinja_comment_block_end:
311 if self.jinja_comment_block_end:
312 self.environment.comment_end_string = self.jinja_comment_block_end
312 self.environment.comment_end_string = self.jinja_comment_block_end
313
313
314
314
315 def _preprocess(self, nb, resources):
315 def _preprocess(self, nb, resources):
316 """
316 """
317 Preprocess the notebook before passing it into the Jinja engine.
317 Preprocess the notebook before passing it into the Jinja engine.
318 To preprocess the notebook is to apply all of the
318 To preprocess the notebook is to apply all of the
319
319
320 Parameters
320 Parameters
321 ----------
321 ----------
322 nb : notebook node
322 nb : notebook node
323 notebook that is being exported.
323 notebook that is being exported.
324 resources : a dict of additional resources that
324 resources : a dict of additional resources that
325 can be accessed read/write by transformers
325 can be accessed read/write by transformers
326 and filters.
326 and filters.
327 """
327 """
328
328
329 # Do a deepcopy first,
329 # Do a deepcopy first,
330 # we are never safe enough with what the transformers could do.
330 # we are never safe enough with what the transformers could do.
331 nbc = deepcopy(nb)
331 nbc = deepcopy(nb)
332 resc = deepcopy(resources)
332 resc = deepcopy(resources)
333 #Run each transformer on the notebook. Carry the output along
333 #Run each transformer on the notebook. Carry the output along
334 #to each transformer
334 #to each transformer
335 for transformer in self.transformers:
335 for transformer in self.transformers:
336 nb, resources = transformer(nbc, resc)
336 nb, resources = transformer(nbc, resc)
337 return nb, resources
337 return nb, resources
338
338
General Comments 0
You need to be logged in to leave comments. Login now