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