##// END OF EJS Templates
support downgrading notebooks with nbconvert...
MinRK -
Show More
@@ -1,10 +1,11
1 1 from .export import *
2 2 from .html import HTMLExporter
3 3 from .slides import SlidesExporter
4 4 from .templateexporter import TemplateExporter
5 5 from .latex import LatexExporter
6 6 from .markdown import MarkdownExporter
7 from .notebook import NotebookExporter
7 8 from .pdf import PDFExporter
8 9 from .python import PythonExporter
9 10 from .rst import RSTExporter
10 11 from .exporter import Exporter
@@ -1,21 +1,32
1 1 """NotebookExporter class"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from .exporter import Exporter
7 7 from IPython.nbformat import current as nbformat
8 from IPython.utils.traitlets import Enum
8 9
9 10 class NotebookExporter(Exporter):
11 """Exports to an IPython notebook."""
12
13 nbformat_version = Enum(list(range(2, nbformat.current_nbformat + 1)),
14 default_value=nbformat.current_nbformat,
15 config=True,
16 help="""The nbformat version to write.
17 Use this to downgrade notebooks.
10 18 """
11 Exports an IPython notebook.
12 """
19 )
13 20 def _file_extension_default(self):
14 21 return 'ipynb'
15 22
16 23 output_mimetype = 'application/json'
17 24
18 25 def from_notebook_node(self, nb, resources=None, **kw):
19 26 nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw)
20 output = nbformat.writes_json(nb_copy)
27 if self.nbformat_version != nbformat.current_nbformat:
28 resources['output_suffix'] = '.v%i' % self.nbformat_version
29 else:
30 resources['output_suffix'] = '.nbconvert'
31 output = nbformat.writes(nb_copy, version=self.nbformat_version)
21 32 return output, resources
@@ -1,22 +1,36
1 1 """Tests for notebook.py"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 import json
7
6 8 from .base import ExportersTestsBase
7 9 from ..notebook import NotebookExporter
8 10
9 11 class TestNotebookExporter(ExportersTestsBase):
10 12 """Contains test functions for notebook.py"""
11 13
12 14 exporter_class = NotebookExporter
13 15
14 16 def test_export(self):
15 17 """
16 18 Does the NotebookExporter return the file unchanged?
17 19 """
18 20 with open(self._get_notebook()) as f:
19 21 file_contents = f.read()
20 (output, resources) = NotebookExporter().from_filename(self._get_notebook())
22 (output, resources) = self.exporter_class().from_filename(self._get_notebook())
21 23 assert len(output) > 0
22 assert output == file_contents
24 self.assertEqual(output, file_contents)
25
26 def test_downgrade_3(self):
27 exporter = self.exporter_class(nbformat_version=3)
28 (output, resources) = exporter.from_filename(self._get_notebook())
29 nb = json.loads(output)
30 self.assertEqual(nb['nbformat'], 3)
31
32 def test_downgrade_2(self):
33 exporter = self.exporter_class(nbformat_version=2)
34 (output, resources) = exporter.from_filename(self._get_notebook())
35 nb = json.loads(output)
36 self.assertEqual(nb['nbformat'], 2)
@@ -1,317 +1,320
1 1 #!/usr/bin/env python
2 2 """NbConvert is a utility for conversion of .ipynb files.
3 3
4 4 Command-line interface for the NbConvert conversion utility.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 from __future__ import print_function
11 11
12 12 import logging
13 13 import sys
14 14 import os
15 15 import glob
16 16
17 17 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
18 18 from IPython.core.profiledir import ProfileDir
19 19 from IPython.config import catch_config_error, Configurable
20 20 from IPython.utils.traitlets import (
21 21 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
22 22 )
23 23 from IPython.utils.importstring import import_item
24 24
25 25 from .exporters.export import get_export_names, exporter_map
26 26 from IPython.nbconvert import exporters, preprocessors, writers, postprocessors
27 27 from .utils.base import NbConvertBase
28 28 from .utils.exceptions import ConversionException
29 29
30 30 #-----------------------------------------------------------------------------
31 31 #Classes and functions
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class DottedOrNone(DottedObjectName):
35 35 """
36 36 A string holding a valid dotted object name in Python, such as A.b3._c
37 37 Also allows for None type."""
38 38
39 39 default_value = u''
40 40
41 41 def validate(self, obj, value):
42 42 if value is not None and len(value) > 0:
43 43 return super(DottedOrNone, self).validate(obj, value)
44 44 else:
45 45 return value
46 46
47 47 nbconvert_aliases = {}
48 48 nbconvert_aliases.update(base_aliases)
49 49 nbconvert_aliases.update({
50 50 'to' : 'NbConvertApp.export_format',
51 51 'template' : 'TemplateExporter.template_file',
52 52 'writer' : 'NbConvertApp.writer_class',
53 53 'post': 'NbConvertApp.postprocessor_class',
54 54 'output': 'NbConvertApp.output_base',
55 55 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
56 'nbformat': 'NotebookExporter.nbformat_version',
56 57 })
57 58
58 59 nbconvert_flags = {}
59 60 nbconvert_flags.update(base_flags)
60 61 nbconvert_flags.update({
61 62 'stdout' : (
62 63 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
63 64 "Write notebook output to stdout instead of files."
64 65 )
65 66 })
66 67
67 68
68 69 class NbConvertApp(BaseIPythonApplication):
69 70 """Application used to convert from notebook file type (``*.ipynb``)"""
70 71
71 72 name = 'ipython-nbconvert'
72 73 aliases = nbconvert_aliases
73 74 flags = nbconvert_flags
74 75
75 76 def _log_level_default(self):
76 77 return logging.INFO
77 78
78 79 def _classes_default(self):
79 80 classes = [NbConvertBase, ProfileDir]
80 81 for pkg in (exporters, preprocessors, writers, postprocessors):
81 82 for name in dir(pkg):
82 83 cls = getattr(pkg, name)
83 84 if isinstance(cls, type) and issubclass(cls, Configurable):
84 85 classes.append(cls)
85 86
86 87 return classes
87 88
88 89 description = Unicode(
89 90 u"""This application is used to convert notebook files (*.ipynb)
90 91 to various other formats.
91 92
92 93 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
93 94
94 95 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
95 can only be use when converting one notebook at a time.
96 can only be used when converting one notebook at a time.
96 97 ''')
97 98
98 99 examples = Unicode(u"""
99 100 The simplest way to use nbconvert is
100 101
101 102 > ipython nbconvert mynotebook.ipynb
102 103
103 104 which will convert mynotebook.ipynb to the default format (probably HTML).
104 105
105 106 You can specify the export format with `--to`.
106 107 Options include {0}
107 108
108 109 > ipython nbconvert --to latex mynotebook.ipynb
109 110
110 111 Both HTML and LaTeX support multiple output templates. LaTeX includes
111 112 'base', 'article' and 'report'. HTML includes 'basic' and 'full'. You
112 113 can specify the flavor of the format used.
113 114
114 115 > ipython nbconvert --to html --template basic mynotebook.ipynb
115 116
116 117 You can also pipe the output to stdout, rather than a file
117 118
118 119 > ipython nbconvert mynotebook.ipynb --stdout
119 120
120 121 PDF is generated via latex
121 122
122 123 > ipython nbconvert mynotebook.ipynb --to pdf
123 124
124 125 You can get (and serve) a Reveal.js-powered slideshow
125 126
126 127 > ipython nbconvert myslides.ipynb --to slides --post serve
127 128
128 129 Multiple notebooks can be given at the command line in a couple of
129 130 different ways:
130 131
131 132 > ipython nbconvert notebook*.ipynb
132 133 > ipython nbconvert notebook1.ipynb notebook2.ipynb
133 134
134 135 or you can specify the notebooks list in a config file, containing::
135 136
136 137 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
137 138
138 139 > ipython nbconvert --config mycfg.py
139 140 """.format(get_export_names()))
140 141
141 142 # Writer specific variables
142 143 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
143 144 help="""Instance of the writer class used to write the
144 145 results of the conversion.""")
145 146 writer_class = DottedObjectName('FilesWriter', config=True,
146 147 help="""Writer class used to write the
147 148 results of the conversion""")
148 149 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
149 150 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
150 151 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
151 152 writer_factory = Type()
152 153
153 154 def _writer_class_changed(self, name, old, new):
154 155 if new.lower() in self.writer_aliases:
155 156 new = self.writer_aliases[new.lower()]
156 157 self.writer_factory = import_item(new)
157 158
158 159 # Post-processor specific variables
159 160 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
160 161 help="""Instance of the PostProcessor class used to write the
161 162 results of the conversion.""")
162 163
163 164 postprocessor_class = DottedOrNone(config=True,
164 165 help="""PostProcessor class used to write the
165 166 results of the conversion""")
166 167 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
167 168 postprocessor_factory = Type()
168 169
169 170 def _postprocessor_class_changed(self, name, old, new):
170 171 if new.lower() in self.postprocessor_aliases:
171 172 new = self.postprocessor_aliases[new.lower()]
172 173 if new:
173 174 self.postprocessor_factory = import_item(new)
174 175
175 176
176 177 # Other configurable variables
177 178 export_format = CaselessStrEnum(get_export_names(),
178 179 default_value="html",
179 180 config=True,
180 181 help="""The export format to be used."""
181 182 )
182 183
183 184 notebooks = List([], config=True, help="""List of notebooks to convert.
184 185 Wildcards are supported.
185 186 Filenames passed positionally will be added to the list.
186 187 """)
187 188
188 189 @catch_config_error
189 190 def initialize(self, argv=None):
190 191 self.init_syspath()
191 192 super(NbConvertApp, self).initialize(argv)
192 193 self.init_notebooks()
193 194 self.init_writer()
194 195 self.init_postprocessor()
195 196
196 197
197 198
198 199 def init_syspath(self):
199 200 """
200 201 Add the cwd to the sys.path ($PYTHONPATH)
201 202 """
202 203 sys.path.insert(0, os.getcwd())
203 204
204 205
205 206 def init_notebooks(self):
206 207 """Construct the list of notebooks.
207 208 If notebooks are passed on the command-line,
208 209 they override notebooks specified in config files.
209 210 Glob each notebook to replace notebook patterns with filenames.
210 211 """
211 212
212 213 # Specifying notebooks on the command-line overrides (rather than adds)
213 214 # the notebook list
214 215 if self.extra_args:
215 216 patterns = self.extra_args
216 217 else:
217 218 patterns = self.notebooks
218 219
219 220 # Use glob to replace all the notebook patterns with filenames.
220 221 filenames = []
221 222 for pattern in patterns:
222 223
223 224 # Use glob to find matching filenames. Allow the user to convert
224 225 # notebooks without having to type the extension.
225 226 globbed_files = glob.glob(pattern)
226 227 globbed_files.extend(glob.glob(pattern + '.ipynb'))
227 228 if not globbed_files:
228 229 self.log.warn("pattern %r matched no files", pattern)
229 230
230 231 for filename in globbed_files:
231 232 if not filename in filenames:
232 233 filenames.append(filename)
233 234 self.notebooks = filenames
234 235
235 236 def init_writer(self):
236 237 """
237 238 Initialize the writer (which is stateless)
238 239 """
239 240 self._writer_class_changed(None, self.writer_class, self.writer_class)
240 241 self.writer = self.writer_factory(parent=self)
241 242
242 243 def init_postprocessor(self):
243 244 """
244 245 Initialize the postprocessor (which is stateless)
245 246 """
246 247 self._postprocessor_class_changed(None, self.postprocessor_class,
247 248 self.postprocessor_class)
248 249 if self.postprocessor_factory:
249 250 self.postprocessor = self.postprocessor_factory(parent=self)
250 251
251 252 def start(self):
252 253 """
253 254 Ran after initialization completed
254 255 """
255 256 super(NbConvertApp, self).start()
256 257 self.convert_notebooks()
257 258
258 259 def convert_notebooks(self):
259 260 """
260 261 Convert the notebooks in the self.notebook traitlet
261 262 """
262 263 # Export each notebook
263 264 conversion_success = 0
264 265
265 266 if self.output_base != '' and len(self.notebooks) > 1:
266 267 self.log.error(
267 268 """UsageError: --output flag or `NbConvertApp.output_base` config option
268 269 cannot be used when converting multiple notebooks.
269 270 """)
270 271 self.exit(1)
271 272
272 273 exporter = exporter_map[self.export_format](config=self.config)
273 274
274 275 for notebook_filename in self.notebooks:
275 276 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
276 277
277 278 # Get a unique key for the notebook and set it in the resources object.
278 279 basename = os.path.basename(notebook_filename)
279 280 notebook_name = basename[:basename.rfind('.')]
280 281 if self.output_base:
281 282 # strip duplicate extension from output_base, to avoid Basname.ext.ext
282 283 if getattr(exporter, 'file_extension', False):
283 284 base, ext = os.path.splitext(self.output_base)
284 285 if ext == '.' + exporter.file_extension:
285 286 self.output_base = base
286 287 notebook_name = self.output_base
287 288 resources = {}
288 289 resources['profile_dir'] = self.profile_dir.location
289 290 resources['unique_key'] = notebook_name
290 291 resources['output_files_dir'] = '%s_files' % notebook_name
291 292 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
292 293
293 294 # Try to export
294 295 try:
295 296 output, resources = exporter.from_filename(notebook_filename, resources=resources)
296 297 except ConversionException as e:
297 298 self.log.error("Error while converting '%s'", notebook_filename,
298 299 exc_info=True)
299 300 self.exit(1)
300 301 else:
302 if 'output_suffix' in resources and not self.output_base:
303 notebook_name += resources['output_suffix']
301 304 write_results = self.writer.write(output, resources, notebook_name=notebook_name)
302 305
303 306 #Post-process if post processor has been defined.
304 307 if hasattr(self, 'postprocessor') and self.postprocessor:
305 308 self.postprocessor(write_results)
306 309 conversion_success += 1
307 310
308 311 # If nothing was converted successfully, help the user.
309 312 if conversion_success == 0:
310 313 self.print_help()
311 314 sys.exit(-1)
312 315
313 316 #-----------------------------------------------------------------------------
314 317 # Main entry point
315 318 #-----------------------------------------------------------------------------
316 319
317 320 launch_new_instance = NbConvertApp.launch_instance
General Comments 0
You need to be logged in to leave comments. Login now