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