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