##// 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 3 from .exporters import *
4 4 import filters
5 5 import transformers
6 import writers
@@ -16,6 +16,7 b' Module containing single call export functions.'
16 16 from functools import wraps
17 17
18 18 from IPython.nbformat.v3.nbbase import NotebookNode
19 from IPython.config import Config
19 20
20 21 from .exporter import Exporter
21 22 from .basichtml import BasicHTMLExporter
@@ -37,17 +38,10 b' def DocDecorator(f):'
37 38 #Set docstring of function
38 39 f.__doc__ = f.__doc__ + """
39 40 nb : Notebook node
40 config : config
41 config : config (optional, keyword arg)
41 42 User configuration instance.
42 transformers : list[of transformer]
43 Custom transformers to apply to the notebook prior to engaging
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.
43 resources : dict (optional, keyword arg)
44 Resources used in the conversion process.
51 45
52 46 Returns
53 47 ----------
@@ -89,7 +83,7 b' __all__ = ['
89 83 ]
90 84
91 85 @DocDecorator
92 def export(exporter_type, nb, config=None, transformers=None, filters=None):
86 def export(exporter_type, nb, **kw):
93 87 """
94 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 100 raise TypeError("Exporter is None")
107 101 elif not issubclass(exporter_type, Exporter):
108 102 raise TypeError("Exporter type does not inherit from Exporter (base)")
109
110 103 if nb is None:
111 104 raise TypeError("nb is None")
112 105
113 106 #Create the exporter
114 exporter_instance = exporter_type(preprocessors=transformers,
115 jinja_filters=filters, config=config)
107 exporter_instance = exporter_type(config=kw.get('config', Config()))
116 108
117 109 #Try to convert the notebook using the appropriate conversion function.
110 resources = kw.get('resources', {})
118 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 113 elif isinstance(nb, basestring):
121 output, resources = exporter_instance.from_filename(nb)
114 output, resources = exporter_instance.from_filename(nb, resources)
122 115 else:
123 output, resources = exporter_instance.from_file(nb)
124 return output, resources, exporter_instance
116 output, resources = exporter_instance.from_file(nb, resources)
117 return output, resources
125 118
126 119
127 120 @DocDecorator
128 def export_sphinx_manual(nb, config=None, transformers=None, filters=None):
121 def export_sphinx_manual(nb, **kw):
129 122 """
130 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 128 @DocDecorator
136 def export_sphinx_howto(nb, config=None, transformers=None, filters=None):
129 def export_sphinx_howto(nb, **kw):
137 130 """
138 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 136 @DocDecorator
144 def export_basic_html(nb, config=None, transformers=None, filters=None):
137 def export_basic_html(nb, **kw):
145 138 """
146 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 144 @DocDecorator
152 def export_full_html(nb, config=None, transformers=None, filters=None):
145 def export_full_html(nb, **kw):
153 146 """
154 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 152 @DocDecorator
160 def export_latex(nb, config=None, transformers=None, filters=None):
153 def export_latex(nb, **kw):
161 154 """
162 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 160 @DocDecorator
168 def export_markdown(nb, config=None, transformers=None, filters=None):
161 def export_markdown(nb, **kw):
169 162 """
170 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 168 @DocDecorator
176 def export_python(nb, config=None, transformers=None, filters=None):
169 def export_python(nb, **kw):
177 170 """
178 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 176 @DocDecorator
184 def export_reveal(nb, config=None, transformers=None, filters=None):
177 def export_reveal(nb, **kw):
185 178 """
186 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 184 @DocDecorator
192 def export_rst(nb, config=None, transformers=None, filters=None):
185 def export_rst(nb, **kw):
193 186 """
194 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 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 195 Export a notebook object to a template type by its name. Reflection
203 196 (Inspect) is used to find the template's corresponding explicit export
204 197 method defined in this module. That method is then called directly.
205 198
206 template_name : str
199 format_name : str
207 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 205 if function_name in globals():
213 return globals()[function_name](nb, config, transformers, filters)
206 return globals()[function_name](nb, **kw)
214 207 else:
215 208 raise NameError("template for `%s` not found" % function_name)
216 209
@@ -218,6 +211,7 b' def get_export_names():'
218 211 "Return a list of the currently supported export targets"
219 212 # grab everything after 'export_'
220 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 216 l = [x for x in l if 'by_name' not in x]
223 217 return sorted(l)
@@ -20,6 +20,7 b' from __future__ import print_function, absolute_import'
20 20 import io
21 21 import os
22 22 import inspect
23 import types
23 24 from copy import deepcopy
24 25
25 26 # other libs/dependencies
@@ -29,7 +30,8 b' from jinja2 import Environment, FileSystemLoader, ChoiceLoader'
29 30 from IPython.config.configurable import Configurable
30 31 from IPython.config import Config
31 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 35 from IPython.utils.text import indent
34 36
35 37 from IPython.nbconvert import filters
@@ -110,27 +112,19 b' class Exporter(Configurable):'
110 112 #Extension that the template files use.
111 113 template_extension = Unicode(".tpl", config=True)
112 114
113 #Processors that process the input data prior to the export, set in the
114 #constructor for this class.
115 transformers = None
116
115 #Configurability, allows the user to easily add filters and transformers.
116 transformers = List(config=True,
117 help="""List of transformers, by name or namespace, to enable.""")
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 124 Public constructor
121 125
122 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 128 config : config
135 129 User configuration instance.
136 130 extra_loaders : list[of Jinja Loaders]
@@ -149,49 +143,53 b' class Exporter(Configurable):'
149 143 self._init_environment(extra_loaders=extra_loaders)
150 144
151 145 #Add transformers
146 self._transformers = []
152 147 self._register_transformers()
153 148
154 149 #Add filters to the Jinja2 environment
155 150 self._register_filters()
156 151
157 #Load user transformers. Overwrite existing transformers if need be.
158 if transformers :
159 for transformer in transformers:
160 self.register_transformer(transformer)
152 #Load user transformers. Enabled by default.
153 if self.transformers:
154 for transformer in self.transformers:
155 self.register_transformer(transformer, True)
161 156
162 157 #Load user filters. Overwrite existing filters if need be.
163 if not filters is None:
164 for key, user_filter in filters.iteritems():
158 if self.filters:
159 for key, user_filter in self.filters.iteritems():
165 160 self.register_filter(key, user_filter)
166 161
162
167 163 @property
168 164 def default_config(self):
169 165 return Config()
170 166
171 167
172
173 def from_notebook_node(self, nb, resources=None):
168 def from_notebook_node(self, nb, resources={}, **kw):
174 169 """
175 170 Convert a notebook from a notebook node instance.
176 171
177 172 Parameters
178 173 ----------
179 174 nb : Notebook node
180 resources : a dict of additional resources that
181 can be accessed read/write by transformers
182 and filters.
175 resources : dict (**kw)
176 of additional resources that can be accessed read/write by
177 transformers and filters.
183 178 """
184 if resources is None:
185 resources = {}
179
180 #Preprocess
186 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 194 Convert a notebook from a notebook file.
197 195
@@ -202,10 +200,10 b' class Exporter(Configurable):'
202 200 """
203 201
204 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 208 Convert a notebook from a notebook file.
211 209
@@ -214,10 +212,10 b' class Exporter(Configurable):'
214 212 file_stream : file-like object
215 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 220 Register a transformer.
223 221 Transformers are classes that act upon the notebook before it is
@@ -229,20 +227,35 b' class Exporter(Configurable):'
229 227 ----------
230 228 transformer : transformer
231 229 """
232 if self.transformers is None:
233 self.transformers = []
234
230
231 #Handle transformer's registration based on it's type
235 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 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 243 elif isinstance(transformer, MetaHasTraits):
239 transformer_instance = transformer(config=self.config)
240 self.transformers.append(transformer_instance)
241 return transformer_instance
244 #Transformer is configurable. Make sure to pass in new default for
245 #the enabled flag if one was specified.
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 252 else:
253 #Transformer is not configurable, construct it
243 254 transformer_instance = transformer()
244 self.transformers.append(transformer_instance)
245 return transformer_instance
255
256 #Register and return the transformer.
257 self._transformers.append(transformer_instance)
258 return transformer_instance
246 259
247 260
248 261 def register_filter(self, name, filter):
@@ -259,6 +272,9 b' class Exporter(Configurable):'
259 272 """
260 273 if inspect.isfunction(filter):
261 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 278 elif isinstance(filter, MetaHasTraits):
263 279 self.environment.filters[name] = filter(config=self.config)
264 280 else:
@@ -268,22 +284,20 b' class Exporter(Configurable):'
268 284
269 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 291 self.register_transformer(transformers.coalesce_streams)
275
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)
292 self.register_transformer(transformers.ExtractFigureTransformer)
279 293
280 294
281 295 def _register_filters(self):
282 296 """
283 297 Register all of the filters required for the exporter.
284 298 """
285 for k, v in default_filters.iteritems():
286 self.register_filter(k, v)
299 for key, value in default_filters.iteritems():
300 self.register_filter(key, value)
287 301
288 302
289 303 def _init_environment(self, extra_loaders=None):
@@ -338,9 +352,9 b' class Exporter(Configurable):'
338 352 # we are never safe enough with what the transformers could do.
339 353 nbc = deepcopy(nb)
340 354 resc = deepcopy(resources)
355
341 356 #Run each transformer on the notebook. Carry the output along
342 357 #to each transformer
343 for transformer in self.transformers:
344 nb, resources = transformer(nbc, resc)
345 return nb, resources
346
358 for transformer in self._transformers:
359 nbc, resc = transformer(nbc, resc)
360 return nbc, resc
@@ -85,23 +85,26 b' class LatexExporter(Exporter):'
85 85 """
86 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 95 super(LatexExporter, self)._register_transformers()
91
92 #Register latex transformer
93 self.register_transformer(transformers.LatexTransformer)
96 self.register_transformer(transformers.LatexTransformer, True)
94 97
95 98 @property
96 99 def default_config(self):
97 100 c = Config({
98 101 'GlobalConfigurable': {
99 'display_data_priority' : ['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text']
102 'display_data_priority' : ['latex', 'png', 'jpg', 'jpeg']
100 103 },
101 104 'ExtractFigureTransformer': {
102 'enabled':True,
103 'extra_ext_map':{'svg':'pdf'},
105 'enabled':True
104 106 }
107
105 108 })
106 109 c.merge(super(LatexExporter,self).default_config)
107 110 return c
@@ -45,7 +45,7 b' class RevealExporter(BasicHTMLExporter):'
45 45 super(RevealExporter, self)._register_transformers()
46 46
47 47 #Register reveal help transformer
48 self.register_transformer(transformers.RevealHelpTransformer)
48 self.register_transformer(transformers.RevealHelpTransformer, True)
49 49
50 50 @property
51 51 def default_config(self):
@@ -42,13 +42,4 b' class SphinxHowtoExporter(LatexExporter):'
42 42 super(SphinxHowtoExporter, self)._register_transformers()
43 43
44 44 #Register sphinx latex transformer
45 self.register_transformer(transformers.SphinxTransformer)
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
45 self.register_transformer(transformers.SphinxTransformer, True)
@@ -19,207 +19,145 b' readme.rst for usage information'
19 19 #Stdlib imports
20 20 from __future__ import print_function
21 21 import sys
22 import io
23 22 import os
23 import glob
24 24
25 25 #From IPython
26 from IPython.config.application import Application
27 from IPython.utils.traitlets import Bool, Unicode
26 from IPython.core.application import BaseIPythonApplication
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 31 from .exporters.export import export_by_name, get_export_names
30 32 from .exporters.exporter import Exporter
31 from .transformers import extractfigure
33 from .writers.base import WriterBase
32 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.
39 KEYS_PROMPT_HEAD = "====================== Keys in Resources =================================="
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 """
40 class NbConvertApp(BaseIPythonApplication):
41 """Application used to convert to and from notebook file type (*.ipynb)"""
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 #-----------------------------------------------------------------------------
56 #Classes and functions
57 #-----------------------------------------------------------------------------
49 examples = Unicode(u"""
50 Running `ipython nbconvert` will read the directory config file and then
51 apply it to one or more notebooks.
58 52
59 class NbConvertApp(Application):
60 __doc__ = """IPython notebook conversion utility
53 Multiple notebooks can be given at the command line in a couple of
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
67 """ % (" ".join(get_export_names()))
68 description = Unicode(__doc__)
75 def _writer_class_changed(self, name, old, new):
76 if new in self.writer_aliases:
77 new = self.writer_aliases[new]
78 self.writer_factory = import_item(new)
69 79
70 examples = _examples
71 80
72 stdout = Bool(
73 False, config=True,
74 help="""Whether to print the converted IPYNB file to stdout
75 use full do diff files without actually writing a new file"""
76 )
81 #Other configurable variables
82 export_format = Unicode(
83 "", config=True,
84 help="""If specified, nbconvert will convert the document(s) specified
85 using this format.""")
77 86
78 write = Bool(
79 True, config=True,
80 help="""Should the converted notebook file be written to disk
81 along with potential extracted resources."""
82 )
87 notebooks = List([], config=True, help="""List of notebooks to convert.
88 Search patterns are supported.""")
83 89
84 aliases = {
85 'stdout':'NbConvertApp.stdout',
86 'write':'NbConvertApp.write',
87 }
90 aliases = {'format':'NbConvertApp.export_format',
91 'notebooks':'NbConvertApp.notebooks',
92 'writer':'NbConvertApp.writer_class'}
88 93
89 flags = {}
90 94
91 flags['stdout'] = (
92 {'NbConvertApp' : {'stdout' : True}},
93 """Print converted file to stdout, equivalent to --stdout=True
94 """
95 )
95 @catch_config_error
96 def initialize(self, argv=None):
97 super(NbConvertApp, self).initialize(argv)
96 98
97 flags['no-write'] = (
98 {'NbConvertApp' : {'write' : True}},
99 """Do not write to disk, equivalent to --write=False
100 """
101 )
99 #Register class here to have help with help all
100 self.classes.insert(0, Exporter)
101 self.classes.insert(0, WriterBase)
102 self.classes.insert(0, GlobalConfigurable)
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
108 super(NbConvertApp, self).__init__(**kwargs)
109 def init_config(self, extra_args):
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
111 self.classes.insert(0, Exporter)
112 self.classes.insert(0, GlobalConfigurable)
115 #Get any additional notebook patterns from the commandline
116 if len(extra_args) > 0:
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 137 def start(self, argv=None):
116 """Entrypoint of NbConvert application.
117
118 Parameters
119 ----------
120 argv : list
121 Commandline arguments
122 138 """
123
124 #Parse the commandline options.
125 self.parse_command_line(argv)
139 Entrypoint of NbConvert application.
140 """
126 141
127 142 #Call base
128 143 super(NbConvertApp, self).start()
129 144
130 #The last arguments in list will be used by nbconvert
131 if len(self.extra_args) is not 3:
132 print( "Wrong number of arguments, use --help flag for usage", file=sys.stderr)
133 sys.exit(-1)
134 export_type = (self.extra_args)[1]
135 ipynb_file = (self.extra_args)[2]
136
137 #Export
138 try:
139 return_value = export_by_name(export_type, ipynb_file)
140 except NameError as e:
141 print("Error: '%s' exporter not found." % export_type,
142 file=sys.stderr)
143 print("Known exporters are:",
144 "\n\t" + "\n\t".join(get_export_names()),
145 file=sys.stderr)
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)
145 #Export each notebook
146 for notebook_filename in self.notebooks:
147
148 #Get a unique key for the notebook and set it in the resources object.
149 basename = os.path.basename(notebook_filename)
150 notebook_name = basename[:basename.rfind('.')]
151 resources = {}
152 resources['unique_key'] = notebook_name
153
154 #Export & write
155 output, resources = export_by_name(self.export_format,
156 notebook_filename,
157 resources=resources,
158 config=self.config)
159 self.writer.write(output, resources, notebook_name=notebook_name)
160
223 161
224 162 #-----------------------------------------------------------------------------
225 163 # Main entry point
@@ -1,6 +1,8 b''
1 1 # Class base Transformers
2 2 from .activatable import ActivatableTransformer
3 3 from .base import ConfigurableTransformer
4 from .convertfigures import ConvertFiguresTransformer
5 from .convertsvg import ConvertSvgTransformer
4 6 from .extractfigure import ExtractFigureTransformer
5 7 from .revealhelp import RevealHelpTransformer
6 8 from .latex import LatexTransformer
@@ -31,7 +31,7 b' class ConfigurableTransformer(GlobalConfigurable):'
31 31 Any configurable traitlets this class exposed will be configurable in profiles
32 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 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 60 You should return modified nb, resources.
61 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 64 Parameters
65 65 ----------
@@ -72,13 +72,13 b' class ConfigurableTransformer(GlobalConfigurable):'
72 72 try :
73 73 for worksheet in nb.worksheets :
74 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 76 return nb, resources
77 77 except NotImplementedError:
78 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 83 Overwrite if you want to apply a transformation on each cell. You
84 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 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 import itertools
16 15
17 16 from IPython.utils.traitlets import Dict, Unicode
18 17 from .activatable import ActivatableTransformer
@@ -21,9 +20,10 b' from .activatable import ActivatableTransformer'
21 20 # Constants
22 21 #-----------------------------------------------------------------------------
23 22
24 FIGURES_KEY = "figures"
25 BINARY_KEY = "binary"
26 TEXT_KEY = "text"
23 # win64 is win32 for backwards compatability, for now. See
24 # http://mail.python.org/pipermail/patches/2000-May/000648.html
25 # for the original patch that this decision was made.
26 WINDOWS_PLATFORMS = ['win32']
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Classes
@@ -35,36 +35,12 b' class ExtractFigureTransformer(ActivatableTransformer):'
35 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
48 default_key_template = Unicode('_fig_{index:02d}.{ext}', config=True)
39 figure_filename_template = Unicode(
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 45 Apply a transformation on each cell,
70 46
@@ -75,69 +51,47 b' class ExtractFigureTransformer(ActivatableTransformer):'
75 51 resources : dictionary
76 52 Additional resources used in the conversion process. Allows
77 53 transformers to pass variables into the Jinja engine.
78 index : int
54 cell_index : int
79 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 :
83 resources[FIGURES_KEY] = {TEXT_KEY:{},BINARY_KEY:{}}
62 #Make sure figures key exists
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 70 for out_type in self.display_data_priority:
87
88 71 if out.hasattr(out_type):
89 figname, key, data, binary = self._new_figure(out[out_type], 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.
72 data = out[out_type]
103 73
104 Parameters
105 ----------
106 extension : str
107 File extension.
108 """
109
110 if extension in self.extra_extension_map :
111 return self.extra_extension_map[extension]
112
113 return extension
114
115
116 def _new_figure(self, data, format):
117 """Create a new figure file in the given format.
74 #Binary files are base64-encoded, SVG is already XML
75 if out_type in ('png', 'jpg', 'pdf'):
76 data = data.decode('base64')
77 elif sys.platform in WINDOWS_PLATFORMS:
78 data = data.replace('\n', '\r\n')
79
80 #Build a figure name
81 figure_name = self.figure_filename_template.format(
82 unique_key=unique_key,
83 cell_index=cell_index,
84 index=index,
85 extension=out_type)
86
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
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
97 return cell, resources
@@ -29,7 +29,7 b' class LatexTransformer(ActivatableTransformer):'
29 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 34 Apply a transformation on each cell,
35 35
@@ -39,9 +39,8 b' class RevealHelpTransformer(ConfigurableTransformer):'
39 39 transformers to pass variables into the Jinja engine.
40 40 """
41 41
42
43 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 45 #Make sure the cell has slideshow metadata.
47 46 cell.metadata.align_type = cell.get('metadata', {}).get('slideshow', {}).get('align_type', 'Left')
@@ -50,9 +49,9 b' class RevealHelpTransformer(ConfigurableTransformer):'
50 49 #Get the slide type. If type is start of subslide or slide,
51 50 #end the last subslide/slide.
52 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 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 57 if 'reveal' not in resources:
General Comments 0
You need to be logged in to leave comments. Login now