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