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