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