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