##// END OF EJS Templates
Merge pull request #3734 from jdfreder/file_subdir...
Matthias Bussonnier -
r11642:321025c8 merge
parent child Browse files
Show More
@@ -1,443 +1,443 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 import copy
23 import copy
24 import collections
24 import collections
25 import datetime
25 import datetime
26
26
27 # other libs/dependencies
27 # other libs/dependencies
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
29
29
30 # IPython imports
30 # IPython imports
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
32 from IPython.config import Config
32 from IPython.config import Config
33 from IPython.nbformat import current as nbformat
33 from IPython.nbformat import current as nbformat
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
35 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent
36 from IPython.utils.text import indent
37 from IPython.utils import py3compat
37 from IPython.utils import py3compat
38
38
39 from IPython.nbconvert import transformers as nbtransformers
39 from IPython.nbconvert import transformers as nbtransformers
40 from IPython.nbconvert import filters
40 from IPython.nbconvert import filters
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals and constants
43 # Globals and constants
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #Jinja2 extensions to load.
46 #Jinja2 extensions to load.
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48
48
49 default_filters = {
49 default_filters = {
50 'indent': indent,
50 'indent': indent,
51 'markdown': filters.markdown2html,
51 'markdown': filters.markdown2html,
52 'ansi2html': filters.ansi2html,
52 'ansi2html': filters.ansi2html,
53 'filter_data_type': filters.DataTypeFilter,
53 'filter_data_type': filters.DataTypeFilter,
54 'get_lines': filters.get_lines,
54 'get_lines': filters.get_lines,
55 'highlight': filters.highlight,
55 'highlight': filters.highlight,
56 'highlight2html': filters.highlight,
56 'highlight2html': filters.highlight,
57 'highlight2latex': filters.highlight2latex,
57 'highlight2latex': filters.highlight2latex,
58 'markdown2latex': filters.markdown2latex,
58 'markdown2latex': filters.markdown2latex,
59 'markdown2rst': filters.markdown2rst,
59 'markdown2rst': filters.markdown2rst,
60 'pycomment': filters.python_comment,
60 'pycomment': filters.python_comment,
61 'rm_ansi': filters.remove_ansi,
61 'rm_ansi': filters.remove_ansi,
62 'rm_dollars': filters.strip_dollars,
62 'rm_dollars': filters.strip_dollars,
63 'rm_fake': filters.rm_fake,
63 'rm_fake': filters.rm_fake,
64 'html_text' : filters.html_text,
64 'html_text' : filters.html_text,
65 'add_anchor': filters.add_anchor,
65 'add_anchor': filters.add_anchor,
66 'ansi2latex': filters.ansi2latex,
66 'ansi2latex': filters.ansi2latex,
67 'rm_math_space': filters.rm_math_space,
67 'rm_math_space': filters.rm_math_space,
68 'wrap': filters.wrap
68 'wrap': filters.wrap
69 }
69 }
70
70
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72 # Class
72 # Class
73 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
74
74
75 class ResourcesDict(collections.defaultdict):
75 class ResourcesDict(collections.defaultdict):
76 def __missing__(self, key):
76 def __missing__(self, key):
77 return ''
77 return ''
78
78
79
79
80 class Exporter(Configurable):
80 class Exporter(Configurable):
81 """
81 """
82 Exports notebooks into other file formats. Uses Jinja 2 templating engine
82 Exports notebooks into other file formats. Uses Jinja 2 templating engine
83 to output new formats. Inherit from this class if you are creating a new
83 to output new formats. Inherit from this class if you are creating a new
84 template type along with new filters/transformers. If the filters/
84 template type along with new filters/transformers. If the filters/
85 transformers provided by default suffice, there is no need to inherit from
85 transformers provided by default suffice, there is no need to inherit from
86 this class. Instead, override the template_file and file_extension
86 this class. Instead, override the template_file and file_extension
87 traits via a config file.
87 traits via a config file.
88
88
89 {filters}
89 {filters}
90 """
90 """
91
91
92 # finish the docstring
92 # finish the docstring
93 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
93 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
94
94
95
95
96 template_file = Unicode(
96 template_file = Unicode(
97 '', config=True,
97 '', config=True,
98 help="Name of the template file to use")
98 help="Name of the template file to use")
99
99
100 file_extension = Unicode(
100 file_extension = Unicode(
101 'txt', config=True,
101 'txt', config=True,
102 help="Extension of the file that should be written to disk"
102 help="Extension of the file that should be written to disk"
103 )
103 )
104
104
105 template_path = Unicode(
105 template_path = Unicode(
106 os.path.join("..", "templates"), config=True,
106 os.path.join("..", "templates"), config=True,
107 help="Path where the template files are located.")
107 help="Path where the template files are located.")
108
108
109 template_skeleton_path = Unicode(
109 template_skeleton_path = Unicode(
110 os.path.join("..", "templates", "skeleton"), config=True,
110 os.path.join("..", "templates", "skeleton"), config=True,
111 help="Path where the template skeleton files are located.")
111 help="Path where the template skeleton files are located.")
112
112
113 #Jinja block definitions
113 #Jinja block definitions
114 jinja_comment_block_start = Unicode("", config=True)
114 jinja_comment_block_start = Unicode("", config=True)
115 jinja_comment_block_end = Unicode("", config=True)
115 jinja_comment_block_end = Unicode("", config=True)
116 jinja_variable_block_start = Unicode("", config=True)
116 jinja_variable_block_start = Unicode("", config=True)
117 jinja_variable_block_end = Unicode("", config=True)
117 jinja_variable_block_end = Unicode("", config=True)
118 jinja_logic_block_start = Unicode("", config=True)
118 jinja_logic_block_start = Unicode("", config=True)
119 jinja_logic_block_end = Unicode("", config=True)
119 jinja_logic_block_end = Unicode("", config=True)
120
120
121 #Extension that the template files use.
121 #Extension that the template files use.
122 template_extension = Unicode(".tpl", config=True)
122 template_extension = Unicode(".tpl", config=True)
123
123
124 #Configurability, allows the user to easily add filters and transformers.
124 #Configurability, allows the user to easily add filters and transformers.
125 transformers = List(config=True,
125 transformers = List(config=True,
126 help="""List of transformers, by name or namespace, to enable.""")
126 help="""List of transformers, by name or namespace, to enable.""")
127
127
128 filters = Dict(config=True,
128 filters = Dict(config=True,
129 help="""Dictionary of filters, by name and namespace, to add to the Jinja
129 help="""Dictionary of filters, by name and namespace, to add to the Jinja
130 environment.""")
130 environment.""")
131
131
132 default_transformers = List([nbtransformers.coalesce_streams,
132 default_transformers = List([nbtransformers.coalesce_streams,
133 nbtransformers.SVG2PDFTransformer,
133 nbtransformers.SVG2PDFTransformer,
134 nbtransformers.ExtractFigureTransformer,
134 nbtransformers.ExtractOutputTransformer,
135 nbtransformers.CSSHTMLHeaderTransformer,
135 nbtransformers.CSSHTMLHeaderTransformer,
136 nbtransformers.RevealHelpTransformer,
136 nbtransformers.RevealHelpTransformer,
137 nbtransformers.LatexTransformer,
137 nbtransformers.LatexTransformer,
138 nbtransformers.SphinxTransformer],
138 nbtransformers.SphinxTransformer],
139 config=True,
139 config=True,
140 help="""List of transformers available by default, by name, namespace,
140 help="""List of transformers available by default, by name, namespace,
141 instance, or type.""")
141 instance, or type.""")
142
142
143
143
144 def __init__(self, config=None, extra_loaders=None, **kw):
144 def __init__(self, config=None, extra_loaders=None, **kw):
145 """
145 """
146 Public constructor
146 Public constructor
147
147
148 Parameters
148 Parameters
149 ----------
149 ----------
150 config : config
150 config : config
151 User configuration instance.
151 User configuration instance.
152 extra_loaders : list[of Jinja Loaders]
152 extra_loaders : list[of Jinja Loaders]
153 ordered list of Jinja loder to find templates. Will be tried in order
153 ordered list of Jinja loder to find templates. Will be tried in order
154 before the default FileSysteme ones.
154 before the default FileSysteme ones.
155 """
155 """
156
156
157 #Call the base class constructor
157 #Call the base class constructor
158 c = self.default_config
158 c = self.default_config
159 if config:
159 if config:
160 c.merge(config)
160 c.merge(config)
161
161
162 super(Exporter, self).__init__(config=c, **kw)
162 super(Exporter, self).__init__(config=c, **kw)
163
163
164 #Init
164 #Init
165 self._init_environment(extra_loaders=extra_loaders)
165 self._init_environment(extra_loaders=extra_loaders)
166 self._init_transformers()
166 self._init_transformers()
167 self._init_filters()
167 self._init_filters()
168
168
169
169
170 @property
170 @property
171 def default_config(self):
171 def default_config(self):
172 return Config()
172 return Config()
173
173
174
174
175 def from_notebook_node(self, nb, resources=None, **kw):
175 def from_notebook_node(self, nb, resources=None, **kw):
176 """
176 """
177 Convert a notebook from a notebook node instance.
177 Convert a notebook from a notebook node instance.
178
178
179 Parameters
179 Parameters
180 ----------
180 ----------
181 nb : Notebook node
181 nb : Notebook node
182 resources : dict (**kw)
182 resources : dict (**kw)
183 of additional resources that can be accessed read/write by
183 of additional resources that can be accessed read/write by
184 transformers and filters.
184 transformers and filters.
185 """
185 """
186 nb_copy = copy.deepcopy(nb)
186 nb_copy = copy.deepcopy(nb)
187 resources = self._init_resources(resources)
187 resources = self._init_resources(resources)
188
188
189 #Preprocess
189 #Preprocess
190 nb_copy, resources = self._transform(nb_copy, resources)
190 nb_copy, resources = self._transform(nb_copy, resources)
191
191
192 #Convert
192 #Convert
193 self.template = self.environment.get_template(self.template_file + self.template_extension)
193 self.template = self.environment.get_template(self.template_file + self.template_extension)
194 output = self.template.render(nb=nb_copy, resources=resources)
194 output = self.template.render(nb=nb_copy, resources=resources)
195 return output, resources
195 return output, resources
196
196
197
197
198 def from_filename(self, filename, resources=None, **kw):
198 def from_filename(self, filename, resources=None, **kw):
199 """
199 """
200 Convert a notebook from a notebook file.
200 Convert a notebook from a notebook file.
201
201
202 Parameters
202 Parameters
203 ----------
203 ----------
204 filename : str
204 filename : str
205 Full filename of the notebook file to open and convert.
205 Full filename of the notebook file to open and convert.
206 """
206 """
207
207
208 #Pull the metadata from the filesystem.
208 #Pull the metadata from the filesystem.
209 if resources is None:
209 if resources is None:
210 resources = ResourcesDict()
210 resources = ResourcesDict()
211 if not 'metadata' in resources or resources['metadata'] == '':
211 if not 'metadata' in resources or resources['metadata'] == '':
212 resources['metadata'] = ResourcesDict()
212 resources['metadata'] = ResourcesDict()
213 basename = os.path.basename(filename)
213 basename = os.path.basename(filename)
214 notebook_name = basename[:basename.rfind('.')]
214 notebook_name = basename[:basename.rfind('.')]
215 resources['metadata']['name'] = notebook_name
215 resources['metadata']['name'] = notebook_name
216
216
217 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
217 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
218 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
218 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
219
219
220 with io.open(filename) as f:
220 with io.open(filename) as f:
221 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
221 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
222
222
223
223
224 def from_file(self, file_stream, resources=None, **kw):
224 def from_file(self, file_stream, resources=None, **kw):
225 """
225 """
226 Convert a notebook from a notebook file.
226 Convert a notebook from a notebook file.
227
227
228 Parameters
228 Parameters
229 ----------
229 ----------
230 file_stream : file-like object
230 file_stream : file-like object
231 Notebook file-like object to convert.
231 Notebook file-like object to convert.
232 """
232 """
233 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
233 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
234
234
235
235
236 def register_transformer(self, transformer, enabled=False):
236 def register_transformer(self, transformer, enabled=False):
237 """
237 """
238 Register a transformer.
238 Register a transformer.
239 Transformers are classes that act upon the notebook before it is
239 Transformers are classes that act upon the notebook before it is
240 passed into the Jinja templating engine. Transformers are also
240 passed into the Jinja templating engine. Transformers are also
241 capable of passing additional information to the Jinja
241 capable of passing additional information to the Jinja
242 templating engine.
242 templating engine.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 transformer : transformer
246 transformer : transformer
247 """
247 """
248 if transformer is None:
248 if transformer is None:
249 raise TypeError('transformer')
249 raise TypeError('transformer')
250 isclass = isinstance(transformer, type)
250 isclass = isinstance(transformer, type)
251 constructed = not isclass
251 constructed = not isclass
252
252
253 #Handle transformer's registration based on it's type
253 #Handle transformer's registration based on it's type
254 if constructed and isinstance(transformer, py3compat.string_types):
254 if constructed and isinstance(transformer, py3compat.string_types):
255 #Transformer is a string, import the namespace and recursively call
255 #Transformer is a string, import the namespace and recursively call
256 #this register_transformer method
256 #this register_transformer method
257 transformer_cls = import_item(transformer)
257 transformer_cls = import_item(transformer)
258 return self.register_transformer(transformer_cls, enabled)
258 return self.register_transformer(transformer_cls, enabled)
259
259
260 if constructed and hasattr(transformer, '__call__'):
260 if constructed and hasattr(transformer, '__call__'):
261 #Transformer is a function, no need to construct it.
261 #Transformer is a function, no need to construct it.
262 #Register and return the transformer.
262 #Register and return the transformer.
263 if enabled:
263 if enabled:
264 transformer.enabled = True
264 transformer.enabled = True
265 self._transformers.append(transformer)
265 self._transformers.append(transformer)
266 return transformer
266 return transformer
267
267
268 elif isclass and isinstance(transformer, MetaHasTraits):
268 elif isclass and isinstance(transformer, MetaHasTraits):
269 #Transformer is configurable. Make sure to pass in new default for
269 #Transformer is configurable. Make sure to pass in new default for
270 #the enabled flag if one was specified.
270 #the enabled flag if one was specified.
271 self.register_transformer(transformer(parent=self), enabled)
271 self.register_transformer(transformer(parent=self), enabled)
272
272
273 elif isclass:
273 elif isclass:
274 #Transformer is not configurable, construct it
274 #Transformer is not configurable, construct it
275 self.register_transformer(transformer(), enabled)
275 self.register_transformer(transformer(), enabled)
276
276
277 else:
277 else:
278 #Transformer is an instance of something without a __call__
278 #Transformer is an instance of something without a __call__
279 #attribute.
279 #attribute.
280 raise TypeError('transformer')
280 raise TypeError('transformer')
281
281
282
282
283 def register_filter(self, name, jinja_filter):
283 def register_filter(self, name, jinja_filter):
284 """
284 """
285 Register a filter.
285 Register a filter.
286 A filter is a function that accepts and acts on one string.
286 A filter is a function that accepts and acts on one string.
287 The filters are accesible within the Jinja templating engine.
287 The filters are accesible within the Jinja templating engine.
288
288
289 Parameters
289 Parameters
290 ----------
290 ----------
291 name : str
291 name : str
292 name to give the filter in the Jinja engine
292 name to give the filter in the Jinja engine
293 filter : filter
293 filter : filter
294 """
294 """
295 if jinja_filter is None:
295 if jinja_filter is None:
296 raise TypeError('filter')
296 raise TypeError('filter')
297 isclass = isinstance(jinja_filter, type)
297 isclass = isinstance(jinja_filter, type)
298 constructed = not isclass
298 constructed = not isclass
299
299
300 #Handle filter's registration based on it's type
300 #Handle filter's registration based on it's type
301 if constructed and isinstance(jinja_filter, py3compat.string_types):
301 if constructed and isinstance(jinja_filter, py3compat.string_types):
302 #filter is a string, import the namespace and recursively call
302 #filter is a string, import the namespace and recursively call
303 #this register_filter method
303 #this register_filter method
304 filter_cls = import_item(jinja_filter)
304 filter_cls = import_item(jinja_filter)
305 return self.register_filter(name, filter_cls)
305 return self.register_filter(name, filter_cls)
306
306
307 if constructed and hasattr(jinja_filter, '__call__'):
307 if constructed and hasattr(jinja_filter, '__call__'):
308 #filter is a function, no need to construct it.
308 #filter is a function, no need to construct it.
309 self.environment.filters[name] = jinja_filter
309 self.environment.filters[name] = jinja_filter
310 return jinja_filter
310 return jinja_filter
311
311
312 elif isclass and isinstance(jinja_filter, MetaHasTraits):
312 elif isclass and isinstance(jinja_filter, MetaHasTraits):
313 #filter is configurable. Make sure to pass in new default for
313 #filter is configurable. Make sure to pass in new default for
314 #the enabled flag if one was specified.
314 #the enabled flag if one was specified.
315 filter_instance = jinja_filter(parent=self)
315 filter_instance = jinja_filter(parent=self)
316 self.register_filter(name, filter_instance )
316 self.register_filter(name, filter_instance )
317
317
318 elif isclass:
318 elif isclass:
319 #filter is not configurable, construct it
319 #filter is not configurable, construct it
320 filter_instance = jinja_filter()
320 filter_instance = jinja_filter()
321 self.register_filter(name, filter_instance)
321 self.register_filter(name, filter_instance)
322
322
323 else:
323 else:
324 #filter is an instance of something without a __call__
324 #filter is an instance of something without a __call__
325 #attribute.
325 #attribute.
326 raise TypeError('filter')
326 raise TypeError('filter')
327
327
328
328
329 def _init_environment(self, extra_loaders=None):
329 def _init_environment(self, extra_loaders=None):
330 """
330 """
331 Create the Jinja templating environment.
331 Create the Jinja templating environment.
332 """
332 """
333 here = os.path.dirname(os.path.realpath(__file__))
333 here = os.path.dirname(os.path.realpath(__file__))
334 loaders = []
334 loaders = []
335 if extra_loaders:
335 if extra_loaders:
336 loaders.extend(extra_loaders)
336 loaders.extend(extra_loaders)
337
337
338 loaders.append(FileSystemLoader([
338 loaders.append(FileSystemLoader([
339 os.path.join(here, self.template_path),
339 os.path.join(here, self.template_path),
340 os.path.join(here, self.template_skeleton_path),
340 os.path.join(here, self.template_skeleton_path),
341 ]))
341 ]))
342
342
343 self.environment = Environment(
343 self.environment = Environment(
344 loader= ChoiceLoader(loaders),
344 loader= ChoiceLoader(loaders),
345 extensions=JINJA_EXTENSIONS
345 extensions=JINJA_EXTENSIONS
346 )
346 )
347
347
348 #Set special Jinja2 syntax that will not conflict with latex.
348 #Set special Jinja2 syntax that will not conflict with latex.
349 if self.jinja_logic_block_start:
349 if self.jinja_logic_block_start:
350 self.environment.block_start_string = self.jinja_logic_block_start
350 self.environment.block_start_string = self.jinja_logic_block_start
351 if self.jinja_logic_block_end:
351 if self.jinja_logic_block_end:
352 self.environment.block_end_string = self.jinja_logic_block_end
352 self.environment.block_end_string = self.jinja_logic_block_end
353 if self.jinja_variable_block_start:
353 if self.jinja_variable_block_start:
354 self.environment.variable_start_string = self.jinja_variable_block_start
354 self.environment.variable_start_string = self.jinja_variable_block_start
355 if self.jinja_variable_block_end:
355 if self.jinja_variable_block_end:
356 self.environment.variable_end_string = self.jinja_variable_block_end
356 self.environment.variable_end_string = self.jinja_variable_block_end
357 if self.jinja_comment_block_start:
357 if self.jinja_comment_block_start:
358 self.environment.comment_start_string = self.jinja_comment_block_start
358 self.environment.comment_start_string = self.jinja_comment_block_start
359 if self.jinja_comment_block_end:
359 if self.jinja_comment_block_end:
360 self.environment.comment_end_string = self.jinja_comment_block_end
360 self.environment.comment_end_string = self.jinja_comment_block_end
361
361
362
362
363 def _init_transformers(self):
363 def _init_transformers(self):
364 """
364 """
365 Register all of the transformers needed for this exporter, disabled
365 Register all of the transformers needed for this exporter, disabled
366 unless specified explicitly.
366 unless specified explicitly.
367 """
367 """
368 self._transformers = []
368 self._transformers = []
369
369
370 #Load default transformers (not necessarly enabled by default).
370 #Load default transformers (not necessarly enabled by default).
371 if self.default_transformers:
371 if self.default_transformers:
372 for transformer in self.default_transformers:
372 for transformer in self.default_transformers:
373 self.register_transformer(transformer)
373 self.register_transformer(transformer)
374
374
375 #Load user transformers. Enable by default.
375 #Load user transformers. Enable by default.
376 if self.transformers:
376 if self.transformers:
377 for transformer in self.transformers:
377 for transformer in self.transformers:
378 self.register_transformer(transformer, enabled=True)
378 self.register_transformer(transformer, enabled=True)
379
379
380
380
381 def _init_filters(self):
381 def _init_filters(self):
382 """
382 """
383 Register all of the filters required for the exporter.
383 Register all of the filters required for the exporter.
384 """
384 """
385
385
386 #Add default filters to the Jinja2 environment
386 #Add default filters to the Jinja2 environment
387 for key, value in default_filters.items():
387 for key, value in default_filters.items():
388 self.register_filter(key, value)
388 self.register_filter(key, value)
389
389
390 #Load user filters. Overwrite existing filters if need be.
390 #Load user filters. Overwrite existing filters if need be.
391 if self.filters:
391 if self.filters:
392 for key, user_filter in self.filters.items():
392 for key, user_filter in self.filters.items():
393 self.register_filter(key, user_filter)
393 self.register_filter(key, user_filter)
394
394
395
395
396 def _init_resources(self, resources):
396 def _init_resources(self, resources):
397
397
398 #Make sure the resources dict is of ResourcesDict type.
398 #Make sure the resources dict is of ResourcesDict type.
399 if resources is None:
399 if resources is None:
400 resources = ResourcesDict()
400 resources = ResourcesDict()
401 if not isinstance(resources, ResourcesDict):
401 if not isinstance(resources, ResourcesDict):
402 new_resources = ResourcesDict()
402 new_resources = ResourcesDict()
403 new_resources.update(resources)
403 new_resources.update(resources)
404 resources = new_resources
404 resources = new_resources
405
405
406 #Make sure the metadata extension exists in resources
406 #Make sure the metadata extension exists in resources
407 if 'metadata' in resources:
407 if 'metadata' in resources:
408 if not isinstance(resources['metadata'], ResourcesDict):
408 if not isinstance(resources['metadata'], ResourcesDict):
409 resources['metadata'] = ResourcesDict(resources['metadata'])
409 resources['metadata'] = ResourcesDict(resources['metadata'])
410 else:
410 else:
411 resources['metadata'] = ResourcesDict()
411 resources['metadata'] = ResourcesDict()
412 if not resources['metadata']['name']:
412 if not resources['metadata']['name']:
413 resources['metadata']['name'] = 'Notebook'
413 resources['metadata']['name'] = 'Notebook'
414
414
415 #Set the output extension
415 #Set the output extension
416 resources['output_extension'] = self.file_extension
416 resources['output_extension'] = self.file_extension
417 return resources
417 return resources
418
418
419
419
420 def _transform(self, nb, resources):
420 def _transform(self, nb, resources):
421 """
421 """
422 Preprocess the notebook before passing it into the Jinja engine.
422 Preprocess the notebook before passing it into the Jinja engine.
423 To preprocess the notebook is to apply all of the
423 To preprocess the notebook is to apply all of the
424
424
425 Parameters
425 Parameters
426 ----------
426 ----------
427 nb : notebook node
427 nb : notebook node
428 notebook that is being exported.
428 notebook that is being exported.
429 resources : a dict of additional resources that
429 resources : a dict of additional resources that
430 can be accessed read/write by transformers
430 can be accessed read/write by transformers
431 and filters.
431 and filters.
432 """
432 """
433
433
434 # Do a copy.deepcopy first,
434 # Do a copy.deepcopy first,
435 # we are never safe enough with what the transformers could do.
435 # we are never safe enough with what the transformers could do.
436 nbc = copy.deepcopy(nb)
436 nbc = copy.deepcopy(nb)
437 resc = copy.deepcopy(resources)
437 resc = copy.deepcopy(resources)
438
438
439 #Run each transformer on the notebook. Carry the output along
439 #Run each transformer on the notebook. Carry the output along
440 #to each transformer
440 #to each transformer
441 for transformer in self._transformers:
441 for transformer in self._transformers:
442 nbc, resc = transformer(nbc, resc)
442 nbc, resc = transformer(nbc, resc)
443 return nbc, resc
443 return nbc, resc
@@ -1,102 +1,102 b''
1 """
1 """
2 Exporter that allows Latex Jinja templates to work. Contains logic to
2 Exporter that allows Latex Jinja templates to work. Contains logic to
3 appropriately prepare IPYNB files for export to LaTeX. Including but
3 appropriately prepare IPYNB files for export to LaTeX. Including but
4 not limited to escaping LaTeX, fixing math region tags, using special
4 not limited to escaping LaTeX, fixing math region tags, using special
5 tags to circumvent Jinja/Latex syntax conflicts.
5 tags to circumvent Jinja/Latex syntax conflicts.
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2013, the IPython Development Team.
8 # Copyright (c) 2013, the IPython Development Team.
9 #
9 #
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11 #
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # Stdlib imports
19 # Stdlib imports
20 import os
20 import os
21
21
22 # IPython imports
22 # IPython imports
23 from IPython.utils.traitlets import Unicode, List
23 from IPython.utils.traitlets import Unicode, List
24 from IPython.config import Config
24 from IPython.config import Config
25
25
26 from IPython.nbconvert import filters, transformers
26 from IPython.nbconvert import filters, transformers
27 from .exporter import Exporter
27 from .exporter import Exporter
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 class LatexExporter(Exporter):
33 class LatexExporter(Exporter):
34 """
34 """
35 Exports to a Latex template. Inherit from this class if your template is
35 Exports to a Latex template. Inherit from this class if your template is
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
37 you are writing your own HTML template and need custom tranformers/filters.
37 you are writing your own HTML template and need custom tranformers/filters.
38 If you don't need custom tranformers/filters, just change the
38 If you don't need custom tranformers/filters, just change the
39 'template_file' config option. Place your template in the special "/latex"
39 'template_file' config option. Place your template in the special "/latex"
40 subfolder of the "../templates" folder.
40 subfolder of the "../templates" folder.
41 """
41 """
42
42
43 file_extension = Unicode(
43 file_extension = Unicode(
44 'tex', config=True,
44 'tex', config=True,
45 help="Extension of the file that should be written to disk")
45 help="Extension of the file that should be written to disk")
46
46
47 template_file = Unicode(
47 template_file = Unicode(
48 'base', config=True,
48 'base', config=True,
49 help="Name of the template file to use")
49 help="Name of the template file to use")
50
50
51 #Latex constants
51 #Latex constants
52 template_path = Unicode(
52 template_path = Unicode(
53 os.path.join("..", "templates", "latex"), config=True,
53 os.path.join("..", "templates", "latex"), config=True,
54 help="Path where the template files are located.")
54 help="Path where the template files are located.")
55
55
56 template_skeleton_path = Unicode(
56 template_skeleton_path = Unicode(
57 os.path.join("..", "templates", "latex", "skeleton"), config=True,
57 os.path.join("..", "templates", "latex", "skeleton"), config=True,
58 help="Path where the template skeleton files are located.")
58 help="Path where the template skeleton files are located.")
59
59
60 #Special Jinja2 syntax that will not conflict when exporting latex.
60 #Special Jinja2 syntax that will not conflict when exporting latex.
61 jinja_comment_block_start = Unicode("((=", config=True)
61 jinja_comment_block_start = Unicode("((=", config=True)
62 jinja_comment_block_end = Unicode("=))", config=True)
62 jinja_comment_block_end = Unicode("=))", config=True)
63 jinja_variable_block_start = Unicode("(((", config=True)
63 jinja_variable_block_start = Unicode("(((", config=True)
64 jinja_variable_block_end = Unicode(")))", config=True)
64 jinja_variable_block_end = Unicode(")))", config=True)
65 jinja_logic_block_start = Unicode("((*", config=True)
65 jinja_logic_block_start = Unicode("((*", config=True)
66 jinja_logic_block_end = Unicode("*))", config=True)
66 jinja_logic_block_end = Unicode("*))", config=True)
67
67
68 #Extension that the template files use.
68 #Extension that the template files use.
69 template_extension = Unicode(".tplx", config=True)
69 template_extension = Unicode(".tplx", config=True)
70
70
71
71
72 def _init_filters(self):
72 def _init_filters(self):
73 """
73 """
74 Register all of the filters required for the exporter.
74 Register all of the filters required for the exporter.
75 """
75 """
76
76
77 #Register the filters of the base class.
77 #Register the filters of the base class.
78 super(LatexExporter, self)._init_filters()
78 super(LatexExporter, self)._init_filters()
79
79
80 #Add latex filters to the Jinja2 environment
80 #Add latex filters to the Jinja2 environment
81 self.register_filter('escape_tex', filters.escape_latex)
81 self.register_filter('escape_tex', filters.escape_latex)
82 self.register_filter('highlight', filters.highlight2latex)
82 self.register_filter('highlight', filters.highlight2latex)
83
83
84
84
85 @property
85 @property
86 def default_config(self):
86 def default_config(self):
87 c = Config({
87 c = Config({
88 'NbConvertBase': {
88 'NbConvertBase': {
89 'display_data_priority' : ['latex', 'pdf', 'png', 'jpg', 'svg', 'jpeg', 'text']
89 'display_data_priority' : ['latex', 'pdf', 'png', 'jpg', 'svg', 'jpeg', 'text']
90 },
90 },
91 'ExtractFigureTransformer': {
91 'ExtractOutputTransformer': {
92 'enabled':True
92 'enabled':True
93 },
93 },
94 'SVG2PDFTransformer': {
94 'SVG2PDFTransformer': {
95 'enabled':True
95 'enabled':True
96 },
96 },
97 'LatexTransformer': {
97 'LatexTransformer': {
98 'enabled':True
98 'enabled':True
99 }
99 }
100 })
100 })
101 c.merge(super(LatexExporter,self).default_config)
101 c.merge(super(LatexExporter,self).default_config)
102 return c
102 return c
@@ -1,42 +1,42 b''
1 """
1 """
2 Exporter for exporting notebooks to restructured text.
2 Exporter for exporting notebooks to restructured text.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from IPython.utils.traitlets import Unicode
16 from IPython.utils.traitlets import Unicode
17 from IPython.config import Config
17 from IPython.config import Config
18
18
19 from .exporter import Exporter
19 from .exporter import Exporter
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class RSTExporter(Exporter):
25 class RSTExporter(Exporter):
26 """
26 """
27 Exports restructured text documents.
27 Exports restructured text documents.
28 """
28 """
29
29
30 file_extension = Unicode(
30 file_extension = Unicode(
31 'rst', config=True,
31 'rst', config=True,
32 help="Extension of the file that should be written to disk")
32 help="Extension of the file that should be written to disk")
33
33
34 template_file = Unicode(
34 template_file = Unicode(
35 'rst', config=True,
35 'rst', config=True,
36 help="Name of the template file to use")
36 help="Name of the template file to use")
37
37
38 @property
38 @property
39 def default_config(self):
39 def default_config(self):
40 c = Config({'ExtractFigureTransformer':{'enabled':True}})
40 c = Config({'ExtractOutputTransformer':{'enabled':True}})
41 c.merge(super(RSTExporter,self).default_config)
41 c.merge(super(RSTExporter,self).default_config)
42 return c
42 return c
@@ -1,113 +1,113 b''
1 """
1 """
2 Module with tests for exporter.py
2 Module with tests for exporter.py
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 IPython.config import Config
17 from IPython.config import Config
18
18
19 from .base import ExportersTestsBase
19 from .base import ExportersTestsBase
20 from .cheese import CheeseTransformer
20 from .cheese import CheeseTransformer
21 from ..exporter import Exporter
21 from ..exporter import Exporter
22
22
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Class
25 # Class
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class TestExporter(ExportersTestsBase):
28 class TestExporter(ExportersTestsBase):
29 """Contains test functions for exporter.py"""
29 """Contains test functions for exporter.py"""
30
30
31
31
32 def test_constructor(self):
32 def test_constructor(self):
33 """
33 """
34 Can an Exporter be constructed?
34 Can an Exporter be constructed?
35 """
35 """
36 Exporter()
36 Exporter()
37
37
38
38
39 def test_export(self):
39 def test_export(self):
40 """
40 """
41 Can an Exporter export something?
41 Can an Exporter export something?
42 """
42 """
43 exporter = self._make_exporter()
43 exporter = self._make_exporter()
44 (output, resources) = exporter.from_filename(self._get_notebook())
44 (output, resources) = exporter.from_filename(self._get_notebook())
45 assert len(output) > 0
45 assert len(output) > 0
46
46
47
47
48 def test_extract_figures(self):
48 def test_extract_outputs(self):
49 """
49 """
50 If the ExtractFigureTransformer is enabled, are figures extracted?
50 If the ExtractOutputTransformer is enabled, are outputs extracted?
51 """
51 """
52 config = Config({'ExtractFigureTransformer': {'enabled': True}})
52 config = Config({'ExtractOutputTransformer': {'enabled': True}})
53 exporter = self._make_exporter(config=config)
53 exporter = self._make_exporter(config=config)
54 (output, resources) = exporter.from_filename(self._get_notebook())
54 (output, resources) = exporter.from_filename(self._get_notebook())
55 assert resources is not None
55 assert resources is not None
56 assert 'figures' in resources
56 assert 'outputs' in resources
57 assert len(resources['figures']) > 0
57 assert len(resources['outputs']) > 0
58
58
59
59
60 def test_transformer_class(self):
60 def test_transformer_class(self):
61 """
61 """
62 Can a transformer be added to the transformers list by class type?
62 Can a transformer be added to the transformers list by class type?
63 """
63 """
64 config = Config({'Exporter': {'transformers': [CheeseTransformer]}})
64 config = Config({'Exporter': {'transformers': [CheeseTransformer]}})
65 exporter = self._make_exporter(config=config)
65 exporter = self._make_exporter(config=config)
66 (output, resources) = exporter.from_filename(self._get_notebook())
66 (output, resources) = exporter.from_filename(self._get_notebook())
67 assert resources is not None
67 assert resources is not None
68 assert 'cheese' in resources
68 assert 'cheese' in resources
69 assert resources['cheese'] == 'real'
69 assert resources['cheese'] == 'real'
70
70
71
71
72 def test_transformer_instance(self):
72 def test_transformer_instance(self):
73 """
73 """
74 Can a transformer be added to the transformers list by instance?
74 Can a transformer be added to the transformers list by instance?
75 """
75 """
76 config = Config({'Exporter': {'transformers': [CheeseTransformer()]}})
76 config = Config({'Exporter': {'transformers': [CheeseTransformer()]}})
77 exporter = self._make_exporter(config=config)
77 exporter = self._make_exporter(config=config)
78 (output, resources) = exporter.from_filename(self._get_notebook())
78 (output, resources) = exporter.from_filename(self._get_notebook())
79 assert resources is not None
79 assert resources is not None
80 assert 'cheese' in resources
80 assert 'cheese' in resources
81 assert resources['cheese'] == 'real'
81 assert resources['cheese'] == 'real'
82
82
83
83
84 def test_transformer_dottedobjectname(self):
84 def test_transformer_dottedobjectname(self):
85 """
85 """
86 Can a transformer be added to the transformers list by dotted object name?
86 Can a transformer be added to the transformers list by dotted object name?
87 """
87 """
88 config = Config({'Exporter': {'transformers': ['IPython.nbconvert.exporters.tests.cheese.CheeseTransformer']}})
88 config = Config({'Exporter': {'transformers': ['IPython.nbconvert.exporters.tests.cheese.CheeseTransformer']}})
89 exporter = self._make_exporter(config=config)
89 exporter = self._make_exporter(config=config)
90 (output, resources) = exporter.from_filename(self._get_notebook())
90 (output, resources) = exporter.from_filename(self._get_notebook())
91 assert resources is not None
91 assert resources is not None
92 assert 'cheese' in resources
92 assert 'cheese' in resources
93 assert resources['cheese'] == 'real'
93 assert resources['cheese'] == 'real'
94
94
95
95
96 def test_transformer_via_method(self):
96 def test_transformer_via_method(self):
97 """
97 """
98 Can a transformer be added via the Exporter convinience method?
98 Can a transformer be added via the Exporter convinience method?
99 """
99 """
100 exporter = self._make_exporter()
100 exporter = self._make_exporter()
101 exporter.register_transformer(CheeseTransformer, enabled=True)
101 exporter.register_transformer(CheeseTransformer, enabled=True)
102 (output, resources) = exporter.from_filename(self._get_notebook())
102 (output, resources) = exporter.from_filename(self._get_notebook())
103 assert resources is not None
103 assert resources is not None
104 assert 'cheese' in resources
104 assert 'cheese' in resources
105 assert resources['cheese'] == 'real'
105 assert resources['cheese'] == 'real'
106
106
107
107
108 def _make_exporter(self, config=None):
108 def _make_exporter(self, config=None):
109 #Create the exporter instance, make sure to set a template name since
109 #Create the exporter instance, make sure to set a template name since
110 #the base Exporter doesn't have a template associated with it.
110 #the base Exporter doesn't have a template associated with it.
111 exporter = Exporter(config=config)
111 exporter = Exporter(config=config)
112 exporter.template_file = 'python'
112 exporter.template_file = 'python'
113 return exporter No newline at end of file
113 return exporter
@@ -1,229 +1,230 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """NBConvert is a utility for conversion of .ipynb files.
2 """NBConvert is a utility for conversion of .ipynb files.
3
3
4 Command-line interface for the NbConvert conversion utility.
4 Command-line interface for the NbConvert conversion utility.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 #Copyright (c) 2013, the IPython Development Team.
7 #Copyright (c) 2013, the IPython Development Team.
8 #
8 #
9 #Distributed under the terms of the Modified BSD License.
9 #Distributed under the terms of the Modified BSD License.
10 #
10 #
11 #The full license is in the file COPYING.txt, distributed with this software.
11 #The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 #Imports
15 #Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #Stdlib imports
18 # Stdlib imports
19 from __future__ import print_function
19 from __future__ import print_function
20 import sys
20 import sys
21 import os
21 import os
22 import glob
22 import glob
23
23
24 #From IPython
24 # From IPython
25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
26 from IPython.config import catch_config_error, Configurable
26 from IPython.config import catch_config_error, Configurable
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 )
29 )
30 from IPython.utils.importstring import import_item
30 from IPython.utils.importstring import import_item
31
31
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 from IPython.nbconvert import exporters, transformers, writers
33 from IPython.nbconvert import exporters, transformers, writers
34 from .utils.base import NbConvertBase
34 from .utils.base import NbConvertBase
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 #Classes and functions
37 #Classes and functions
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 nbconvert_aliases = {}
40 nbconvert_aliases = {}
41 nbconvert_aliases.update(base_aliases)
41 nbconvert_aliases.update(base_aliases)
42 nbconvert_aliases.update({
42 nbconvert_aliases.update({
43 'format' : 'NbConvertApp.export_format',
43 'format' : 'NbConvertApp.export_format',
44 'notebooks' : 'NbConvertApp.notebooks',
44 'notebooks' : 'NbConvertApp.notebooks',
45 'writer' : 'NbConvertApp.writer_class',
45 'writer' : 'NbConvertApp.writer_class',
46 })
46 })
47
47
48 nbconvert_flags = {}
48 nbconvert_flags = {}
49 nbconvert_flags.update(base_flags)
49 nbconvert_flags.update(base_flags)
50 nbconvert_flags.update({
50 nbconvert_flags.update({
51 'stdout' : (
51 'stdout' : (
52 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
52 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
53 "Write notebook output to stdout instead of files."
53 "Write notebook output to stdout instead of files."
54 )
54 )
55 })
55 })
56
56
57
57
58 class NbConvertApp(BaseIPythonApplication):
58 class NbConvertApp(BaseIPythonApplication):
59 """Application used to convert to and from notebook file type (*.ipynb)"""
59 """Application used to convert to and from notebook file type (*.ipynb)"""
60
60
61 name = 'ipython-nbconvert'
61 name = 'ipython-nbconvert'
62 aliases = nbconvert_aliases
62 aliases = nbconvert_aliases
63 flags = nbconvert_flags
63 flags = nbconvert_flags
64
64
65 def _classes_default(self):
65 def _classes_default(self):
66 classes = [NbConvertBase]
66 classes = [NbConvertBase]
67 for pkg in (exporters, transformers, writers):
67 for pkg in (exporters, transformers, writers):
68 for name in dir(pkg):
68 for name in dir(pkg):
69 cls = getattr(pkg, name)
69 cls = getattr(pkg, name)
70 if isinstance(cls, type) and issubclass(cls, Configurable):
70 if isinstance(cls, type) and issubclass(cls, Configurable):
71 classes.append(cls)
71 classes.append(cls)
72 return classes
72 return classes
73
73
74 description = Unicode(
74 description = Unicode(
75 u"""This application is used to convert notebook files (*.ipynb)
75 u"""This application is used to convert notebook files (*.ipynb)
76 to various other formats.""")
76 to various other formats.""")
77
77
78 examples = Unicode(u"""
78 examples = Unicode(u"""
79 The simplest way to use nbconvert is
79 The simplest way to use nbconvert is
80
80
81 > ipython nbconvert mynotebook.ipynb
81 > ipython nbconvert mynotebook.ipynb
82
82
83 which will convert mynotebook.ipynb to the default format (probably HTML).
83 which will convert mynotebook.ipynb to the default format (probably HTML).
84
84
85 You can specify the export format with `--format`.
85 You can specify the export format with `--format`.
86 Options include {0}
86 Options include {0}
87
87
88 > ipython nbconvert --format latex mynotebook.ipnynb
88 > ipython nbconvert --format latex mynotebook.ipnynb
89
89
90 You can also pipe the output to stdout, rather than a file
90 You can also pipe the output to stdout, rather than a file
91
91
92 > ipython nbconvert mynotebook.ipynb --stdout
92 > ipython nbconvert mynotebook.ipynb --stdout
93
93
94 Multiple notebooks can be given at the command line in a couple of
94 Multiple notebooks can be given at the command line in a couple of
95 different ways:
95 different ways:
96
96
97 > ipython nbconvert notebook*.ipynb
97 > ipython nbconvert notebook*.ipynb
98 > ipython nbconvert notebook1.ipynb notebook2.ipynb
98 > ipython nbconvert notebook1.ipynb notebook2.ipynb
99
99
100 or you can specify the notebooks list in a config file, containing::
100 or you can specify the notebooks list in a config file, containing::
101
101
102 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
102 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
103
103
104 > ipython nbconvert --config mycfg.py
104 > ipython nbconvert --config mycfg.py
105 """.format(get_export_names()))
105 """.format(get_export_names()))
106 #Writer specific variables
106 # Writer specific variables
107 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
107 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
108 help="""Instance of the writer class used to write the
108 help="""Instance of the writer class used to write the
109 results of the conversion.""")
109 results of the conversion.""")
110 writer_class = DottedObjectName('FilesWriter', config=True,
110 writer_class = DottedObjectName('FilesWriter', config=True,
111 help="""Writer class used to write the
111 help="""Writer class used to write the
112 results of the conversion""")
112 results of the conversion""")
113 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
113 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
114 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
114 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
115 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
115 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
116 writer_factory = Type()
116 writer_factory = Type()
117
117
118 def _writer_class_changed(self, name, old, new):
118 def _writer_class_changed(self, name, old, new):
119 if new in self.writer_aliases:
119 if new in self.writer_aliases:
120 new = self.writer_aliases[new]
120 new = self.writer_aliases[new]
121 self.writer_factory = import_item(new)
121 self.writer_factory = import_item(new)
122
122
123
123
124 #Other configurable variables
124 # Other configurable variables
125 export_format = CaselessStrEnum(get_export_names(),
125 export_format = CaselessStrEnum(get_export_names(),
126 default_value="full_html",
126 default_value="full_html",
127 config=True,
127 config=True,
128 help="""The export format to be used."""
128 help="""The export format to be used."""
129 )
129 )
130
130
131 notebooks = List([], config=True, help="""List of notebooks to convert.
131 notebooks = List([], config=True, help="""List of notebooks to convert.
132 Wildcards are supported.
132 Wildcards are supported.
133 Filenames passed positionally will be added to the list.
133 Filenames passed positionally will be added to the list.
134 """)
134 """)
135
135
136 @catch_config_error
136 @catch_config_error
137 def initialize(self, argv=None):
137 def initialize(self, argv=None):
138 super(NbConvertApp, self).initialize(argv)
138 super(NbConvertApp, self).initialize(argv)
139 self.init_notebooks()
139 self.init_notebooks()
140 self.init_writer()
140 self.init_writer()
141
141
142 def init_notebooks(self):
142 def init_notebooks(self):
143 """Construct the list of notebooks.
143 """Construct the list of notebooks.
144 If notebooks are passed on the command-line,
144 If notebooks are passed on the command-line,
145 they override notebooks specified in config files.
145 they override notebooks specified in config files.
146 Glob each notebook to replace notebook patterns with filenames.
146 Glob each notebook to replace notebook patterns with filenames.
147 """
147 """
148
148
149 # Specifying notebooks on the command-line overrides (rather than adds)
149 # Specifying notebooks on the command-line overrides (rather than adds)
150 # the notebook list
150 # the notebook list
151 if self.extra_args:
151 if self.extra_args:
152 patterns = self.extra_args
152 patterns = self.extra_args
153 else:
153 else:
154 patterns = self.notebooks
154 patterns = self.notebooks
155
155
156 #Use glob to replace all the notebook patterns with filenames.
156 # Use glob to replace all the notebook patterns with filenames.
157 filenames = []
157 filenames = []
158 for pattern in patterns:
158 for pattern in patterns:
159 for filename in glob.glob(pattern):
159 for filename in glob.glob(pattern):
160 if not filename in filenames:
160 if not filename in filenames:
161 filenames.append(filename)
161 filenames.append(filename)
162 self.notebooks = filenames
162 self.notebooks = filenames
163
163
164 def init_writer(self):
164 def init_writer(self):
165 """
165 """
166 Initialize the writer (which is stateless)
166 Initialize the writer (which is stateless)
167 """
167 """
168 self._writer_class_changed(None, self.writer_class, self.writer_class)
168 self._writer_class_changed(None, self.writer_class, self.writer_class)
169 self.writer = self.writer_factory(parent=self)
169 self.writer = self.writer_factory(parent=self)
170
170
171 def start(self):
171 def start(self):
172 """
172 """
173 Ran after initialization completed
173 Ran after initialization completed
174 """
174 """
175 super(NbConvertApp, self).start()
175 super(NbConvertApp, self).start()
176 self.convert_notebooks()
176 self.convert_notebooks()
177
177
178 def convert_notebooks(self):
178 def convert_notebooks(self):
179 """
179 """
180 Convert the notebooks in the self.notebook traitlet
180 Convert the notebooks in the self.notebook traitlet
181 """
181 """
182 #Export each notebook
182 # Export each notebook
183 conversion_success = 0
183 conversion_success = 0
184 for notebook_filename in self.notebooks:
184 for notebook_filename in self.notebooks:
185
185
186 #Get a unique key for the notebook and set it in the resources object.
186 # Get a unique key for the notebook and set it in the resources object.
187 basename = os.path.basename(notebook_filename)
187 basename = os.path.basename(notebook_filename)
188 notebook_name = basename[:basename.rfind('.')]
188 notebook_name = basename[:basename.rfind('.')]
189 resources = {}
189 resources = {}
190 resources['unique_key'] = notebook_name
190 resources['unique_key'] = notebook_name
191 resources['output_files_dir'] = '%s_files' % notebook_name
191
192
192 #Try to export
193 # Try to export
193 try:
194 try:
194 output, resources = export_by_name(self.export_format,
195 output, resources = export_by_name(self.export_format,
195 notebook_filename,
196 notebook_filename,
196 resources=resources,
197 resources=resources,
197 config=self.config)
198 config=self.config)
198 except ExporterNameError as e:
199 except ExporterNameError as e:
199 print("Error: '%s' exporter not found." % self.export_format,
200 print("Error: '%s' exporter not found." % self.export_format,
200 file=sys.stderr)
201 file=sys.stderr)
201 print("Known exporters are:",
202 print("Known exporters are:",
202 "\n\t" + "\n\t".join(get_export_names()),
203 "\n\t" + "\n\t".join(get_export_names()),
203 file=sys.stderr)
204 file=sys.stderr)
204 sys.exit(-1)
205 sys.exit(-1)
205 #except Exception as e:
206 # except Exception as e:
206 #print("Error: could not export '%s'" % notebook_filename, file=sys.stderr)
207 # print("Error: could not export '%s'" % notebook_filename, file=sys.stderr)
207 #print(e, file=sys.stderr)
208 # print(e, file=sys.stderr)
208 else:
209 else:
209 self.writer.write(output, resources, notebook_name=notebook_name)
210 self.writer.write(output, resources, notebook_name=notebook_name)
210 conversion_success += 1
211 conversion_success += 1
211
212
212 #If nothing was converted successfully, help the user.
213 # If nothing was converted successfully, help the user.
213 if conversion_success == 0:
214 if conversion_success == 0:
214
215
215 #No notebooks were specified, show help.
216 # No notebooks were specified, show help.
216 if len(self.notebooks) == 0:
217 if len(self.notebooks) == 0:
217 self.print_help()
218 self.print_help()
218
219
219 #Notebooks were specified, but not converted successfully. Show how
220 # Notebooks were specified, but not converted successfully. Show how
220 #to access help.
221 # to access help.
221 else:
222 else:
222 print('For help, use "ipython nbconvert --help"')
223 print('For help, use "ipython nbconvert --help"')
223
224
224
225
225 #-----------------------------------------------------------------------------
226 #-----------------------------------------------------------------------------
226 # Main entry point
227 # Main entry point
227 #-----------------------------------------------------------------------------
228 #-----------------------------------------------------------------------------
228
229
229 launch_new_instance = NbConvertApp.launch_instance
230 launch_new_instance = NbConvertApp.launch_instance
@@ -1,123 +1,123 b''
1 """
1 """
2 Contains tests for the nbconvertapp
2 Contains tests for the nbconvertapp
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
5 #Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 #Distributed under the terms of the Modified BSD License.
7 #Distributed under the terms of the Modified BSD License.
8 #
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import os
16 import os
17 from .base import TestsBase
17 from .base import TestsBase
18
18
19 from IPython.utils import py3compat
19 from IPython.utils import py3compat
20
20
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Constants
23 # Constants
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #Define ipython commandline name
26 # Define ipython commandline name
27 if py3compat.PY3:
27 if py3compat.PY3:
28 IPYTHON = 'ipython3'
28 IPYTHON = 'ipython3'
29 else:
29 else:
30 IPYTHON = 'ipython'
30 IPYTHON = 'ipython'
31
31
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Classes and functions
34 # Classes and functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 class TestNbConvertApp(TestsBase):
37 class TestNbConvertApp(TestsBase):
38 """Collection of NbConvertApp tests"""
38 """Collection of NbConvertApp tests"""
39
39
40
40
41 def test_notebook_help(self):
41 def test_notebook_help(self):
42 """
42 """
43 Will help show if no notebooks are specified?
43 Will help show if no notebooks are specified?
44 """
44 """
45 with self.create_temp_cwd():
45 with self.create_temp_cwd():
46 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
46 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
47
47
48
48
49 def test_glob(self):
49 def test_glob(self):
50 """
50 """
51 Do search patterns work for notebook names?
51 Do search patterns work for notebook names?
52 """
52 """
53 with self.create_temp_cwd(['notebook*.ipynb']):
53 with self.create_temp_cwd(['notebook*.ipynb']):
54 assert not 'error' in self.call([IPYTHON, 'nbconvert',
54 assert not 'error' in self.call([IPYTHON, 'nbconvert',
55 '--format="python"', '--notebooks=["*.ipynb"]']).lower()
55 '--format="python"', '--notebooks=["*.ipynb"]']).lower()
56 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
56 assert os.path.isfile('notebook1.py')
57 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
57 assert os.path.isfile('notebook2.py')
58
58
59
59
60 def test_glob_subdir(self):
60 def test_glob_subdir(self):
61 """
61 """
62 Do search patterns work for subdirectory notebook names?
62 Do search patterns work for subdirectory notebook names?
63 """
63 """
64 with self.create_temp_cwd() as cwd:
64 with self.create_temp_cwd() as cwd:
65 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
65 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
66 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
66 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
67 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
67 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
68 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
68 assert os.path.isfile('notebook1.py')
69 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
69 assert os.path.isfile('notebook2.py')
70
70
71
71
72 def test_explicit(self):
72 def test_explicit(self):
73 """
73 """
74 Do explicit notebook names work?
74 Do explicit notebook names work?
75 """
75 """
76 with self.create_temp_cwd(['notebook*.ipynb']):
76 with self.create_temp_cwd(['notebook*.ipynb']):
77 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
77 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
78 '--notebooks=["notebook2.ipynb"]']).lower()
78 '--notebooks=["notebook2.ipynb"]']).lower()
79 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
79 assert not os.path.isfile('notebook1.py')
80 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
80 assert os.path.isfile('notebook2.py')
81
81
82
82
83 def test_glob_explicit(self):
83 def test_glob_explicit(self):
84 """
84 """
85 Can a search pattern be used along with matching explicit notebook names?
85 Can a search pattern be used along with matching explicit notebook names?
86 """
86 """
87 with self.create_temp_cwd(['notebook*.ipynb']):
87 with self.create_temp_cwd(['notebook*.ipynb']):
88 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
88 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
89 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
89 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
90 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
90 assert os.path.isfile('notebook1.py')
91 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
91 assert os.path.isfile('notebook2.py')
92
92
93
93
94 def test_explicit_glob(self):
94 def test_explicit_glob(self):
95 """
95 """
96 Can explicit notebook names be used and then a matching search pattern?
96 Can explicit notebook names be used and then a matching search pattern?
97 """
97 """
98 with self.create_temp_cwd(['notebook*.ipynb']):
98 with self.create_temp_cwd(['notebook*.ipynb']):
99 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
99 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
100 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
100 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
101 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
101 assert os.path.isfile('notebook1.py')
102 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
102 assert os.path.isfile('notebook2.py')
103
103
104
104
105 def test_default_config(self):
105 def test_default_config(self):
106 """
106 """
107 Does the default config work?
107 Does the default config work?
108 """
108 """
109 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
109 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
110 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
110 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
111 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
111 assert os.path.isfile('notebook1.py')
112 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
112 assert not os.path.isfile('notebook2.py')
113
113
114
114
115 def test_override_config(self):
115 def test_override_config(self):
116 """
116 """
117 Can the default config be overriden?
117 Can the default config be overriden?
118 """
118 """
119 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
119 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
120 'override.py']):
120 'override.py']):
121 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
121 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
122 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
122 assert not os.path.isfile('notebook1.py')
123 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
123 assert os.path.isfile('notebook2.py')
@@ -1,12 +1,12 b''
1 # Class base Transformers
1 # Class base Transformers
2 from .base import Transformer
2 from .base import Transformer
3 from .convertfigures import ConvertFiguresTransformer
3 from .convertfigures import ConvertFiguresTransformer
4 from .svg2pdf import SVG2PDFTransformer
4 from .svg2pdf import SVG2PDFTransformer
5 from .extractfigure import ExtractFigureTransformer
5 from .extractoutput import ExtractOutputTransformer
6 from .revealhelp import RevealHelpTransformer
6 from .revealhelp import RevealHelpTransformer
7 from .latex import LatexTransformer
7 from .latex import LatexTransformer
8 from .sphinx import SphinxTransformer
8 from .sphinx import SphinxTransformer
9 from .csshtmlheader import CSSHTMLHeaderTransformer
9 from .csshtmlheader import CSSHTMLHeaderTransformer
10
10
11 # decorated function Transformers
11 # decorated function Transformers
12 from .coalescestreams import coalesce_streams
12 from .coalescestreams import coalesce_streams
@@ -1,64 +1,64 b''
1 """Module containing a transformer that converts outputs in the notebook from
1 """Module containing a transformer that converts outputs in the notebook from
2 one format to another.
2 one format to another.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from .base import Transformer
16 from .base import Transformer
17 from IPython.utils.traitlets import Unicode
17 from IPython.utils.traitlets import Unicode
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 class ConvertFiguresTransformer(Transformer):
23 class ConvertFiguresTransformer(Transformer):
24 """
24 """
25 Converts all of the outputs in a notebook from one format to another.
25 Converts all of the outputs in a notebook from one format to another.
26 """
26 """
27
27
28 from_format = Unicode(config=True, help='Format the converter accepts')
28 from_format = Unicode(config=True, help='Format the converter accepts')
29 to_format = Unicode(config=True, help='Format the converter writes')
29 to_format = Unicode(config=True, help='Format the converter writes')
30
30
31 def __init__(self, **kw):
31 def __init__(self, **kw):
32 """
32 """
33 Public constructor
33 Public constructor
34 """
34 """
35 super(ConvertFiguresTransformer, self).__init__(**kw)
35 super(ConvertFiguresTransformer, self).__init__(**kw)
36
36
37
37
38 def convert_figure(self, data_format, data):
38 def convert_figure(self, data_format, data):
39 raise NotImplementedError()
39 raise NotImplementedError()
40
40
41
41
42 def transform_cell(self, cell, resources, cell_index):
42 def transform_cell(self, cell, resources, cell_index):
43 """
43 """
44 Apply a transformation on each cell,
44 Apply a transformation on each cell,
45
45
46 See base.py
46 See base.py
47 """
47 """
48
48
49 #Loop through all of the datatypes of the outputs in the cell.
49 # Loop through all of the datatypes of the outputs in the cell.
50 for index, cell_out in enumerate(cell.get('outputs', [])):
50 for index, cell_out in enumerate(cell.get('outputs', [])):
51 for data_type, data in cell_out.items():
51 for data_type, data in cell_out.items():
52 # this must run *before* extract figures,
52 # this must run *before* extract outputs,
53 # so figure_name and filename do not exist
53 # so figure_name and filename do not exist
54 self._convert_figure(cell_out, resources, data_type, data)
54 self._convert_figure(cell_out, resources, data_type, data)
55 return cell, resources
55 return cell, resources
56
56
57
57
58 def _convert_figure(self, cell_out, resources, data_type, data):
58 def _convert_figure(self, cell_out, resources, data_type, data):
59 """
59 """
60 Convert a figure and output the results to the cell output
60 Convert a figure and output the results to the cell output
61 """
61 """
62 if not self.to_format in cell_out and data_type == self.from_format:
62 if not self.to_format in cell_out and data_type == self.from_format:
63 data = self.convert_figure(data_type, data)
63 data = self.convert_figure(data_type, data)
64 cell_out[self.to_format] = data
64 cell_out[self.to_format] = data
@@ -1,96 +1,101 b''
1 """Module containing a transformer that extracts all of the figures from the
1 """Module containing a transformer that extracts all of the outputs from the
2 notebook file. The extracted figures are returned in the 'resources' dictionary.
2 notebook file. The extracted outputs are returned in the 'resources' dictionary.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import base64
16 import base64
17 import sys
17 import sys
18 import os
18
19
19 from IPython.utils.traitlets import Unicode
20 from IPython.utils.traitlets import Unicode
20 from .base import Transformer
21 from .base import Transformer
21 from IPython.utils import py3compat
22 from IPython.utils import py3compat
22
23
23 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
24 # Classes
25 # Classes
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
26
27
27 class ExtractFigureTransformer(Transformer):
28 class ExtractOutputTransformer(Transformer):
28 """
29 """
29 Extracts all of the figures from the notebook file. The extracted
30 Extracts all of the outputs from the notebook file. The extracted
30 figures are returned in the 'resources' dictionary.
31 outputs are returned in the 'resources' dictionary.
31 """
32 """
32
33
33 figure_filename_template = Unicode(
34 figure_filename_template = Unicode(
34 "{unique_key}_{cell_index}_{index}.{extension}", config=True)
35 "{unique_key}_{cell_index}_{index}.{extension}", config=True)
35
36
36
37
37 def transform_cell(self, cell, resources, cell_index):
38 def transform_cell(self, cell, resources, cell_index):
38 """
39 """
39 Apply a transformation on each cell,
40 Apply a transformation on each cell,
40
41
41 Parameters
42 Parameters
42 ----------
43 ----------
43 cell : NotebookNode cell
44 cell : NotebookNode cell
44 Notebook cell being processed
45 Notebook cell being processed
45 resources : dictionary
46 resources : dictionary
46 Additional resources used in the conversion process. Allows
47 Additional resources used in the conversion process. Allows
47 transformers to pass variables into the Jinja engine.
48 transformers to pass variables into the Jinja engine.
48 cell_index : int
49 cell_index : int
49 Index of the cell being processed (see base.py)
50 Index of the cell being processed (see base.py)
50 """
51 """
51
52
52 #Get the unique key from the resource dict if it exists. If it does not
53 #Get the unique key from the resource dict if it exists. If it does not
53 #exist, use 'figure' as the default.
54 #exist, use 'figure' as the default. Also, get files directory if it
55 #has been specified
54 unique_key = resources.get('unique_key', 'figure')
56 unique_key = resources.get('unique_key', 'figure')
57 output_files_dir = resources.get('output_files_dir', None)
55
58
56 #Make sure figures key exists
59 #Make sure outputs key exists
57 if not 'figures' in resources:
60 if not 'outputs' in resources:
58 resources['figures'] = {}
61 resources['outputs'] = {}
59
62
60 #Loop through all of the outputs in the cell
63 #Loop through all of the outputs in the cell
61 for index, out in enumerate(cell.get('outputs', [])):
64 for index, out in enumerate(cell.get('outputs', [])):
62
65
63 #Get the output in data formats that the template is interested in.
66 #Get the output in data formats that the template is interested in.
64 for out_type in self.display_data_priority:
67 for out_type in self.display_data_priority:
65 if out.hasattr(out_type):
68 if out.hasattr(out_type):
66 data = out[out_type]
69 data = out[out_type]
67
70
68 #Binary files are base64-encoded, SVG is already XML
71 #Binary files are base64-encoded, SVG is already XML
69 if out_type in ('png', 'jpg', 'jpeg', 'pdf'):
72 if out_type in ('png', 'jpg', 'jpeg', 'pdf'):
70 # data is b64-encoded as text (str, unicode)
73 # data is b64-encoded as text (str, unicode)
71 # decodestring only accepts bytes
74 # decodestring only accepts bytes
72 data = py3compat.cast_bytes(data)
75 data = py3compat.cast_bytes(data)
73 data = base64.decodestring(data)
76 data = base64.decodestring(data)
74 elif sys.platform == 'win32':
77 elif sys.platform == 'win32':
75 data = data.replace('\n', '\r\n').encode("UTF-8")
78 data = data.replace('\n', '\r\n').encode("UTF-8")
76 else:
79 else:
77 data = data.encode("UTF-8")
80 data = data.encode("UTF-8")
78
81
79 #Build a figure name
82 #Build a figure name
80 figure_name = self.figure_filename_template.format(
83 filename = self.figure_filename_template.format(
81 unique_key=unique_key,
84 unique_key=unique_key,
82 cell_index=cell_index,
85 cell_index=cell_index,
83 index=index,
86 index=index,
84 extension=out_type)
87 extension=out_type)
85
88
86 #On the cell, make the figure available via
89 #On the cell, make the figure available via
87 # cell.outputs[i].svg_filename ... etc (svg in example)
90 # cell.outputs[i].svg_filename ... etc (svg in example)
88 # Where
91 # Where
89 # cell.outputs[i].svg contains the data
92 # cell.outputs[i].svg contains the data
90 out[out_type + '_filename'] = figure_name
93 if output_files_dir is not None:
94 filename = os.path.join(output_files_dir, filename)
95 out[out_type + '_filename'] = filename
91
96
92 #In the resources, make the figure available via
97 #In the resources, make the figure available via
93 # resources['figures']['filename'] = data
98 # resources['outputs']['filename'] = data
94 resources['figures'][figure_name] = data
99 resources['outputs'][filename] = data
95
100
96 return cell, resources
101 return cell, resources
@@ -1,43 +1,43 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """
2 """
3 Contains debug writer.
3 Contains debug writer.
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 .base import WriterBase
17 from .base import WriterBase
18 from pprint import pprint
18 from pprint import pprint
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 class DebugWriter(WriterBase):
24 class DebugWriter(WriterBase):
25 """Consumes output from nbconvert export...() methods and writes usefull
25 """Consumes output from nbconvert export...() methods and writes usefull
26 debugging information to the stdout. The information includes a list of
26 debugging information to the stdout. The information includes a list of
27 resources that were extracted from the notebook(s) during export."""
27 resources that were extracted from the notebook(s) during export."""
28
28
29
29
30 def write(self, output, resources, notebook_name='notebook', **kw):
30 def write(self, output, resources, notebook_name='notebook', **kw):
31 """
31 """
32 Consume and write Jinja output.
32 Consume and write Jinja output.
33
33
34 See base for more...
34 See base for more...
35 """
35 """
36
36
37 if 'figures' in resources:
37 if 'outputs' in resources:
38 print("Figures extracted from %s" % notebook_name)
38 print("outputs extracted from %s" % notebook_name)
39 print('-' * 80)
39 print('-' * 80)
40 pprint.pprint(resources['figures'], indent=2, width=70)
40 pprint.pprint(resources['outputs'], indent=2, width=70)
41 else:
41 else:
42 print("No figures extracted from %s" % notebook_name)
42 print("No outputs extracted from %s" % notebook_name)
43 print('=' * 80)
43 print('=' * 80)
@@ -1,98 +1,102 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """
2 """
3 Contains writer for writing nbconvert output to filesystem.
3 Contains writer for writing nbconvert output to filesystem.
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 import io
17 import io
18 import os
18 import os
19 import shutil
19 import shutil
20 import glob
20 import glob
21
21
22 from IPython.utils.traitlets import Unicode
22 from IPython.utils.traitlets import Unicode
23
23
24 from .base import WriterBase
24 from .base import WriterBase
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 class FilesWriter(WriterBase):
30 class FilesWriter(WriterBase):
31 """Consumes nbconvert output and produces files."""
31 """Consumes nbconvert output and produces files."""
32
32
33
33
34 build_directory = Unicode("nbconvert_build", config=True,
34 build_directory = Unicode(".", config=True,
35 help="""Directory to write output to. Leave blank
35 help="""Directory to write output to. Leave blank
36 to output to the current directory""")
36 to output to the current directory""")
37
37
38
38
39 #Make sure that the output directory exists.
39 # Make sure that the output directory exists.
40 def _build_directory_changed(self, name, old, new):
40 def _build_directory_changed(self, name, old, new):
41 if new and not os.path.isdir(new):
41 if new and not os.path.isdir(new):
42 os.makedirs(new)
42 os.makedirs(new)
43
43
44
44
45 def __init__(self, **kw):
45 def __init__(self, **kw):
46 super(FilesWriter, self).__init__(**kw)
46 super(FilesWriter, self).__init__(**kw)
47 self._build_directory_changed('build_directory', self.build_directory,
47 self._build_directory_changed('build_directory', self.build_directory,
48 self.build_directory)
48 self.build_directory)
49
49
50
50
51 def write(self, output, resources, notebook_name=None, **kw):
51 def write(self, output, resources, notebook_name=None, **kw):
52 """
52 """
53 Consume and write Jinja output to the file system. Output directory
53 Consume and write Jinja output to the file system. Output directory
54 is set via the 'build_directory' variable of this instance (a
54 is set via the 'build_directory' variable of this instance (a
55 configurable).
55 configurable).
56
56
57 See base for more...
57 See base for more...
58 """
58 """
59
59
60 #Pull the extension from the resources dict.
60 # Pull the extension and subdir from the resources dict.
61 output_extension = resources['output_extension']
61 output_extension = resources['output_extension']
62
62
63 #Write all of the extracted resources to the destination directory.
63 # Write all of the extracted resources to the destination directory.
64 #NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
64 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
65 #TRANSFORMER SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
65 # TRANSFORMER SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
66 for filename, data in resources.get('figures', {}).items():
66 for filename, data in resources.get('outputs', {}).items():
67
67
68 #Determine where to write the file to
68 # Determine where to write the file to
69 dest = os.path.join(self.build_directory, filename)
69 dest = os.path.join(self.build_directory, filename)
70 path = os.path.dirname(dest)
71 if not os.path.isdir(path):
72 os.makedirs(path)
70
73
71 #Write file
74 # Write file
72 with io.open(dest, 'wb') as f:
75 with io.open(dest, 'wb') as f:
73 f.write(data)
76 f.write(data)
74
77
75 #Copy referenced files to output directory
78 # Copy referenced files to output directory
76 if self.build_directory:
79 if self.build_directory:
77 for filename in self.files:
80 for filename in self.files:
78
81
79 #Copy files that match search pattern
82 # Copy files that match search pattern
80 for matching_filename in glob.glob(filename):
83 for matching_filename in glob.glob(filename):
81
84
82 #Make sure folder exists.
85 # Make sure folder exists.
83 dest = os.path.join(self.build_directory, filename)
86 dest = os.path.join(self.build_directory, filename)
84 path = os.path.dirname(dest)
87 path = os.path.dirname(dest)
85 if not os.path.isdir(path):
88 if not os.path.isdir(path):
86 os.makedirs(path)
89 os.makedirs(path)
87
90
88 #Copy
91 # Copy if destination is different.
89 shutil.copyfile(matching_filename, dest)
92 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
93 shutil.copyfile(matching_filename, dest)
90
94
91 #Determine where to write conversion results.
95 # Determine where to write conversion results.
92 dest = notebook_name + '.' + output_extension
96 dest = notebook_name + '.' + output_extension
93 if self.build_directory:
97 if self.build_directory:
94 dest = os.path.join(self.build_directory, dest)
98 dest = os.path.join(self.build_directory, dest)
95
99
96 #Write conversion results.
100 # Write conversion results.
97 with io.open(dest, 'w') as f:
101 with io.open(dest, 'w') as f:
98 f.write(output)
102 f.write(output)
@@ -1,37 +1,35 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """
2 """
3 Contains Stdout writer
3 Contains Stdout writer
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 .base import WriterBase
17 from .base import WriterBase
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 class StdoutWriter(WriterBase):
23 class StdoutWriter(WriterBase):
24 """Consumes output from nbconvert export...() methods and writes to the
24 """Consumes output from nbconvert export...() methods and writes to the
25 stdout stream. Allows for quick debuging of nbconvert output. Using the
25 stdout stream."""
26 debug flag makes the writer pretty-print the figures contained within the
27 notebook."""
28
26
29
27
30 def write(self, output, resources, **kw):
28 def write(self, output, resources, **kw):
31 """
29 """
32 Consume and write Jinja output.
30 Consume and write Jinja output.
33
31
34 See base for more...
32 See base for more...
35 """
33 """
36
34
37 print(output)
35 print(output)
General Comments 0
You need to be logged in to leave comments. Login now