##// END OF EJS Templates
Added writers and supporting code.
Jonathan Frederic -
Show More
@@ -0,0 +1,74 b''
1 """Module containing a transformer that converts outputs in the notebook from
2 one format to another.
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 from .activatable import ActivatableTransformer
17
18 #-----------------------------------------------------------------------------
19 # Classes
20 #-----------------------------------------------------------------------------
21
22 class ConvertFiguresTransformer(ActivatableTransformer):
23 """
24 Converts all of the outputs in a notebook from one format to another.
25 """
26
27
28 def __init__(self, from_formats, to_format, **kw):
29 """
30 Public constructor
31
32 Parameters
33 ----------
34 from_formats : list [of string]
35 Formats that the converter can convert from
36 to_format : string
37 Format that the converter converts to
38 config : Config
39 Configuration file structure
40 **kw : misc
41 Additional arguments
42 """
43 super(ConvertFiguresTransformer, self).__init__(**kw)
44
45 self._from_formats = from_formats
46 self._to_format = to_format
47
48
49 def convert_figure(self, data_format, data):
50 raise NotImplementedError()
51
52
53 def transform_cell(self, cell, resources, cell_index):
54 """
55 Apply a transformation on each cell,
56
57 See base.py
58 """
59
60 #Loop through all of the datatypes of the outputs in the cell.
61 for index, cell_out in enumerate(cell.get('outputs', [])):
62 for data_type, data in cell_out.items():
63 self._convert_figure(cell_out, data_type, data)
64 return cell, resources
65
66
67 def _convert_figure(self, cell_out, data_type, data):
68 """
69 Convert a figure and output the results to the cell output
70 """
71
72 if not self._to_format in cell_out:
73 if data_type in self._from_formats:
74 cell_out[self._to_format] = self.convert_figure(data_type, data)
@@ -0,0 +1,70 b''
1 """Module containing a transformer that converts outputs in the notebook from
2 one format to another.
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 import os
17 from IPython.utils.tempdir import TemporaryDirectory
18
19 from .convertfigures import ConvertFiguresTransformer
20
21
22 #-----------------------------------------------------------------------------
23 # Constants
24 #-----------------------------------------------------------------------------
25
26 INKSCAPE_COMMAND = "inkscape --without-gui --export-pdf=\"{to_filename}\" \"{from_filename}\""
27
28
29 #-----------------------------------------------------------------------------
30 # Classes
31 #-----------------------------------------------------------------------------
32
33 class ConvertSvgTransformer(ConvertFiguresTransformer):
34 """
35 Converts all of the outputs in a notebook from one format to another.
36 """
37
38
39 def __init__(self, **kw):
40 """
41 Constructor
42 """
43 super(ConvertSvgTransformer, self).__init__(['svg'], 'pdf', **kw)
44
45
46 def convert_figure(self, data_format, data):
47 """
48 Convert a single Svg figure. Returns converted data.
49 """
50
51 #Work in a temporary directory
52 with TemporaryDirectory() as tmpdir:
53
54 #Write fig to temp file
55 input_filename = os.path.join(tmpdir, 'figure.' + data_format)
56 with open(input_filename, 'w') as f:
57 f.write(data)
58
59 #Call conversion application
60 output_filename = os.path.join(tmpdir, 'figure.pdf')
61 shell = INKSCAPE_COMMAND.format(from_filename=input_filename,
62 to_filename=output_filename)
63 subprocess.call(shell, shell=True) #Shell=True okay since input is trusted.
64
65 #Read output from drive
66 if os.path.isfile(output_filename):
67 with open(output_filename) as f:
68 return f.read()
69 else:
70 return TypeError("Inkscape svg to png conversion failed")
@@ -3,3 +3,4 b''
3 from .exporters import *
3 from .exporters import *
4 import filters
4 import filters
5 import transformers
5 import transformers
6 import writers
@@ -16,6 +16,7 b' Module containing single call export functions.'
16 from functools import wraps
16 from functools import wraps
17
17
18 from IPython.nbformat.v3.nbbase import NotebookNode
18 from IPython.nbformat.v3.nbbase import NotebookNode
19 from IPython.config import Config
19
20
20 from .exporter import Exporter
21 from .exporter import Exporter
21 from .basichtml import BasicHTMLExporter
22 from .basichtml import BasicHTMLExporter
@@ -37,17 +38,10 b' def DocDecorator(f):'
37 #Set docstring of function
38 #Set docstring of function
38 f.__doc__ = f.__doc__ + """
39 f.__doc__ = f.__doc__ + """
39 nb : Notebook node
40 nb : Notebook node
40 config : config
41 config : config (optional, keyword arg)
41 User configuration instance.
42 User configuration instance.
42 transformers : list[of transformer]
43 resources : dict (optional, keyword arg)
43 Custom transformers to apply to the notebook prior to engaging
44 Resources used in the conversion process.
44 the Jinja template engine. Any transformers specified here
45 will override existing transformers if a naming conflict
46 occurs.
47 filters : list[of filter]
48 Custom filters to make accessible to the Jinja templates. Any
49 filters specified here will override existing filters if a
50 naming conflict occurs.
51
45
52 Returns
46 Returns
53 ----------
47 ----------
@@ -89,7 +83,7 b' __all__ = ['
89 ]
83 ]
90
84
91 @DocDecorator
85 @DocDecorator
92 def export(exporter_type, nb, config=None, transformers=None, filters=None):
86 def export(exporter_type, nb, **kw):
93 """
87 """
94 Export a notebook object using specific exporter class.
88 Export a notebook object using specific exporter class.
95
89
@@ -106,111 +100,110 b' def export(exporter_type, nb, config=None, transformers=None, filters=None):'
106 raise TypeError("Exporter is None")
100 raise TypeError("Exporter is None")
107 elif not issubclass(exporter_type, Exporter):
101 elif not issubclass(exporter_type, Exporter):
108 raise TypeError("Exporter type does not inherit from Exporter (base)")
102 raise TypeError("Exporter type does not inherit from Exporter (base)")
109
110 if nb is None:
103 if nb is None:
111 raise TypeError("nb is None")
104 raise TypeError("nb is None")
112
105
113 #Create the exporter
106 #Create the exporter
114 exporter_instance = exporter_type(preprocessors=transformers,
107 exporter_instance = exporter_type(config=kw.get('config', Config()))
115 jinja_filters=filters, config=config)
116
108
117 #Try to convert the notebook using the appropriate conversion function.
109 #Try to convert the notebook using the appropriate conversion function.
110 resources = kw.get('resources', {})
118 if isinstance(nb, NotebookNode):
111 if isinstance(nb, NotebookNode):
119 output, resources = exporter_instance.from_notebook_node(nb)
112 output, resources = exporter_instance.from_notebook_node(nb, resources)
120 elif isinstance(nb, basestring):
113 elif isinstance(nb, basestring):
121 output, resources = exporter_instance.from_filename(nb)
114 output, resources = exporter_instance.from_filename(nb, resources)
122 else:
115 else:
123 output, resources = exporter_instance.from_file(nb)
116 output, resources = exporter_instance.from_file(nb, resources)
124 return output, resources, exporter_instance
117 return output, resources
125
118
126
119
127 @DocDecorator
120 @DocDecorator
128 def export_sphinx_manual(nb, config=None, transformers=None, filters=None):
121 def export_sphinx_manual(nb, **kw):
129 """
122 """
130 Export a notebook object to Sphinx Manual LaTeX
123 Export a notebook object to Sphinx Manual LaTeX
131 """
124 """
132 return export(SphinxManualExporter, nb, config, transformers, filters)
125 return export(SphinxManualExporter, nb, **kw)
133
126
134
127
135 @DocDecorator
128 @DocDecorator
136 def export_sphinx_howto(nb, config=None, transformers=None, filters=None):
129 def export_sphinx_howto(nb, **kw):
137 """
130 """
138 Export a notebook object to Sphinx HowTo LaTeX
131 Export a notebook object to Sphinx HowTo LaTeX
139 """
132 """
140 return export(SphinxHowtoExporter, nb, config, transformers, filters)
133 return export(SphinxHowtoExporter, nb, **kw)
141
134
142
135
143 @DocDecorator
136 @DocDecorator
144 def export_basic_html(nb, config=None, transformers=None, filters=None):
137 def export_basic_html(nb, **kw):
145 """
138 """
146 Export a notebook object to Basic HTML
139 Export a notebook object to Basic HTML
147 """
140 """
148 return export(BasicHTMLExporter, nb, config, transformers, filters)
141 return export(BasicHTMLExporter, nb, **kw)
149
142
150
143
151 @DocDecorator
144 @DocDecorator
152 def export_full_html(nb, config=None, transformers=None, filters=None):
145 def export_full_html(nb, **kw):
153 """
146 """
154 Export a notebook object to Full HTML
147 Export a notebook object to Full HTML
155 """
148 """
156 return export(FullHTMLExporter, nb, config, transformers, filters)
149 return export(FullHTMLExporter, nb, **kw)
157
150
158
151
159 @DocDecorator
152 @DocDecorator
160 def export_latex(nb, config=None, transformers=None, filters=None):
153 def export_latex(nb, **kw):
161 """
154 """
162 Export a notebook object to LaTeX
155 Export a notebook object to LaTeX
163 """
156 """
164 return export(LatexExporter, nb, config, transformers, filters)
157 return export(LatexExporter, nb, **kw)
165
158
166
159
167 @DocDecorator
160 @DocDecorator
168 def export_markdown(nb, config=None, transformers=None, filters=None):
161 def export_markdown(nb, **kw):
169 """
162 """
170 Export a notebook object to Markdown
163 Export a notebook object to Markdown
171 """
164 """
172 return export(MarkdownExporter, nb, config, transformers, filters)
165 return export(MarkdownExporter, nb, **kw)
173
166
174
167
175 @DocDecorator
168 @DocDecorator
176 def export_python(nb, config=None, transformers=None, filters=None):
169 def export_python(nb, **kw):
177 """
170 """
178 Export a notebook object to Python
171 Export a notebook object to Python
179 """
172 """
180 return export(PythonExporter, nb, config, transformers, filters)
173 return export(PythonExporter, nb, **kw)
181
174
182
175
183 @DocDecorator
176 @DocDecorator
184 def export_reveal(nb, config=None, transformers=None, filters=None):
177 def export_reveal(nb, **kw):
185 """
178 """
186 Export a notebook object to Reveal
179 Export a notebook object to Reveal
187 """
180 """
188 return export(RevealExporter, nb, config, transformers, filters)
181 return export(RevealExporter, nb, **kw)
189
182
190
183
191 @DocDecorator
184 @DocDecorator
192 def export_rst(nb, config=None, transformers=None, filters=None):
185 def export_rst(nb, **kw):
193 """
186 """
194 Export a notebook object to RST
187 Export a notebook object to RST
195 """
188 """
196 return export(RstExporter, nb, config, transformers, filters)
189 return export(RstExporter, nb, **kw)
197
190
198
191
199 @DocDecorator
192 @DocDecorator
200 def export_by_name(template_name, nb, config=None, transformers=None, filters=None):
193 def export_by_name(format_name, nb, **kw):
201 """
194 """
202 Export a notebook object to a template type by its name. Reflection
195 Export a notebook object to a template type by its name. Reflection
203 (Inspect) is used to find the template's corresponding explicit export
196 (Inspect) is used to find the template's corresponding explicit export
204 method defined in this module. That method is then called directly.
197 method defined in this module. That method is then called directly.
205
198
206 template_name : str
199 format_name : str
207 Name of the template style to export to.
200 Name of the template style to export to.
208 """
201 """
209
202
210 function_name = "export_" + template_name.lower()
203 function_name = "export_" + format_name.lower()
211
204
212 if function_name in globals():
205 if function_name in globals():
213 return globals()[function_name](nb, config, transformers, filters)
206 return globals()[function_name](nb, **kw)
214 else:
207 else:
215 raise NameError("template for `%s` not found" % function_name)
208 raise NameError("template for `%s` not found" % function_name)
216
209
@@ -218,6 +211,7 b' def get_export_names():'
218 "Return a list of the currently supported export targets"
211 "Return a list of the currently supported export targets"
219 # grab everything after 'export_'
212 # grab everything after 'export_'
220 l = [x[len('export_'):] for x in __all__ if x.startswith('export_')]
213 l = [x[len('export_'):] for x in __all__ if x.startswith('export_')]
221 # filter out the one method that is not a template
214
215 # filter out the one method that is not a template
222 l = [x for x in l if 'by_name' not in x]
216 l = [x for x in l if 'by_name' not in x]
223 return sorted(l)
217 return sorted(l)
@@ -20,6 +20,7 b' from __future__ import print_function, absolute_import'
20 import io
20 import io
21 import os
21 import os
22 import inspect
22 import inspect
23 import types
23 from copy import deepcopy
24 from copy import deepcopy
24
25
25 # other libs/dependencies
26 # other libs/dependencies
@@ -29,7 +30,8 b' from jinja2 import Environment, FileSystemLoader, ChoiceLoader'
29 from IPython.config.configurable import Configurable
30 from IPython.config.configurable import Configurable
30 from IPython.config import Config
31 from IPython.config import Config
31 from IPython.nbformat import current as nbformat
32 from IPython.nbformat import current as nbformat
32 from IPython.utils.traitlets import MetaHasTraits, Unicode
33 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
34 from IPython.utils.importstring import import_item
33 from IPython.utils.text import indent
35 from IPython.utils.text import indent
34
36
35 from IPython.nbconvert import filters
37 from IPython.nbconvert import filters
@@ -110,27 +112,19 b' class Exporter(Configurable):'
110 #Extension that the template files use.
112 #Extension that the template files use.
111 template_extension = Unicode(".tpl", config=True)
113 template_extension = Unicode(".tpl", config=True)
112
114
113 #Processors that process the input data prior to the export, set in the
115 #Configurability, allows the user to easily add filters and transformers.
114 #constructor for this class.
116 transformers = List(config=True,
115 transformers = None
117 help="""List of transformers, by name or namespace, to enable.""")
116
118 filters = Dict(config=True,
119 help="""Dictionary of filters, by name and namespace, to add to the Jinja
120 environment.""")
117
121
118 def __init__(self, transformers=None, filters=None, config=None, extra_loaders=None, **kw):
122 def __init__(self, config=None, extra_loaders=None, **kw):
119 """
123 """
120 Public constructor
124 Public constructor
121
125
122 Parameters
126 Parameters
123 ----------
127 ----------
124 transformers : list[of transformer]
125 Custom transformers to apply to the notebook prior to engaging
126 the Jinja template engine. Any transformers specified here
127 will override existing transformers if a naming conflict
128 occurs.
129 filters : dict[of filter]
130 filters specified here will override existing filters if a naming
131 conflict occurs. Filters are availlable in jinja template through
132 the name of the corresponding key. Cf class docstring for
133 availlable default filters.
134 config : config
128 config : config
135 User configuration instance.
129 User configuration instance.
136 extra_loaders : list[of Jinja Loaders]
130 extra_loaders : list[of Jinja Loaders]
@@ -149,49 +143,53 b' class Exporter(Configurable):'
149 self._init_environment(extra_loaders=extra_loaders)
143 self._init_environment(extra_loaders=extra_loaders)
150
144
151 #Add transformers
145 #Add transformers
146 self._transformers = []
152 self._register_transformers()
147 self._register_transformers()
153
148
154 #Add filters to the Jinja2 environment
149 #Add filters to the Jinja2 environment
155 self._register_filters()
150 self._register_filters()
156
151
157 #Load user transformers. Overwrite existing transformers if need be.
152 #Load user transformers. Enabled by default.
158 if transformers :
153 if self.transformers:
159 for transformer in transformers:
154 for transformer in self.transformers:
160 self.register_transformer(transformer)
155 self.register_transformer(transformer, True)
161
156
162 #Load user filters. Overwrite existing filters if need be.
157 #Load user filters. Overwrite existing filters if need be.
163 if not filters is None:
158 if self.filters:
164 for key, user_filter in filters.iteritems():
159 for key, user_filter in self.filters.iteritems():
165 self.register_filter(key, user_filter)
160 self.register_filter(key, user_filter)
166
161
162
167 @property
163 @property
168 def default_config(self):
164 def default_config(self):
169 return Config()
165 return Config()
170
166
171
167
172
168 def from_notebook_node(self, nb, resources={}, **kw):
173 def from_notebook_node(self, nb, resources=None):
174 """
169 """
175 Convert a notebook from a notebook node instance.
170 Convert a notebook from a notebook node instance.
176
171
177 Parameters
172 Parameters
178 ----------
173 ----------
179 nb : Notebook node
174 nb : Notebook node
180 resources : a dict of additional resources that
175 resources : dict (**kw)
181 can be accessed read/write by transformers
176 of additional resources that can be accessed read/write by
182 and filters.
177 transformers and filters.
183 """
178 """
184 if resources is None:
179
185 resources = {}
180 #Preprocess
186 nb, resources = self._preprocess(nb, resources)
181 nb, resources = self._preprocess(nb, resources)
187
188 #Load the template file.
189 self.template = self.environment.get_template(self.template_file+self.template_extension)
190
191 return self.template.render(nb=nb, resources=resources), resources
192
182
183 #Convert
184 self.template = self.environment.get_template(self.template_file + self.template_extension)
185 output = self.template.render(nb=nb, resources=resources)
186
187 #Set output extension in resources dict
188 resources['output_extension'] = self.file_extension
189 return output, resources
193
190
194 def from_filename(self, filename):
191
192 def from_filename(self, filename, resources={}, **kw):
195 """
193 """
196 Convert a notebook from a notebook file.
194 Convert a notebook from a notebook file.
197
195
@@ -202,10 +200,10 b' class Exporter(Configurable):'
202 """
200 """
203
201
204 with io.open(filename) as f:
202 with io.open(filename) as f:
205 return self.from_notebook_node(nbformat.read(f, 'json'))
203 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
206
204
207
205
208 def from_file(self, file_stream):
206 def from_file(self, file_stream, resources={}, **kw):
209 """
207 """
210 Convert a notebook from a notebook file.
208 Convert a notebook from a notebook file.
211
209
@@ -214,10 +212,10 b' class Exporter(Configurable):'
214 file_stream : file-like object
212 file_stream : file-like object
215 Notebook file-like object to convert.
213 Notebook file-like object to convert.
216 """
214 """
217 return self.from_notebook_node(nbformat.read(file_stream, 'json'))
215 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
218
216
219
217
220 def register_transformer(self, transformer):
218 def register_transformer(self, transformer, enabled=None):
221 """
219 """
222 Register a transformer.
220 Register a transformer.
223 Transformers are classes that act upon the notebook before it is
221 Transformers are classes that act upon the notebook before it is
@@ -229,20 +227,35 b' class Exporter(Configurable):'
229 ----------
227 ----------
230 transformer : transformer
228 transformer : transformer
231 """
229 """
232 if self.transformers is None:
230
233 self.transformers = []
231 #Handle transformer's registration based on it's type
234
235 if inspect.isfunction(transformer):
232 if inspect.isfunction(transformer):
236 self.transformers.append(transformer)
233 #Transformer is a function, no need to construct it.
234 self._transformers.append(transformer)
237 return transformer
235 return transformer
236
237 elif isinstance(transformer, types.StringTypes):
238 #Transformer is a string, import the namespace and recursively call
239 #this register_transformer method
240 transformer_cls = import_item(DottedObjectName(transformer))
241 return self.register_transformer(transformer_cls, enabled=None)
242
238 elif isinstance(transformer, MetaHasTraits):
243 elif isinstance(transformer, MetaHasTraits):
239 transformer_instance = transformer(config=self.config)
244 #Transformer is configurable. Make sure to pass in new default for
240 self.transformers.append(transformer_instance)
245 #the enabled flag if one was specified.
241 return transformer_instance
246 c = Config()
247 if not enabled is None:
248 c = Config({transformer.__name__: {'enabled': enabled}})
249 c.merge(self.config)
250 transformer_instance = transformer(config=c)
251
242 else:
252 else:
253 #Transformer is not configurable, construct it
243 transformer_instance = transformer()
254 transformer_instance = transformer()
244 self.transformers.append(transformer_instance)
255
245 return transformer_instance
256 #Register and return the transformer.
257 self._transformers.append(transformer_instance)
258 return transformer_instance
246
259
247
260
248 def register_filter(self, name, filter):
261 def register_filter(self, name, filter):
@@ -259,6 +272,9 b' class Exporter(Configurable):'
259 """
272 """
260 if inspect.isfunction(filter):
273 if inspect.isfunction(filter):
261 self.environment.filters[name] = filter
274 self.environment.filters[name] = filter
275 elif isinstance(filter, types.StringTypes):
276 filter_cls = import_item(DottedObjectName(filter))
277 self.register_filter(name, filter_cls)
262 elif isinstance(filter, MetaHasTraits):
278 elif isinstance(filter, MetaHasTraits):
263 self.environment.filters[name] = filter(config=self.config)
279 self.environment.filters[name] = filter(config=self.config)
264 else:
280 else:
@@ -268,22 +284,20 b' class Exporter(Configurable):'
268
284
269 def _register_transformers(self):
285 def _register_transformers(self):
270 """
286 """
271 Register all of the transformers needed for this exporter.
287 Register all of the transformers needed for this exporter, disabled
288 unless specified explicitly.
272 """
289 """
273
290
274 self.register_transformer(transformers.coalesce_streams)
291 self.register_transformer(transformers.coalesce_streams)
275
292 self.register_transformer(transformers.ExtractFigureTransformer)
276 #Remember the figure extraction transformer so it can be enabled and
277 #disabled easily later.
278 self.extract_figure_transformer = self.register_transformer(transformers.ExtractFigureTransformer)
279
293
280
294
281 def _register_filters(self):
295 def _register_filters(self):
282 """
296 """
283 Register all of the filters required for the exporter.
297 Register all of the filters required for the exporter.
284 """
298 """
285 for k, v in default_filters.iteritems():
299 for key, value in default_filters.iteritems():
286 self.register_filter(k, v)
300 self.register_filter(key, value)
287
301
288
302
289 def _init_environment(self, extra_loaders=None):
303 def _init_environment(self, extra_loaders=None):
@@ -338,9 +352,9 b' class Exporter(Configurable):'
338 # we are never safe enough with what the transformers could do.
352 # we are never safe enough with what the transformers could do.
339 nbc = deepcopy(nb)
353 nbc = deepcopy(nb)
340 resc = deepcopy(resources)
354 resc = deepcopy(resources)
355
341 #Run each transformer on the notebook. Carry the output along
356 #Run each transformer on the notebook. Carry the output along
342 #to each transformer
357 #to each transformer
343 for transformer in self.transformers:
358 for transformer in self._transformers:
344 nb, resources = transformer(nbc, resc)
359 nbc, resc = transformer(nbc, resc)
345 return nb, resources
360 return nbc, resc
346
@@ -85,23 +85,26 b' class LatexExporter(Exporter):'
85 """
85 """
86 Register all of the transformers needed for this exporter.
86 Register all of the transformers needed for this exporter.
87 """
87 """
88
89 #Register ConvertSvgTransformer before any other transformers!
90 #Important because it allows the conversion of svg->png BEFORE the
91 #extract figure transformer acts on the data.
92 self.register_transformer(transformers.ConvertSvgTransformer, True)
88
93
89 #Register the transformers of the base class.
94 #Register transformers
90 super(LatexExporter, self)._register_transformers()
95 super(LatexExporter, self)._register_transformers()
91
96 self.register_transformer(transformers.LatexTransformer, True)
92 #Register latex transformer
93 self.register_transformer(transformers.LatexTransformer)
94
97
95 @property
98 @property
96 def default_config(self):
99 def default_config(self):
97 c = Config({
100 c = Config({
98 'GlobalConfigurable': {
101 'GlobalConfigurable': {
99 'display_data_priority' : ['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text']
102 'display_data_priority' : ['latex', 'png', 'jpg', 'jpeg']
100 },
103 },
101 'ExtractFigureTransformer': {
104 'ExtractFigureTransformer': {
102 'enabled':True,
105 'enabled':True
103 'extra_ext_map':{'svg':'pdf'},
104 }
106 }
107
105 })
108 })
106 c.merge(super(LatexExporter,self).default_config)
109 c.merge(super(LatexExporter,self).default_config)
107 return c
110 return c
@@ -45,7 +45,7 b' class RevealExporter(BasicHTMLExporter):'
45 super(RevealExporter, self)._register_transformers()
45 super(RevealExporter, self)._register_transformers()
46
46
47 #Register reveal help transformer
47 #Register reveal help transformer
48 self.register_transformer(transformers.RevealHelpTransformer)
48 self.register_transformer(transformers.RevealHelpTransformer, True)
49
49
50 @property
50 @property
51 def default_config(self):
51 def default_config(self):
@@ -42,13 +42,4 b' class SphinxHowtoExporter(LatexExporter):'
42 super(SphinxHowtoExporter, self)._register_transformers()
42 super(SphinxHowtoExporter, self)._register_transformers()
43
43
44 #Register sphinx latex transformer
44 #Register sphinx latex transformer
45 self.register_transformer(transformers.SphinxTransformer)
45 self.register_transformer(transformers.SphinxTransformer, True)
46
47 @property
48 def default_config(self):
49 c = Config({
50 'SphinxTransformer': {'enabled':True}
51 })
52 c.merge(super(SphinxHowtoExporter,self).default_config)
53 return c
54
@@ -19,207 +19,145 b' readme.rst for usage information'
19 #Stdlib imports
19 #Stdlib imports
20 from __future__ import print_function
20 from __future__ import print_function
21 import sys
21 import sys
22 import io
23 import os
22 import os
23 import glob
24
24
25 #From IPython
25 #From IPython
26 from IPython.config.application import Application
26 from IPython.core.application import BaseIPythonApplication
27 from IPython.utils.traitlets import Bool, Unicode
27 from IPython.config.application import catch_config_error
28 from IPython.utils.traitlets import Unicode, List, Instance, DottedObjectName, Type
29 from IPython.utils.importstring import import_item
28
30
29 from .exporters.export import export_by_name, get_export_names
31 from .exporters.export import export_by_name, get_export_names
30 from .exporters.exporter import Exporter
32 from .exporters.exporter import Exporter
31 from .transformers import extractfigure
33 from .writers.base import WriterBase
32 from .utils.config import GlobalConfigurable
34 from .utils.config import GlobalConfigurable
33
35
34 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
35 #Globals and constants
37 #Classes and functions
36 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
37
39
38 #'Keys in resources' user prompt.
40 class NbConvertApp(BaseIPythonApplication):
39 KEYS_PROMPT_HEAD = "====================== Keys in Resources =================================="
41 """Application used to convert to and from notebook file type (*.ipynb)"""
40 KEYS_PROMPT_BODY = """
41 ===========================================================================
42 You are responsible for writing these files into the appropriate
43 directory(ies) if need be. If you do not want to see this message, enable
44 the 'write' (boolean) flag of the converter.
45 ===========================================================================
46 """
47
42
48 _examples = """
49 ipython nbconvert rst Untitled0.ipynb # convert ipynb to ReStructured Text
50 ipython nbconvert latex Untitled0.ipynb # convert ipynb to LaTeX
51 ipython nbconvert reveal Untitled0.ipynb # convert to Reveal (HTML/JS) slideshow
52 """
53
43
44 description = Unicode(
45 u"""This application is used to convert notebook files (*.ipynb).
46 An ipython config file can be used to batch convert notebooks in the
47 current directory.""")
54
48
55 #-----------------------------------------------------------------------------
49 examples = Unicode(u"""
56 #Classes and functions
50 Running `ipython nbconvert` will read the directory config file and then
57 #-----------------------------------------------------------------------------
51 apply it to one or more notebooks.
58
52
59 class NbConvertApp(Application):
53 Multiple notebooks can be given at the command line in a couple of
60 __doc__ = """IPython notebook conversion utility
54 different ways:
55
56 > ipython nbconvert notebook*.ipynb
57 > ipython nbconvert notebook1.ipynb notebook2.ipynb
58 > ipython nbconvert # this will use the config file to fill in the notebooks
59 """)
61
60
62 Convert to and from notebook file type (*.ipynb)
61 config_file_name = Unicode(u'ipython_nbconvert_config.py')
63
62
64 ipython nbconvert TARGET FILENAME
63 #Writer specific variables
64 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
65 help="""Instance of the writer class used to write the
66 results of the conversion.""")
67 writer_class = DottedObjectName('FilesWriter', config=True,
68 help="""Writer class used to write the
69 results of the conversion""")
70 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
71 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
72 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
73 writer_factory = Type()
65
74
66 Supported export TARGETs are: %s
75 def _writer_class_changed(self, name, old, new):
67 """ % (" ".join(get_export_names()))
76 if new in self.writer_aliases:
68 description = Unicode(__doc__)
77 new = self.writer_aliases[new]
78 self.writer_factory = import_item(new)
69
79
70 examples = _examples
71
80
72 stdout = Bool(
81 #Other configurable variables
73 False, config=True,
82 export_format = Unicode(
74 help="""Whether to print the converted IPYNB file to stdout
83 "", config=True,
75 use full do diff files without actually writing a new file"""
84 help="""If specified, nbconvert will convert the document(s) specified
76 )
85 using this format.""")
77
86
78 write = Bool(
87 notebooks = List([], config=True, help="""List of notebooks to convert.
79 True, config=True,
88 Search patterns are supported.""")
80 help="""Should the converted notebook file be written to disk
81 along with potential extracted resources."""
82 )
83
89
84 aliases = {
90 aliases = {'format':'NbConvertApp.export_format',
85 'stdout':'NbConvertApp.stdout',
91 'notebooks':'NbConvertApp.notebooks',
86 'write':'NbConvertApp.write',
92 'writer':'NbConvertApp.writer_class'}
87 }
88
93
89 flags = {}
90
94
91 flags['stdout'] = (
95 @catch_config_error
92 {'NbConvertApp' : {'stdout' : True}},
96 def initialize(self, argv=None):
93 """Print converted file to stdout, equivalent to --stdout=True
97 super(NbConvertApp, self).initialize(argv)
94 """
95 )
96
98
97 flags['no-write'] = (
99 #Register class here to have help with help all
98 {'NbConvertApp' : {'write' : True}},
100 self.classes.insert(0, Exporter)
99 """Do not write to disk, equivalent to --write=False
101 self.classes.insert(0, WriterBase)
100 """
102 self.classes.insert(0, GlobalConfigurable)
101 )
102
103
104 #Init
105 self.init_config(self.extra_args)
106 self.init_writer()
103
107
104 def __init__(self, **kwargs):
105 """Public constructor"""
106
108
107 #Call base class
109 def init_config(self, extra_args):
108 super(NbConvertApp, self).__init__(**kwargs)
110 """
111 Add notebooks to the config if needed. Glob each notebook to replace
112 notebook patterns with filenames.
113 """
109
114
110 #Register class here to have help with help all
115 #Get any additional notebook patterns from the commandline
111 self.classes.insert(0, Exporter)
116 if len(extra_args) > 0:
112 self.classes.insert(0, GlobalConfigurable)
117 for pattern in extra_args:
118 self.notebooks.append(pattern)
119
120 #Use glob to replace all the notebook patterns with filenames.
121 filenames = []
122 for pattern in self.notebooks:
123 for filename in glob.glob(pattern):
124 if not filename in filenames:
125 filenames.append(filename)
126 self.notebooks = filenames
127
128
129 def init_writer(self):
130 """
131 Initialize the writer (which is stateless)
132 """
133 self._writer_class_changed(None, self.writer_class, self.writer_class)
134 self.writer = self.writer_factory(parent=self)
113
135
114
136
115 def start(self, argv=None):
137 def start(self, argv=None):
116 """Entrypoint of NbConvert application.
117
118 Parameters
119 ----------
120 argv : list
121 Commandline arguments
122 """
138 """
123
139 Entrypoint of NbConvert application.
124 #Parse the commandline options.
140 """
125 self.parse_command_line(argv)
126
141
127 #Call base
142 #Call base
128 super(NbConvertApp, self).start()
143 super(NbConvertApp, self).start()
129
144
130 #The last arguments in list will be used by nbconvert
145 #Export each notebook
131 if len(self.extra_args) is not 3:
146 for notebook_filename in self.notebooks:
132 print( "Wrong number of arguments, use --help flag for usage", file=sys.stderr)
147
133 sys.exit(-1)
148 #Get a unique key for the notebook and set it in the resources object.
134 export_type = (self.extra_args)[1]
149 basename = os.path.basename(notebook_filename)
135 ipynb_file = (self.extra_args)[2]
150 notebook_name = basename[:basename.rfind('.')]
136
151 resources = {}
137 #Export
152 resources['unique_key'] = notebook_name
138 try:
153
139 return_value = export_by_name(export_type, ipynb_file)
154 #Export & write
140 except NameError as e:
155 output, resources = export_by_name(self.export_format,
141 print("Error: '%s' exporter not found." % export_type,
156 notebook_filename,
142 file=sys.stderr)
157 resources=resources,
143 print("Known exporters are:",
158 config=self.config)
144 "\n\t" + "\n\t".join(get_export_names()),
159 self.writer.write(output, resources, notebook_name=notebook_name)
145 file=sys.stderr)
160
146 sys.exit(-1)
147 else:
148 (output, resources, exporter) = return_value
149
150 #TODO: Allow user to set output directory and file.
151 destination_filename = None
152 destination_directory = None
153 if self.write:
154
155 #Get the file name without the '.ipynb' (6 chars) extension and then
156 #remove any addition periods and spaces. The resulting name will
157 #be used to create the directory that the files will be exported
158 #into.
159 out_root = ipynb_file[:-6].replace('.', '_').replace(' ', '_')
160 destination_filename = os.path.join(out_root+'.'+exporter.file_extension)
161
162 destination_directory = out_root+'_files'
163 if not os.path.exists(destination_directory):
164 os.mkdir(destination_directory)
165
166 #Write the results
167 if self.stdout or not (destination_filename is None and destination_directory is None):
168 self._write_results(output, resources, destination_filename, destination_directory)
169
170
171 def _write_results(self, output, resources, destination_filename=None, destination_directory=None):
172 """Output the conversion results to the console and/or filesystem
173
174 Parameters
175 ----------
176 output : str
177 Output of conversion
178 resources : dictionary
179 Additional input/output used by the transformers. For
180 example, the ExtractFigure transformer outputs the
181 figures it extracts into this dictionary. This method
182 relies on the figures being in this dictionary when
183 attempting to write the figures to the file system.
184 destination_filename : str, Optional
185 Filename to write output into. If None, output is not
186 written to a file.
187 destination_directory : str, Optional
188 Directory to write notebook data (i.e. figures) to. If
189 None, figures are not written to the file system.
190 """
191
192 if self.stdout:
193 print(output.encode('utf-8'))
194
195 #Write file output from conversion.
196 if not destination_filename is None:
197 with io.open(destination_filename, 'w') as f:
198 f.write(output)
199
200 #Get the key names used by the extract figure transformer
201 figures_key = extractfigure.FIGURES_KEY
202 binary_key = extractfigure.BINARY_KEY
203 text_key = extractfigure.TEXT_KEY
204
205 #Output any associate figures into the same "root" directory.
206 binkeys = resources.get(figures_key, {}).get(binary_key,{}).keys()
207 textkeys = resources.get(figures_key, {}).get(text_key,{}).keys()
208 if binkeys or textkeys :
209 if not destination_directory is None:
210 for key in binkeys:
211 with io.open(os.path.join(destination_directory, key), 'wb') as f:
212 f.write(resources[figures_key][binary_key][key])
213 for key in textkeys:
214 with io.open(os.path.join(destination_directory, key), 'w') as f:
215 f.write(resources[figures_key][text_key][key])
216
217 #Figures that weren't exported which will need to be created by the
218 #user. Tell the user what figures these are.
219 if self.stdout:
220 print(KEYS_PROMPT_HEAD, file=sys.stderr)
221 print(resources[figures_key].keys(), file=sys.stderr)
222 print(KEYS_PROMPT_BODY , file=sys.stderr)
223
161
224 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
225 # Main entry point
163 # Main entry point
@@ -1,6 +1,8 b''
1 # Class base Transformers
1 # Class base Transformers
2 from .activatable import ActivatableTransformer
2 from .activatable import ActivatableTransformer
3 from .base import ConfigurableTransformer
3 from .base import ConfigurableTransformer
4 from .convertfigures import ConvertFiguresTransformer
5 from .convertsvg import ConvertSvgTransformer
4 from .extractfigure import ExtractFigureTransformer
6 from .extractfigure import ExtractFigureTransformer
5 from .revealhelp import RevealHelpTransformer
7 from .revealhelp import RevealHelpTransformer
6 from .latex import LatexTransformer
8 from .latex import LatexTransformer
@@ -31,7 +31,7 b' class ConfigurableTransformer(GlobalConfigurable):'
31 Any configurable traitlets this class exposed will be configurable in profiles
31 Any configurable traitlets this class exposed will be configurable in profiles
32 using c.SubClassName.atribute=value
32 using c.SubClassName.atribute=value
33
33
34 you can overwrite cell_transform to apply a transformation independently on each cell
34 you can overwrite transform_cell to apply a transformation independently on each cell
35 or __call__ if you prefer your own logic. See corresponding docstring for informations.
35 or __call__ if you prefer your own logic. See corresponding docstring for informations.
36 """
36 """
37
37
@@ -59,7 +59,7 b' class ConfigurableTransformer(GlobalConfigurable):'
59
59
60 You should return modified nb, resources.
60 You should return modified nb, resources.
61 If you wish to apply your transform on each cell, you might want to
61 If you wish to apply your transform on each cell, you might want to
62 overwrite cell_transform method instead.
62 overwrite transform_cell method instead.
63
63
64 Parameters
64 Parameters
65 ----------
65 ----------
@@ -72,13 +72,13 b' class ConfigurableTransformer(GlobalConfigurable):'
72 try :
72 try :
73 for worksheet in nb.worksheets :
73 for worksheet in nb.worksheets :
74 for index, cell in enumerate(worksheet.cells):
74 for index, cell in enumerate(worksheet.cells):
75 worksheet.cells[index], resources = self.cell_transform(cell, resources, index)
75 worksheet.cells[index], resources = self.transform_cell(cell, resources, index)
76 return nb, resources
76 return nb, resources
77 except NotImplementedError:
77 except NotImplementedError:
78 raise NotImplementedError('should be implemented by subclass')
78 raise NotImplementedError('should be implemented by subclass')
79
79
80
80
81 def cell_transform(self, cell, resources, index):
81 def transform_cell(self, cell, resources, index):
82 """
82 """
83 Overwrite if you want to apply a transformation on each cell. You
83 Overwrite if you want to apply a transformation on each cell. You
84 should return modified cell and resource dictionary.
84 should return modified cell and resource dictionary.
@@ -12,7 +12,6 b" notebook file. The extracted figures are returned in the 'resources' dictionary"
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 import itertools
16
15
17 from IPython.utils.traitlets import Dict, Unicode
16 from IPython.utils.traitlets import Dict, Unicode
18 from .activatable import ActivatableTransformer
17 from .activatable import ActivatableTransformer
@@ -21,9 +20,10 b' from .activatable import ActivatableTransformer'
21 # Constants
20 # Constants
22 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
23
22
24 FIGURES_KEY = "figures"
23 # win64 is win32 for backwards compatability, for now. See
25 BINARY_KEY = "binary"
24 # http://mail.python.org/pipermail/patches/2000-May/000648.html
26 TEXT_KEY = "text"
25 # for the original patch that this decision was made.
26 WINDOWS_PLATFORMS = ['win32']
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Classes
29 # Classes
@@ -35,36 +35,12 b' class ExtractFigureTransformer(ActivatableTransformer):'
35 figures are returned in the 'resources' dictionary.
35 figures are returned in the 'resources' dictionary.
36 """
36 """
37
37
38 extra_extension_map = Dict({},
39 config=True,
40 help="""Extra map to override extension based on type.
41 Useful for latex where SVG will be converted to PDF before inclusion
42 """)
43
44 key_format_map = Dict({}, config=True,)
45 figure_name_format_map = Dict({}, config=True)
46
38
47 #TODO: Change this to .format {} syntax
39 figure_filename_template = Unicode(
48 default_key_template = Unicode('_fig_{index:02d}.{ext}', config=True)
40 "{unique_key}_{cell_index}_{index}.{extension}", config=True)
49
41
50 def __init__(self, config=None, **kw):
51 """
52 Public constructor
53
54 Parameters
55 ----------
56 config : Config
57 Configuration file structure
58 **kw : misc
59 Additional arguments
60 """
61
62 super(ExtractFigureTransformer, self).__init__(config=config, **kw)
63
64 # A unique index for association with extracted figures
65 self.index_generator = itertools.count(1)
66
42
67 def cell_transform(self, cell, resources, index):
43 def transform_cell(self, cell, resources, cell_index):
68 """
44 """
69 Apply a transformation on each cell,
45 Apply a transformation on each cell,
70
46
@@ -75,69 +51,47 b' class ExtractFigureTransformer(ActivatableTransformer):'
75 resources : dictionary
51 resources : dictionary
76 Additional resources used in the conversion process. Allows
52 Additional resources used in the conversion process. Allows
77 transformers to pass variables into the Jinja engine.
53 transformers to pass variables into the Jinja engine.
78 index : int
54 cell_index : int
79 Index of the cell being processed (see base.py)
55 Index of the cell being processed (see base.py)
80 """
56 """
57
58 #Get the unique key from the resource dict if it exists. If it does not
59 #exist, use 'figure' as the default.
60 unique_key = resources.get('unique_key', 'figure')
81
61
82 if resources.get(FIGURES_KEY, None) is None :
62 #Make sure figures key exists
83 resources[FIGURES_KEY] = {TEXT_KEY:{},BINARY_KEY:{}}
63 if not 'figures' in resources:
64 resources['figures'] = {}
84
65
85 for out in cell.get('outputs', []):
66 #Loop through all of the outputs in the cell
67 for index, out in enumerate(cell.get('outputs', [])):
68
69 #Get the output in data formats that the template is interested in.
86 for out_type in self.display_data_priority:
70 for out_type in self.display_data_priority:
87
88 if out.hasattr(out_type):
71 if out.hasattr(out_type):
89 figname, key, data, binary = self._new_figure(out[out_type], out_type)
72 data = out[out_type]
90 out['key_'+out_type] = figname
91
92 if binary :
93 resources[FIGURES_KEY][BINARY_KEY][key] = data
94 else :
95 resources[FIGURES_KEY][TEXT_KEY][key] = data
96
97 index += 1
98 return cell, resources
99
100
101 def _get_override_extension(self, extension):
102 """Gets the overriden extension if it exists, else returns extension.
103
73
104 Parameters
74 #Binary files are base64-encoded, SVG is already XML
105 ----------
75 if out_type in ('png', 'jpg', 'pdf'):
106 extension : str
76 data = data.decode('base64')
107 File extension.
77 elif sys.platform in WINDOWS_PLATFORMS:
108 """
78 data = data.replace('\n', '\r\n')
109
79
110 if extension in self.extra_extension_map :
80 #Build a figure name
111 return self.extra_extension_map[extension]
81 figure_name = self.figure_filename_template.format(
112
82 unique_key=unique_key,
113 return extension
83 cell_index=cell_index,
114
84 index=index,
115
85 extension=out_type)
116 def _new_figure(self, data, format):
86
117 """Create a new figure file in the given format.
87 #On the cell, make the figure available via
88 # cell.outputs[i].svg_filename ... etc (svg in example)
89 # Where
90 # cell.outputs[i].svg contains the data
91 out[out_type + '_filename'] = figure_name
92
93 #In the resources, make the figure available via
94 # resources['figures']['filename'] = data
95 resources['figures'][figure_name] = data
118
96
119 Parameters
97 return cell, resources
120 ----------
121 data : str
122 Cell data (from Notebook node cell)
123 format : str
124 Figure format
125 index : int
126 Index of the figure being extracted
127 """
128
129 figure_name_template = self.figure_name_format_map.get(format, self.default_key_template)
130 key_template = self.key_format_map.get(format, self.default_key_template)
131
132 #TODO: option to pass the hash as data?
133 index = next(self.index_generator)
134 figure_name = figure_name_template.format(index=index, ext=self._get_override_extension(format))
135 key = key_template.format(index=index, ext=self._get_override_extension(format))
136
137 #Binary files are base64-encoded, SVG is already XML
138 binary = False
139 if format in ('png', 'jpg', 'pdf'):
140 data = data.decode('base64')
141 binary = True
142
143 return figure_name, key, data, binary
@@ -29,7 +29,7 b' class LatexTransformer(ActivatableTransformer):'
29 Converter for latex destined documents.
29 Converter for latex destined documents.
30 """
30 """
31
31
32 def cell_transform(self, cell, resources, index):
32 def transform_cell(self, cell, resources, index):
33 """
33 """
34 Apply a transformation on each cell,
34 Apply a transformation on each cell,
35
35
@@ -39,9 +39,8 b' class RevealHelpTransformer(ConfigurableTransformer):'
39 transformers to pass variables into the Jinja engine.
39 transformers to pass variables into the Jinja engine.
40 """
40 """
41
41
42
43 for worksheet in nb.worksheets :
42 for worksheet in nb.worksheets :
44 for i, cell in enumerate(worksheet.cells):
43 for index, cell in enumerate(worksheet.cells):
45
44
46 #Make sure the cell has slideshow metadata.
45 #Make sure the cell has slideshow metadata.
47 cell.metadata.align_type = cell.get('metadata', {}).get('slideshow', {}).get('align_type', 'Left')
46 cell.metadata.align_type = cell.get('metadata', {}).get('slideshow', {}).get('align_type', 'Left')
@@ -50,9 +49,9 b' class RevealHelpTransformer(ConfigurableTransformer):'
50 #Get the slide type. If type is start of subslide or slide,
49 #Get the slide type. If type is start of subslide or slide,
51 #end the last subslide/slide.
50 #end the last subslide/slide.
52 if cell.metadata.slide_type in ['slide']:
51 if cell.metadata.slide_type in ['slide']:
53 worksheet.cells[i - 1].metadata.slide_helper = 'slide_end'
52 worksheet.cells[index - 1].metadata.slide_helper = 'slide_end'
54 if cell.metadata.slide_type in ['subslide']:
53 if cell.metadata.slide_type in ['subslide']:
55 worksheet.cells[i - 1].metadata.slide_helper = 'subslide_end'
54 worksheet.cells[index - 1].metadata.slide_helper = 'subslide_end'
56
55
57
56
58 if 'reveal' not in resources:
57 if 'reveal' not in resources:
General Comments 0
You need to be logged in to leave comments. Login now