##// END OF EJS Templates
Merge pull request #6067 from minrk/output_prefix...
Thomas Kluyver -
r17313:045d4856 merge
parent child Browse files
Show More
@@ -1,312 +1,317 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 })
56 })
57
57
58 nbconvert_flags = {}
58 nbconvert_flags = {}
59 nbconvert_flags.update(base_flags)
59 nbconvert_flags.update(base_flags)
60 nbconvert_flags.update({
60 nbconvert_flags.update({
61 'stdout' : (
61 'stdout' : (
62 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
62 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
63 "Write notebook output to stdout instead of files."
63 "Write notebook output to stdout instead of files."
64 )
64 )
65 })
65 })
66
66
67
67
68 class NbConvertApp(BaseIPythonApplication):
68 class NbConvertApp(BaseIPythonApplication):
69 """Application used to convert from notebook file type (``*.ipynb``)"""
69 """Application used to convert from notebook file type (``*.ipynb``)"""
70
70
71 name = 'ipython-nbconvert'
71 name = 'ipython-nbconvert'
72 aliases = nbconvert_aliases
72 aliases = nbconvert_aliases
73 flags = nbconvert_flags
73 flags = nbconvert_flags
74
74
75 def _log_level_default(self):
75 def _log_level_default(self):
76 return logging.INFO
76 return logging.INFO
77
77
78 def _classes_default(self):
78 def _classes_default(self):
79 classes = [NbConvertBase, ProfileDir]
79 classes = [NbConvertBase, ProfileDir]
80 for pkg in (exporters, preprocessors, writers, postprocessors):
80 for pkg in (exporters, preprocessors, writers, postprocessors):
81 for name in dir(pkg):
81 for name in dir(pkg):
82 cls = getattr(pkg, name)
82 cls = getattr(pkg, name)
83 if isinstance(cls, type) and issubclass(cls, Configurable):
83 if isinstance(cls, type) and issubclass(cls, Configurable):
84 classes.append(cls)
84 classes.append(cls)
85
85
86 return classes
86 return classes
87
87
88 description = Unicode(
88 description = Unicode(
89 u"""This application is used to convert notebook files (*.ipynb)
89 u"""This application is used to convert notebook files (*.ipynb)
90 to various other formats.
90 to various other formats.
91
91
92 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
92 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
93
93
94 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
94 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
95 can only be use when converting one notebook at a time.
95 can only be use when converting one notebook at a time.
96 ''')
96 ''')
97
97
98 examples = Unicode(u"""
98 examples = Unicode(u"""
99 The simplest way to use nbconvert is
99 The simplest way to use nbconvert is
100
100
101 > ipython nbconvert mynotebook.ipynb
101 > ipython nbconvert mynotebook.ipynb
102
102
103 which will convert mynotebook.ipynb to the default format (probably HTML).
103 which will convert mynotebook.ipynb to the default format (probably HTML).
104
104
105 You can specify the export format with `--to`.
105 You can specify the export format with `--to`.
106 Options include {0}
106 Options include {0}
107
107
108 > ipython nbconvert --to latex mynotebook.ipynb
108 > ipython nbconvert --to latex mynotebook.ipynb
109
109
110 Both HTML and LaTeX support multiple output templates. LaTeX includes
110 Both HTML and LaTeX support multiple output templates. LaTeX includes
111 'base', 'article' and 'report'. HTML includes 'basic' and 'full'. You
111 'base', 'article' and 'report'. HTML includes 'basic' and 'full'. You
112 can specify the flavor of the format used.
112 can specify the flavor of the format used.
113
113
114 > ipython nbconvert --to html --template basic mynotebook.ipynb
114 > ipython nbconvert --to html --template basic mynotebook.ipynb
115
115
116 You can also pipe the output to stdout, rather than a file
116 You can also pipe the output to stdout, rather than a file
117
117
118 > ipython nbconvert mynotebook.ipynb --stdout
118 > ipython nbconvert mynotebook.ipynb --stdout
119
119
120 PDF is generated via latex
120 PDF is generated via latex
121
121
122 > ipython nbconvert mynotebook.ipynb --to pdf
122 > ipython nbconvert mynotebook.ipynb --to pdf
123
123
124 You can get (and serve) a Reveal.js-powered slideshow
124 You can get (and serve) a Reveal.js-powered slideshow
125
125
126 > ipython nbconvert myslides.ipynb --to slides --post serve
126 > ipython nbconvert myslides.ipynb --to slides --post serve
127
127
128 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
129 different ways:
129 different ways:
130
130
131 > ipython nbconvert notebook*.ipynb
131 > ipython nbconvert notebook*.ipynb
132 > ipython nbconvert notebook1.ipynb notebook2.ipynb
132 > ipython nbconvert notebook1.ipynb notebook2.ipynb
133
133
134 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::
135
135
136 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
136 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
137
137
138 > ipython nbconvert --config mycfg.py
138 > ipython nbconvert --config mycfg.py
139 """.format(get_export_names()))
139 """.format(get_export_names()))
140
140
141 # Writer specific variables
141 # Writer specific variables
142 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
142 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
143 help="""Instance of the writer class used to write the
143 help="""Instance of the writer class used to write the
144 results of the conversion.""")
144 results of the conversion.""")
145 writer_class = DottedObjectName('FilesWriter', config=True,
145 writer_class = DottedObjectName('FilesWriter', config=True,
146 help="""Writer class used to write the
146 help="""Writer class used to write the
147 results of the conversion""")
147 results of the conversion""")
148 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
148 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
149 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
149 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
150 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
150 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
151 writer_factory = Type()
151 writer_factory = Type()
152
152
153 def _writer_class_changed(self, name, old, new):
153 def _writer_class_changed(self, name, old, new):
154 if new.lower() in self.writer_aliases:
154 if new.lower() in self.writer_aliases:
155 new = self.writer_aliases[new.lower()]
155 new = self.writer_aliases[new.lower()]
156 self.writer_factory = import_item(new)
156 self.writer_factory = import_item(new)
157
157
158 # Post-processor specific variables
158 # Post-processor specific variables
159 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
159 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
160 help="""Instance of the PostProcessor class used to write the
160 help="""Instance of the PostProcessor class used to write the
161 results of the conversion.""")
161 results of the conversion.""")
162
162
163 postprocessor_class = DottedOrNone(config=True,
163 postprocessor_class = DottedOrNone(config=True,
164 help="""PostProcessor class used to write the
164 help="""PostProcessor class used to write the
165 results of the conversion""")
165 results of the conversion""")
166 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
166 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
167 postprocessor_factory = Type()
167 postprocessor_factory = Type()
168
168
169 def _postprocessor_class_changed(self, name, old, new):
169 def _postprocessor_class_changed(self, name, old, new):
170 if new.lower() in self.postprocessor_aliases:
170 if new.lower() in self.postprocessor_aliases:
171 new = self.postprocessor_aliases[new.lower()]
171 new = self.postprocessor_aliases[new.lower()]
172 if new:
172 if new:
173 self.postprocessor_factory = import_item(new)
173 self.postprocessor_factory = import_item(new)
174
174
175
175
176 # Other configurable variables
176 # Other configurable variables
177 export_format = CaselessStrEnum(get_export_names(),
177 export_format = CaselessStrEnum(get_export_names(),
178 default_value="html",
178 default_value="html",
179 config=True,
179 config=True,
180 help="""The export format to be used."""
180 help="""The export format to be used."""
181 )
181 )
182
182
183 notebooks = List([], config=True, help="""List of notebooks to convert.
183 notebooks = List([], config=True, help="""List of notebooks to convert.
184 Wildcards are supported.
184 Wildcards are supported.
185 Filenames passed positionally will be added to the list.
185 Filenames passed positionally will be added to the list.
186 """)
186 """)
187
187
188 @catch_config_error
188 @catch_config_error
189 def initialize(self, argv=None):
189 def initialize(self, argv=None):
190 self.init_syspath()
190 self.init_syspath()
191 super(NbConvertApp, self).initialize(argv)
191 super(NbConvertApp, self).initialize(argv)
192 self.init_notebooks()
192 self.init_notebooks()
193 self.init_writer()
193 self.init_writer()
194 self.init_postprocessor()
194 self.init_postprocessor()
195
195
196
196
197
197
198 def init_syspath(self):
198 def init_syspath(self):
199 """
199 """
200 Add the cwd to the sys.path ($PYTHONPATH)
200 Add the cwd to the sys.path ($PYTHONPATH)
201 """
201 """
202 sys.path.insert(0, os.getcwd())
202 sys.path.insert(0, os.getcwd())
203
203
204
204
205 def init_notebooks(self):
205 def init_notebooks(self):
206 """Construct the list of notebooks.
206 """Construct the list of notebooks.
207 If notebooks are passed on the command-line,
207 If notebooks are passed on the command-line,
208 they override notebooks specified in config files.
208 they override notebooks specified in config files.
209 Glob each notebook to replace notebook patterns with filenames.
209 Glob each notebook to replace notebook patterns with filenames.
210 """
210 """
211
211
212 # Specifying notebooks on the command-line overrides (rather than adds)
212 # Specifying notebooks on the command-line overrides (rather than adds)
213 # the notebook list
213 # the notebook list
214 if self.extra_args:
214 if self.extra_args:
215 patterns = self.extra_args
215 patterns = self.extra_args
216 else:
216 else:
217 patterns = self.notebooks
217 patterns = self.notebooks
218
218
219 # Use glob to replace all the notebook patterns with filenames.
219 # Use glob to replace all the notebook patterns with filenames.
220 filenames = []
220 filenames = []
221 for pattern in patterns:
221 for pattern in patterns:
222
222
223 # Use glob to find matching filenames. Allow the user to convert
223 # Use glob to find matching filenames. Allow the user to convert
224 # notebooks without having to type the extension.
224 # notebooks without having to type the extension.
225 globbed_files = glob.glob(pattern)
225 globbed_files = glob.glob(pattern)
226 globbed_files.extend(glob.glob(pattern + '.ipynb'))
226 globbed_files.extend(glob.glob(pattern + '.ipynb'))
227 if not globbed_files:
227 if not globbed_files:
228 self.log.warn("pattern %r matched no files", pattern)
228 self.log.warn("pattern %r matched no files", pattern)
229
229
230 for filename in globbed_files:
230 for filename in globbed_files:
231 if not filename in filenames:
231 if not filename in filenames:
232 filenames.append(filename)
232 filenames.append(filename)
233 self.notebooks = filenames
233 self.notebooks = filenames
234
234
235 def init_writer(self):
235 def init_writer(self):
236 """
236 """
237 Initialize the writer (which is stateless)
237 Initialize the writer (which is stateless)
238 """
238 """
239 self._writer_class_changed(None, self.writer_class, self.writer_class)
239 self._writer_class_changed(None, self.writer_class, self.writer_class)
240 self.writer = self.writer_factory(parent=self)
240 self.writer = self.writer_factory(parent=self)
241
241
242 def init_postprocessor(self):
242 def init_postprocessor(self):
243 """
243 """
244 Initialize the postprocessor (which is stateless)
244 Initialize the postprocessor (which is stateless)
245 """
245 """
246 self._postprocessor_class_changed(None, self.postprocessor_class,
246 self._postprocessor_class_changed(None, self.postprocessor_class,
247 self.postprocessor_class)
247 self.postprocessor_class)
248 if self.postprocessor_factory:
248 if self.postprocessor_factory:
249 self.postprocessor = self.postprocessor_factory(parent=self)
249 self.postprocessor = self.postprocessor_factory(parent=self)
250
250
251 def start(self):
251 def start(self):
252 """
252 """
253 Ran after initialization completed
253 Ran after initialization completed
254 """
254 """
255 super(NbConvertApp, self).start()
255 super(NbConvertApp, self).start()
256 self.convert_notebooks()
256 self.convert_notebooks()
257
257
258 def convert_notebooks(self):
258 def convert_notebooks(self):
259 """
259 """
260 Convert the notebooks in the self.notebook traitlet
260 Convert the notebooks in the self.notebook traitlet
261 """
261 """
262 # Export each notebook
262 # Export each notebook
263 conversion_success = 0
263 conversion_success = 0
264
264
265 if self.output_base != '' and len(self.notebooks) > 1:
265 if self.output_base != '' and len(self.notebooks) > 1:
266 self.log.error(
266 self.log.error(
267 """UsageError: --output flag or `NbConvertApp.output_base` config option
267 """UsageError: --output flag or `NbConvertApp.output_base` config option
268 cannot be used when converting multiple notebooks.
268 cannot be used when converting multiple notebooks.
269 """)
269 """)
270 self.exit(1)
270 self.exit(1)
271
271
272 exporter = exporter_map[self.export_format](config=self.config)
272 exporter = exporter_map[self.export_format](config=self.config)
273
273
274 for notebook_filename in self.notebooks:
274 for notebook_filename in self.notebooks:
275 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
275 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
276
276
277 # Get a unique key for the notebook and set it in the resources object.
277 # Get a unique key for the notebook and set it in the resources object.
278 basename = os.path.basename(notebook_filename)
278 basename = os.path.basename(notebook_filename)
279 notebook_name = basename[:basename.rfind('.')]
279 notebook_name = basename[:basename.rfind('.')]
280 if self.output_base:
280 if self.output_base:
281 # strip duplicate extension from output_base, to avoid Basname.ext.ext
282 if getattr(exporter, 'file_extension', False):
283 base, ext = os.path.splitext(self.output_base)
284 if ext == '.' + exporter.file_extension:
285 self.output_base = base
281 notebook_name = self.output_base
286 notebook_name = self.output_base
282 resources = {}
287 resources = {}
283 resources['profile_dir'] = self.profile_dir.location
288 resources['profile_dir'] = self.profile_dir.location
284 resources['unique_key'] = notebook_name
289 resources['unique_key'] = notebook_name
285 resources['output_files_dir'] = '%s_files' % notebook_name
290 resources['output_files_dir'] = '%s_files' % notebook_name
286 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
291 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
287
292
288 # Try to export
293 # Try to export
289 try:
294 try:
290 output, resources = exporter.from_filename(notebook_filename, resources=resources)
295 output, resources = exporter.from_filename(notebook_filename, resources=resources)
291 except ConversionException as e:
296 except ConversionException as e:
292 self.log.error("Error while converting '%s'", notebook_filename,
297 self.log.error("Error while converting '%s'", notebook_filename,
293 exc_info=True)
298 exc_info=True)
294 self.exit(1)
299 self.exit(1)
295 else:
300 else:
296 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
301 write_results = self.writer.write(output, resources, notebook_name=notebook_name)
297
302
298 #Post-process if post processor has been defined.
303 #Post-process if post processor has been defined.
299 if hasattr(self, 'postprocessor') and self.postprocessor:
304 if hasattr(self, 'postprocessor') and self.postprocessor:
300 self.postprocessor(write_resultes)
305 self.postprocessor(write_results)
301 conversion_success += 1
306 conversion_success += 1
302
307
303 # If nothing was converted successfully, help the user.
308 # If nothing was converted successfully, help the user.
304 if conversion_success == 0:
309 if conversion_success == 0:
305 self.print_help()
310 self.print_help()
306 sys.exit(-1)
311 sys.exit(-1)
307
312
308 #-----------------------------------------------------------------------------
313 #-----------------------------------------------------------------------------
309 # Main entry point
314 # Main entry point
310 #-----------------------------------------------------------------------------
315 #-----------------------------------------------------------------------------
311
316
312 launch_new_instance = NbConvertApp.launch_instance
317 launch_new_instance = NbConvertApp.launch_instance
@@ -1,202 +1,212 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test NbConvertApp"""
2 """Test NbConvertApp"""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import glob
8 import glob
9 import sys
9 import sys
10
10
11 from .base import TestsBase
11 from .base import TestsBase
12 from ..postprocessors import PostProcessorBase
12 from ..postprocessors import PostProcessorBase
13
13
14 import IPython.testing.tools as tt
14 import IPython.testing.tools as tt
15 from IPython.testing import decorators as dec
15 from IPython.testing import decorators as dec
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Classes and functions
18 # Classes and functions
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 class DummyPost(PostProcessorBase):
21 class DummyPost(PostProcessorBase):
22 def postprocess(self, filename):
22 def postprocess(self, filename):
23 print("Dummy:%s" % filename)
23 print("Dummy:%s" % filename)
24
24
25 class TestNbConvertApp(TestsBase):
25 class TestNbConvertApp(TestsBase):
26 """Collection of NbConvertApp tests"""
26 """Collection of NbConvertApp tests"""
27
27
28
28
29 def test_notebook_help(self):
29 def test_notebook_help(self):
30 """Will help show if no notebooks are specified?"""
30 """Will help show if no notebooks are specified?"""
31 with self.create_temp_cwd():
31 with self.create_temp_cwd():
32 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
32 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
33 self.assertIn("see '--help-all'", out)
33 self.assertIn("see '--help-all'", out)
34
34
35 def test_help_output(self):
35 def test_help_output(self):
36 """ipython nbconvert --help-all works"""
36 """ipython nbconvert --help-all works"""
37 tt.help_all_output_test('nbconvert')
37 tt.help_all_output_test('nbconvert')
38
38
39 def test_glob(self):
39 def test_glob(self):
40 """
40 """
41 Do search patterns work for notebook names?
41 Do search patterns work for notebook names?
42 """
42 """
43 with self.create_temp_cwd(['notebook*.ipynb']):
43 with self.create_temp_cwd(['notebook*.ipynb']):
44 self.call('nbconvert --to python *.ipynb --log-level 0')
44 self.call('nbconvert --to python *.ipynb --log-level 0')
45 assert os.path.isfile('notebook1.py')
45 assert os.path.isfile('notebook1.py')
46 assert os.path.isfile('notebook2.py')
46 assert os.path.isfile('notebook2.py')
47
47
48
48
49 def test_glob_subdir(self):
49 def test_glob_subdir(self):
50 """
50 """
51 Do search patterns work for subdirectory notebook names?
51 Do search patterns work for subdirectory notebook names?
52 """
52 """
53 with self.create_temp_cwd():
53 with self.create_temp_cwd():
54 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
54 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
55 self.call('nbconvert --to python --log-level 0 ' +
55 self.call('nbconvert --to python --log-level 0 ' +
56 os.path.join('subdir', '*.ipynb'))
56 os.path.join('subdir', '*.ipynb'))
57 assert os.path.isfile('notebook1.py')
57 assert os.path.isfile('notebook1.py')
58 assert os.path.isfile('notebook2.py')
58 assert os.path.isfile('notebook2.py')
59
59
60
60
61 def test_explicit(self):
61 def test_explicit(self):
62 """
62 """
63 Do explicit notebook names work?
63 Do explicit notebook names work?
64 """
64 """
65 with self.create_temp_cwd(['notebook*.ipynb']):
65 with self.create_temp_cwd(['notebook*.ipynb']):
66 self.call('nbconvert --log-level 0 --to python notebook2')
66 self.call('nbconvert --log-level 0 --to python notebook2')
67 assert not os.path.isfile('notebook1.py')
67 assert not os.path.isfile('notebook1.py')
68 assert os.path.isfile('notebook2.py')
68 assert os.path.isfile('notebook2.py')
69
69
70
70
71 @dec.onlyif_cmds_exist('pdflatex')
71 @dec.onlyif_cmds_exist('pdflatex')
72 @dec.onlyif_cmds_exist('pandoc')
72 @dec.onlyif_cmds_exist('pandoc')
73 def test_filename_spaces(self):
73 def test_filename_spaces(self):
74 """
74 """
75 Generate PDFs with graphics if notebooks have spaces in the name?
75 Generate PDFs with graphics if notebooks have spaces in the name?
76 """
76 """
77 with self.create_temp_cwd(['notebook2.ipynb']):
77 with self.create_temp_cwd(['notebook2.ipynb']):
78 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
78 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
79 self.call('nbconvert --log-level 0 --to pdf'
79 self.call('nbconvert --log-level 0 --to pdf'
80 ' "notebook with spaces"'
80 ' "notebook with spaces"'
81 ' --PDFExporter.latex_count=1'
81 ' --PDFExporter.latex_count=1'
82 ' --PDFExporter.verbose=True'
82 ' --PDFExporter.verbose=True'
83 )
83 )
84 assert os.path.isfile('notebook with spaces.pdf')
84 assert os.path.isfile('notebook with spaces.pdf')
85
85
86 def test_post_processor(self):
86 def test_post_processor(self):
87 """Do post processors work?"""
87 """Do post processors work?"""
88 with self.create_temp_cwd(['notebook1.ipynb']):
88 with self.create_temp_cwd(['notebook1.ipynb']):
89 out, err = self.call('nbconvert --log-level 0 --to python notebook1 '
89 out, err = self.call('nbconvert --log-level 0 --to python notebook1 '
90 '--post IPython.nbconvert.tests.test_nbconvertapp.DummyPost')
90 '--post IPython.nbconvert.tests.test_nbconvertapp.DummyPost')
91 self.assertIn('Dummy:notebook1.py', out)
91 self.assertIn('Dummy:notebook1.py', out)
92
92
93 @dec.onlyif_cmds_exist('pandoc')
93 @dec.onlyif_cmds_exist('pandoc')
94 def test_spurious_cr(self):
94 def test_spurious_cr(self):
95 """Check for extra CR characters"""
95 """Check for extra CR characters"""
96 with self.create_temp_cwd(['notebook2.ipynb']):
96 with self.create_temp_cwd(['notebook2.ipynb']):
97 self.call('nbconvert --log-level 0 --to latex notebook2')
97 self.call('nbconvert --log-level 0 --to latex notebook2')
98 assert os.path.isfile('notebook2.tex')
98 assert os.path.isfile('notebook2.tex')
99 with open('notebook2.tex') as f:
99 with open('notebook2.tex') as f:
100 tex = f.read()
100 tex = f.read()
101 self.call('nbconvert --log-level 0 --to html notebook2')
101 self.call('nbconvert --log-level 0 --to html notebook2')
102 assert os.path.isfile('notebook2.html')
102 assert os.path.isfile('notebook2.html')
103 with open('notebook2.html') as f:
103 with open('notebook2.html') as f:
104 html = f.read()
104 html = f.read()
105 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
105 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
106 self.assertEqual(html.count('\r'), html.count('\r\n'))
106 self.assertEqual(html.count('\r'), html.count('\r\n'))
107
107
108 @dec.onlyif_cmds_exist('pandoc')
108 @dec.onlyif_cmds_exist('pandoc')
109 def test_png_base64_html_ok(self):
109 def test_png_base64_html_ok(self):
110 """Is embedded png data well formed in HTML?"""
110 """Is embedded png data well formed in HTML?"""
111 with self.create_temp_cwd(['notebook2.ipynb']):
111 with self.create_temp_cwd(['notebook2.ipynb']):
112 self.call('nbconvert --log-level 0 --to HTML '
112 self.call('nbconvert --log-level 0 --to HTML '
113 'notebook2.ipynb --template full')
113 'notebook2.ipynb --template full')
114 assert os.path.isfile('notebook2.html')
114 assert os.path.isfile('notebook2.html')
115 with open('notebook2.html') as f:
115 with open('notebook2.html') as f:
116 assert "data:image/png;base64,b'" not in f.read()
116 assert "data:image/png;base64,b'" not in f.read()
117
117
118 @dec.onlyif_cmds_exist('pandoc')
118 @dec.onlyif_cmds_exist('pandoc')
119 def test_template(self):
119 def test_template(self):
120 """
120 """
121 Do export templates work?
121 Do export templates work?
122 """
122 """
123 with self.create_temp_cwd(['notebook2.ipynb']):
123 with self.create_temp_cwd(['notebook2.ipynb']):
124 self.call('nbconvert --log-level 0 --to slides '
124 self.call('nbconvert --log-level 0 --to slides '
125 'notebook2.ipynb')
125 'notebook2.ipynb')
126 assert os.path.isfile('notebook2.slides.html')
126 assert os.path.isfile('notebook2.slides.html')
127 with open('notebook2.slides.html') as f:
127 with open('notebook2.slides.html') as f:
128 assert '/reveal.css' in f.read()
128 assert '/reveal.css' in f.read()
129
129
130 def test_output_ext(self):
131 """test --output=outputfile[.ext]"""
132 with self.create_temp_cwd(['notebook1.ipynb']):
133 self.call('nbconvert --log-level 0 --to python '
134 'notebook1.ipynb --output nb.py')
135 assert os.path.exists('nb.py')
136
137 self.call('nbconvert --log-level 0 --to python '
138 'notebook1.ipynb --output nb2')
139 assert os.path.exists('nb2.py')
130
140
131 def test_glob_explicit(self):
141 def test_glob_explicit(self):
132 """
142 """
133 Can a search pattern be used along with matching explicit notebook names?
143 Can a search pattern be used along with matching explicit notebook names?
134 """
144 """
135 with self.create_temp_cwd(['notebook*.ipynb']):
145 with self.create_temp_cwd(['notebook*.ipynb']):
136 self.call('nbconvert --log-level 0 --to python '
146 self.call('nbconvert --log-level 0 --to python '
137 '*.ipynb notebook1.ipynb notebook2.ipynb')
147 '*.ipynb notebook1.ipynb notebook2.ipynb')
138 assert os.path.isfile('notebook1.py')
148 assert os.path.isfile('notebook1.py')
139 assert os.path.isfile('notebook2.py')
149 assert os.path.isfile('notebook2.py')
140
150
141
151
142 def test_explicit_glob(self):
152 def test_explicit_glob(self):
143 """
153 """
144 Can explicit notebook names be used and then a matching search pattern?
154 Can explicit notebook names be used and then a matching search pattern?
145 """
155 """
146 with self.create_temp_cwd(['notebook*.ipynb']):
156 with self.create_temp_cwd(['notebook*.ipynb']):
147 self.call('nbconvert --log-level 0 --to=python '
157 self.call('nbconvert --log-level 0 --to=python '
148 'notebook1.ipynb notebook2.ipynb *.ipynb')
158 'notebook1.ipynb notebook2.ipynb *.ipynb')
149 assert os.path.isfile('notebook1.py')
159 assert os.path.isfile('notebook1.py')
150 assert os.path.isfile('notebook2.py')
160 assert os.path.isfile('notebook2.py')
151
161
152
162
153 def test_default_config(self):
163 def test_default_config(self):
154 """
164 """
155 Does the default config work?
165 Does the default config work?
156 """
166 """
157 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
167 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
158 self.call('nbconvert --log-level 0')
168 self.call('nbconvert --log-level 0')
159 assert os.path.isfile('notebook1.py')
169 assert os.path.isfile('notebook1.py')
160 assert not os.path.isfile('notebook2.py')
170 assert not os.path.isfile('notebook2.py')
161
171
162
172
163 def test_override_config(self):
173 def test_override_config(self):
164 """
174 """
165 Can the default config be overriden?
175 Can the default config be overriden?
166 """
176 """
167 with self.create_temp_cwd(['notebook*.ipynb',
177 with self.create_temp_cwd(['notebook*.ipynb',
168 'ipython_nbconvert_config.py',
178 'ipython_nbconvert_config.py',
169 'override.py']):
179 'override.py']):
170 self.call('nbconvert --log-level 0 --config="override.py"')
180 self.call('nbconvert --log-level 0 --config="override.py"')
171 assert not os.path.isfile('notebook1.py')
181 assert not os.path.isfile('notebook1.py')
172 assert os.path.isfile('notebook2.py')
182 assert os.path.isfile('notebook2.py')
173
183
174 def test_accents_in_filename(self):
184 def test_accents_in_filename(self):
175 """
185 """
176 Can notebook names include accents?
186 Can notebook names include accents?
177 """
187 """
178 with self.create_temp_cwd():
188 with self.create_temp_cwd():
179 self.create_empty_notebook(u'nb1_análisis.ipynb')
189 self.create_empty_notebook(u'nb1_análisis.ipynb')
180 self.call('nbconvert --log-level 0 --to python nb1_*')
190 self.call('nbconvert --log-level 0 --to python nb1_*')
181 assert os.path.isfile(u'nb1_análisis.py')
191 assert os.path.isfile(u'nb1_análisis.py')
182
192
183 @dec.onlyif_cmds_exist('pdflatex', 'pandoc')
193 @dec.onlyif_cmds_exist('pdflatex', 'pandoc')
184 def test_filename_accent_pdf(self):
194 def test_filename_accent_pdf(self):
185 """
195 """
186 Generate PDFs if notebooks have an accent in their name?
196 Generate PDFs if notebooks have an accent in their name?
187 """
197 """
188 with self.create_temp_cwd():
198 with self.create_temp_cwd():
189 self.create_empty_notebook(u'nb1_análisis.ipynb')
199 self.create_empty_notebook(u'nb1_análisis.ipynb')
190 self.call('nbconvert --log-level 0 --to pdf "nb1_*"'
200 self.call('nbconvert --log-level 0 --to pdf "nb1_*"'
191 ' --PDFExporter.latex_count=1'
201 ' --PDFExporter.latex_count=1'
192 ' --PDFExporter.verbose=True')
202 ' --PDFExporter.verbose=True')
193 assert os.path.isfile(u'nb1_análisis.pdf')
203 assert os.path.isfile(u'nb1_análisis.pdf')
194
204
195 def test_cwd_plugin(self):
205 def test_cwd_plugin(self):
196 """
206 """
197 Verify that an extension in the cwd can be imported.
207 Verify that an extension in the cwd can be imported.
198 """
208 """
199 with self.create_temp_cwd(['hello.py']):
209 with self.create_temp_cwd(['hello.py']):
200 self.create_empty_notebook(u'empty.ipynb')
210 self.create_empty_notebook(u'empty.ipynb')
201 self.call('nbconvert empty --to html --NbConvertApp.writer_class=\'hello.HelloWriter\'')
211 self.call('nbconvert empty --to html --NbConvertApp.writer_class=\'hello.HelloWriter\'')
202 assert os.path.isfile(u'hello.txt')
212 assert os.path.isfile(u'hello.txt')
General Comments 0
You need to be logged in to leave comments. Login now