##// END OF EJS Templates
Catch ConversionExceptions in main nbconvert loop
David Wolever -
Show More
@@ -1,226 +1,235 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.config import catch_config_error, Configurable
26 from IPython.config import catch_config_error, Configurable
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 )
29 )
30 from IPython.utils.importstring import import_item
30 from IPython.utils.importstring import import_item
31
31
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 from IPython.nbconvert import exporters, transformers, writers
33 from IPython.nbconvert import exporters, transformers, writers
34 from .utils.base import NbConvertBase
34 from .utils.base import NbConvertBase
35 from .utils.exceptions import ConversionException
35
36
36 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
37 #Classes and functions
38 #Classes and functions
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
40 nbconvert_aliases = {}
41 nbconvert_aliases = {}
41 nbconvert_aliases.update(base_aliases)
42 nbconvert_aliases.update(base_aliases)
42 nbconvert_aliases.update({
43 nbconvert_aliases.update({
43 'format' : 'NbConvertApp.export_format',
44 'format' : 'NbConvertApp.export_format',
44 'notebooks' : 'NbConvertApp.notebooks',
45 'notebooks' : 'NbConvertApp.notebooks',
45 'writer' : 'NbConvertApp.writer_class',
46 'writer' : 'NbConvertApp.writer_class',
46 })
47 })
47
48
48 nbconvert_flags = {}
49 nbconvert_flags = {}
49 nbconvert_flags.update(base_flags)
50 nbconvert_flags.update(base_flags)
50 nbconvert_flags.update({
51 nbconvert_flags.update({
51 'stdout' : (
52 'stdout' : (
52 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
53 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
53 "Write notebook output to stdout instead of files."
54 "Write notebook output to stdout instead of files."
54 )
55 )
55 })
56 })
56
57
57
58
58 class NbConvertApp(BaseIPythonApplication):
59 class NbConvertApp(BaseIPythonApplication):
59 """Application used to convert to and from notebook file type (*.ipynb)"""
60 """Application used to convert to and from notebook file type (*.ipynb)"""
60
61
61 name = 'ipython-nbconvert'
62 name = 'ipython-nbconvert'
62 aliases = nbconvert_aliases
63 aliases = nbconvert_aliases
63 flags = nbconvert_flags
64 flags = nbconvert_flags
64
65
65 def _classes_default(self):
66 def _classes_default(self):
66 classes = [NbConvertBase]
67 classes = [NbConvertBase]
67 for pkg in (exporters, transformers, writers):
68 for pkg in (exporters, transformers, writers):
68 for name in dir(pkg):
69 for name in dir(pkg):
69 cls = getattr(pkg, name)
70 cls = getattr(pkg, name)
70 if isinstance(cls, type) and issubclass(cls, Configurable):
71 if isinstance(cls, type) and issubclass(cls, Configurable):
71 classes.append(cls)
72 classes.append(cls)
72 return classes
73 return classes
73
74
74 description = Unicode(
75 description = Unicode(
75 u"""This application is used to convert notebook files (*.ipynb)
76 u"""This application is used to convert notebook files (*.ipynb)
76 to various other formats.""")
77 to various other formats.""")
77
78
78 examples = Unicode(u"""
79 examples = Unicode(u"""
79 The simplest way to use nbconvert is
80 The simplest way to use nbconvert is
80
81
81 > ipython nbconvert mynotebook.ipynb
82 > ipython nbconvert mynotebook.ipynb
82
83
83 which will convert mynotebook.ipynb to the default format (probably HTML).
84 which will convert mynotebook.ipynb to the default format (probably HTML).
84
85
85 You can specify the export format with `--format`.
86 You can specify the export format with `--format`.
86 Options include {0}
87 Options include {0}
87
88
88 > ipython nbconvert --format latex mynotebook.ipnynb
89 > ipython nbconvert --format latex mynotebook.ipnynb
89
90
90 You can also pipe the output to stdout, rather than a file
91 You can also pipe the output to stdout, rather than a file
91
92
92 > ipython nbconvert mynotebook.ipynb --stdout
93 > ipython nbconvert mynotebook.ipynb --stdout
93
94
94 Multiple notebooks can be given at the command line in a couple of
95 Multiple notebooks can be given at the command line in a couple of
95 different ways:
96 different ways:
96
97
97 > ipython nbconvert notebook*.ipynb
98 > ipython nbconvert notebook*.ipynb
98 > ipython nbconvert notebook1.ipynb notebook2.ipynb
99 > ipython nbconvert notebook1.ipynb notebook2.ipynb
99
100
100 or you can specify the notebooks list in a config file, containing::
101 or you can specify the notebooks list in a config file, containing::
101
102
102 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
103 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
103
104
104 > ipython nbconvert --config mycfg.py
105 > ipython nbconvert --config mycfg.py
105 """.format(get_export_names()))
106 """.format(get_export_names()))
106 # Writer specific variables
107 # Writer specific variables
107 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
108 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
108 help="""Instance of the writer class used to write the
109 help="""Instance of the writer class used to write the
109 results of the conversion.""")
110 results of the conversion.""")
110 writer_class = DottedObjectName('FilesWriter', config=True,
111 writer_class = DottedObjectName('FilesWriter', config=True,
111 help="""Writer class used to write the
112 help="""Writer class used to write the
112 results of the conversion""")
113 results of the conversion""")
113 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
114 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
114 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
115 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
115 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
116 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
116 writer_factory = Type()
117 writer_factory = Type()
117
118
118 def _writer_class_changed(self, name, old, new):
119 def _writer_class_changed(self, name, old, new):
119 if new in self.writer_aliases:
120 if new in self.writer_aliases:
120 new = self.writer_aliases[new]
121 new = self.writer_aliases[new]
121 self.writer_factory = import_item(new)
122 self.writer_factory = import_item(new)
122
123
123
124
124 # Other configurable variables
125 # Other configurable variables
125 export_format = CaselessStrEnum(get_export_names(),
126 export_format = CaselessStrEnum(get_export_names(),
126 default_value="full_html",
127 default_value="full_html",
127 config=True,
128 config=True,
128 help="""The export format to be used."""
129 help="""The export format to be used."""
129 )
130 )
130
131
131 notebooks = List([], config=True, help="""List of notebooks to convert.
132 notebooks = List([], config=True, help="""List of notebooks to convert.
132 Wildcards are supported.
133 Wildcards are supported.
133 Filenames passed positionally will be added to the list.
134 Filenames passed positionally will be added to the list.
134 """)
135 """)
135
136
136 @catch_config_error
137 @catch_config_error
137 def initialize(self, argv=None):
138 def initialize(self, argv=None):
138 super(NbConvertApp, self).initialize(argv)
139 super(NbConvertApp, self).initialize(argv)
139 self.init_notebooks()
140 self.init_notebooks()
140 self.init_writer()
141 self.init_writer()
141
142
142 def init_notebooks(self):
143 def init_notebooks(self):
143 """Construct the list of notebooks.
144 """Construct the list of notebooks.
144 If notebooks are passed on the command-line,
145 If notebooks are passed on the command-line,
145 they override notebooks specified in config files.
146 they override notebooks specified in config files.
146 Glob each notebook to replace notebook patterns with filenames.
147 Glob each notebook to replace notebook patterns with filenames.
147 """
148 """
148
149
149 # Specifying notebooks on the command-line overrides (rather than adds)
150 # Specifying notebooks on the command-line overrides (rather than adds)
150 # the notebook list
151 # the notebook list
151 if self.extra_args:
152 if self.extra_args:
152 patterns = self.extra_args
153 patterns = self.extra_args
153 else:
154 else:
154 patterns = self.notebooks
155 patterns = self.notebooks
155
156
156 # Use glob to replace all the notebook patterns with filenames.
157 # Use glob to replace all the notebook patterns with filenames.
157 filenames = []
158 filenames = []
158 for pattern in patterns:
159 for pattern in patterns:
159
160
160 # Use glob to find matching filenames. Allow the user to convert
161 # Use glob to find matching filenames. Allow the user to convert
161 # notebooks without having to type the extension.
162 # notebooks without having to type the extension.
162 globbed_files = glob.glob(pattern)
163 globbed_files = glob.glob(pattern)
163 globbed_files.extend(glob.glob(pattern + '.ipynb'))
164 globbed_files.extend(glob.glob(pattern + '.ipynb'))
164
165
165 for filename in globbed_files:
166 for filename in globbed_files:
166 if not filename in filenames:
167 if not filename in filenames:
167 filenames.append(filename)
168 filenames.append(filename)
168 self.notebooks = filenames
169 self.notebooks = filenames
169
170
170 def init_writer(self):
171 def init_writer(self):
171 """
172 """
172 Initialize the writer (which is stateless)
173 Initialize the writer (which is stateless)
173 """
174 """
174 self._writer_class_changed(None, self.writer_class, self.writer_class)
175 self._writer_class_changed(None, self.writer_class, self.writer_class)
175 self.writer = self.writer_factory(parent=self)
176 self.writer = self.writer_factory(parent=self)
176
177
177 def start(self):
178 def start(self):
178 """
179 """
179 Ran after initialization completed
180 Ran after initialization completed
180 """
181 """
181 super(NbConvertApp, self).start()
182 super(NbConvertApp, self).start()
182 self.convert_notebooks()
183 self.convert_notebooks()
183
184
184 def convert_notebooks(self):
185 def convert_notebooks(self):
185 """
186 """
186 Convert the notebooks in the self.notebook traitlet
187 Convert the notebooks in the self.notebook traitlet
187 """
188 """
188 # Export each notebook
189 # Export each notebook
189 conversion_success = 0
190 conversion_success = 0
190 for notebook_filename in self.notebooks:
191 for notebook_filename in self.notebooks:
191
192
192 # Get a unique key for the notebook and set it in the resources object.
193 # Get a unique key for the notebook and set it in the resources object.
193 basename = os.path.basename(notebook_filename)
194 basename = os.path.basename(notebook_filename)
194 notebook_name = basename[:basename.rfind('.')]
195 notebook_name = basename[:basename.rfind('.')]
195 resources = {}
196 resources = {}
196 resources['unique_key'] = notebook_name
197 resources['unique_key'] = notebook_name
197 resources['output_files_dir'] = '%s_files' % notebook_name
198 resources['output_files_dir'] = '%s_files' % notebook_name
198
199
199 # Try to export
200 # Try to export
200 try:
201 try:
201 output, resources = export_by_name(self.export_format,
202 output, resources = export_by_name(self.export_format,
202 notebook_filename,
203 notebook_filename,
203 resources=resources,
204 resources=resources,
204 config=self.config)
205 config=self.config)
205 except ExporterNameError as e:
206 except ExporterNameError as e:
206 print("Error: '%s' exporter not found." % self.export_format,
207 print("Error while converting '%s': '%s' exporter not found."
208 %(notebook_filename, self.export_format),
207 file=sys.stderr)
209 file=sys.stderr)
208 print("Known exporters are:",
210 print("Known exporters are:",
209 "\n\t" + "\n\t".join(get_export_names()),
211 "\n\t" + "\n\t".join(get_export_names()),
210 file=sys.stderr)
212 file=sys.stderr)
211 sys.exit(-1)
213 sys.exit(-1)
214 except ConversionException as e:
215 print("Error while converting '%s': %s" %(notebook_filename, e),
216 file=sys.stderr)
217 sys.exit(-1)
218 # except Exception as e:
219 # print("Error: could not export '%s'" % notebook_filename, file=sys.stderr)
220 # print(e, file=sys.stderr)
212 else:
221 else:
213 self.writer.write(output, resources, notebook_name=notebook_name)
222 self.writer.write(output, resources, notebook_name=notebook_name)
214 conversion_success += 1
223 conversion_success += 1
215
224
216 # If nothing was converted successfully, help the user.
225 # If nothing was converted successfully, help the user.
217 if conversion_success == 0:
226 if conversion_success == 0:
218 self.print_help()
227 self.print_help()
219 sys.exit(-1)
228 sys.exit(-1)
220
229
221
230
222 #-----------------------------------------------------------------------------
231 #-----------------------------------------------------------------------------
223 # Main entry point
232 # Main entry point
224 #-----------------------------------------------------------------------------
233 #-----------------------------------------------------------------------------
225
234
226 launch_new_instance = NbConvertApp.launch_instance
235 launch_new_instance = NbConvertApp.launch_instance
@@ -1,17 +1,17 b''
1 """NbConvert specific exceptions"""
1 """NbConvert specific exceptions"""
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Copyright (c) 2013, the IPython Development Team.
3 # Copyright (c) 2013, the IPython Development Team.
4 #
4 #
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6 #
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Classes and functions
11 # Classes and functions
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 class ConversionException(Exception):
14 class ConversionException(Exception):
15 """An exception raised by the conversion process."""
15 """An exception raised by the conversion process."""
16
16
17 pass No newline at end of file
17 pass
@@ -1,71 +1,69 b''
1 """Utility for calling pandoc"""
1 """Utility for calling pandoc"""
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Copyright (c) 2013 the IPython Development Team.
3 # Copyright (c) 2013 the IPython Development Team.
4 #
4 #
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6 #
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import subprocess
17 import subprocess
18
18
19 # IPython imports
19 # IPython imports
20 from IPython.utils.py3compat import cast_bytes
20 from IPython.utils.py3compat import cast_bytes
21
21
22 from .exceptions import ConversionException
23
22 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
23 # Classes and functions
25 # Classes and functions
24 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
25
27
26 class PandocMissing(SystemExit):
28 class PandocMissing(ConversionException):
27 """Exception raised when Pandoc is missing.
29 """Exception raised when Pandoc is missing. """
28
29 A subclass of SystemExit so it will cause an exit if not caught, but
30 explicitly named so that it's possible to catch.
31 """
32 pass
30 pass
33
31
34
32
35 def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):
33 def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):
36 """Convert an input string in format `from` to format `to` via pandoc.
34 """Convert an input string in format `from` to format `to` via pandoc.
37
35
38 This function will raise an error if pandoc is not installed.
36 This function will raise an error if pandoc is not installed.
39 Any error messages generated by pandoc are printed to stderr.
37 Any error messages generated by pandoc are printed to stderr.
40
38
41 Parameters
39 Parameters
42 ----------
40 ----------
43 source : string
41 source : string
44 Input string, assumed to be valid format `from`.
42 Input string, assumed to be valid format `from`.
45 fmt : string
43 fmt : string
46 The name of the input format (markdown, etc.)
44 The name of the input format (markdown, etc.)
47 to : string
45 to : string
48 The name of the output format (html, etc.)
46 The name of the output format (html, etc.)
49
47
50 Returns
48 Returns
51 -------
49 -------
52 out : unicode
50 out : unicode
53 Output as returned by pandoc.
51 Output as returned by pandoc.
54 """
52 """
55 command = ['pandoc', '-f', fmt, '-t', to]
53 command = ['pandoc', '-f', fmt, '-t', to]
56 if extra_args:
54 if extra_args:
57 command.extend(extra_args)
55 command.extend(extra_args)
58 try:
56 try:
59 p = subprocess.Popen(command,
57 p = subprocess.Popen(command,
60 stdin=subprocess.PIPE, stdout=subprocess.PIPE
58 stdin=subprocess.PIPE, stdout=subprocess.PIPE
61 )
59 )
62 except OSError as e:
60 except OSError as e:
63 raise PandocMissing(
61 raise PandocMissing(
64 "Error trying to run '%s': %s.\n" %(" ".join(command), e) +
62 "The command '%s' returned an error: %s.\n" %(" ".join(command), e) +
65 "Please check that pandoc is installed:\n" +
63 "Please check that pandoc is installed:\n" +
66 "http://johnmacfarlane.net/pandoc/installing.html"
64 "http://johnmacfarlane.net/pandoc/installing.html"
67 )
65 )
68 out, _ = p.communicate(cast_bytes(source, encoding))
66 out, _ = p.communicate(cast_bytes(source, encoding))
69 out = out.decode(encoding, 'replace')
67 out = out.decode(encoding, 'replace')
70 return out[:-1]
68 return out[:-1]
71
69
General Comments 0
You need to be logged in to leave comments. Login now