##// END OF EJS Templates
Backport PR #3974: nbconvert: Fix app tests on Window7 w/ Python 3.3...
MinRK -
Show More
@@ -1,321 +1,320 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """NBConvert is a utility for conversion of .ipynb files.
2 """NBConvert is a utility for conversion of .ipynb files.
3
3
4 Command-line interface for the NbConvert conversion utility.
4 Command-line interface for the NbConvert conversion utility.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 #Copyright (c) 2013, the IPython Development Team.
7 #Copyright (c) 2013, the IPython Development Team.
8 #
8 #
9 #Distributed under the terms of the Modified BSD License.
9 #Distributed under the terms of the Modified BSD License.
10 #
10 #
11 #The full license is in the file COPYING.txt, distributed with this software.
11 #The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 #Imports
15 #Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Stdlib imports
18 # Stdlib imports
19 from __future__ import print_function
19 from __future__ import print_function
20
20
21 import logging
21 import logging
22 import sys
22 import sys
23 import os
23 import os
24 import glob
24 import glob
25
25
26 # From IPython
26 # From IPython
27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 from IPython.config import catch_config_error, Configurable
28 from IPython.config import catch_config_error, Configurable
29 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
30 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
30 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
31 )
31 )
32 from IPython.utils.importstring import import_item
32 from IPython.utils.importstring import import_item
33 from IPython.utils.text import dedent
33 from IPython.utils.text import dedent
34
34
35 from .exporters.export import get_export_names, exporter_map
35 from .exporters.export import get_export_names, exporter_map
36 from IPython.nbconvert import exporters, transformers, writers, post_processors
36 from IPython.nbconvert import exporters, transformers, writers, post_processors
37 from .utils.base import NbConvertBase
37 from .utils.base import NbConvertBase
38 from .utils.exceptions import ConversionException
38 from .utils.exceptions import ConversionException
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 #Classes and functions
41 #Classes and functions
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 class DottedOrNone(DottedObjectName):
44 class DottedOrNone(DottedObjectName):
45 """
45 """
46 A string holding a valid dotted object name in Python, such as A.b3._c
46 A string holding a valid dotted object name in Python, such as A.b3._c
47 Also allows for None type."""
47 Also allows for None type."""
48
48
49 default_value = u''
49 default_value = u''
50
50
51 def validate(self, obj, value):
51 def validate(self, obj, value):
52 if value is not None and len(value) > 0:
52 if value is not None and len(value) > 0:
53 return super(DottedOrNone, self).validate(obj, value)
53 return super(DottedOrNone, self).validate(obj, value)
54 else:
54 else:
55 return value
55 return value
56
56
57 nbconvert_aliases = {}
57 nbconvert_aliases = {}
58 nbconvert_aliases.update(base_aliases)
58 nbconvert_aliases.update(base_aliases)
59 nbconvert_aliases.update({
59 nbconvert_aliases.update({
60 'to' : 'NbConvertApp.export_format',
60 'to' : 'NbConvertApp.export_format',
61 'template' : 'Exporter.template_file',
61 'template' : 'Exporter.template_file',
62 'notebooks' : 'NbConvertApp.notebooks',
63 'writer' : 'NbConvertApp.writer_class',
62 'writer' : 'NbConvertApp.writer_class',
64 'post': 'NbConvertApp.post_processor_class',
63 'post': 'NbConvertApp.post_processor_class',
65 'output': 'NbConvertApp.output_base'
64 'output': 'NbConvertApp.output_base'
66 })
65 })
67
66
68 nbconvert_flags = {}
67 nbconvert_flags = {}
69 nbconvert_flags.update(base_flags)
68 nbconvert_flags.update(base_flags)
70 nbconvert_flags.update({
69 nbconvert_flags.update({
71 'stdout' : (
70 'stdout' : (
72 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
71 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
73 "Write notebook output to stdout instead of files."
72 "Write notebook output to stdout instead of files."
74 )
73 )
75 })
74 })
76
75
77
76
78 class NbConvertApp(BaseIPythonApplication):
77 class NbConvertApp(BaseIPythonApplication):
79 """Application used to convert to and from notebook file type (*.ipynb)"""
78 """Application used to convert to and from notebook file type (*.ipynb)"""
80
79
81 name = 'ipython-nbconvert'
80 name = 'ipython-nbconvert'
82 aliases = nbconvert_aliases
81 aliases = nbconvert_aliases
83 flags = nbconvert_flags
82 flags = nbconvert_flags
84
83
85 def _log_level_default(self):
84 def _log_level_default(self):
86 return logging.INFO
85 return logging.INFO
87
86
88 def _classes_default(self):
87 def _classes_default(self):
89 classes = [NbConvertBase]
88 classes = [NbConvertBase]
90 for pkg in (exporters, transformers, writers):
89 for pkg in (exporters, transformers, writers):
91 for name in dir(pkg):
90 for name in dir(pkg):
92 cls = getattr(pkg, name)
91 cls = getattr(pkg, name)
93 if isinstance(cls, type) and issubclass(cls, Configurable):
92 if isinstance(cls, type) and issubclass(cls, Configurable):
94 classes.append(cls)
93 classes.append(cls)
95 return classes
94 return classes
96
95
97 description = Unicode(
96 description = Unicode(
98 u"""This application is used to convert notebook files (*.ipynb)
97 u"""This application is used to convert notebook files (*.ipynb)
99 to various other formats.
98 to various other formats.
100
99
101 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
100 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
102
101
103 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
102 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
104 can only be use when converting one notebook at a time.
103 can only be use when converting one notebook at a time.
105 ''')
104 ''')
106
105
107 examples = Unicode(u"""
106 examples = Unicode(u"""
108 The simplest way to use nbconvert is
107 The simplest way to use nbconvert is
109
108
110 > ipython nbconvert mynotebook.ipynb
109 > ipython nbconvert mynotebook.ipynb
111
110
112 which will convert mynotebook.ipynb to the default format (probably HTML).
111 which will convert mynotebook.ipynb to the default format (probably HTML).
113
112
114 You can specify the export format with `--to`.
113 You can specify the export format with `--to`.
115 Options include {0}
114 Options include {0}
116
115
117 > ipython nbconvert --to latex mynotebook.ipnynb
116 > ipython nbconvert --to latex mynotebook.ipnynb
118
117
119 Both HTML and LaTeX support multiple output templates. LaTeX includes
118 Both HTML and LaTeX support multiple output templates. LaTeX includes
120 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
119 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
121 can specify the flavor of the format used.
120 can specify the flavor of the format used.
122
121
123 > ipython nbconvert --to html --template basic mynotebook.ipynb
122 > ipython nbconvert --to html --template basic mynotebook.ipynb
124
123
125 You can also pipe the output to stdout, rather than a file
124 You can also pipe the output to stdout, rather than a file
126
125
127 > ipython nbconvert mynotebook.ipynb --stdout
126 > ipython nbconvert mynotebook.ipynb --stdout
128
127
129 A post-processor can be used to compile a PDF
128 A post-processor can be used to compile a PDF
130
129
131 > ipython nbconvert mynotebook.ipynb --to latex --post PDF
130 > ipython nbconvert mynotebook.ipynb --to latex --post PDF
132
131
133 You can get (and serve) a Reveal.js-powered slideshow
132 You can get (and serve) a Reveal.js-powered slideshow
134
133
135 > ipython nbconvert myslides.ipynb --to slides --post serve
134 > ipython nbconvert myslides.ipynb --to slides --post serve
136
135
137 Multiple notebooks can be given at the command line in a couple of
136 Multiple notebooks can be given at the command line in a couple of
138 different ways:
137 different ways:
139
138
140 > ipython nbconvert notebook*.ipynb
139 > ipython nbconvert notebook*.ipynb
141 > ipython nbconvert notebook1.ipynb notebook2.ipynb
140 > ipython nbconvert notebook1.ipynb notebook2.ipynb
142
141
143 or you can specify the notebooks list in a config file, containing::
142 or you can specify the notebooks list in a config file, containing::
144
143
145 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
144 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
146
145
147 > ipython nbconvert --config mycfg.py
146 > ipython nbconvert --config mycfg.py
148 """.format(get_export_names()))
147 """.format(get_export_names()))
149
148
150 # Writer specific variables
149 # Writer specific variables
151 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
150 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
152 help="""Instance of the writer class used to write the
151 help="""Instance of the writer class used to write the
153 results of the conversion.""")
152 results of the conversion.""")
154 writer_class = DottedObjectName('FilesWriter', config=True,
153 writer_class = DottedObjectName('FilesWriter', config=True,
155 help="""Writer class used to write the
154 help="""Writer class used to write the
156 results of the conversion""")
155 results of the conversion""")
157 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
156 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
158 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
157 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
159 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
158 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
160 writer_factory = Type()
159 writer_factory = Type()
161
160
162 def _writer_class_changed(self, name, old, new):
161 def _writer_class_changed(self, name, old, new):
163 if new.lower() in self.writer_aliases:
162 if new.lower() in self.writer_aliases:
164 new = self.writer_aliases[new.lower()]
163 new = self.writer_aliases[new.lower()]
165 self.writer_factory = import_item(new)
164 self.writer_factory = import_item(new)
166
165
167 # Post-processor specific variables
166 # Post-processor specific variables
168 post_processor = Instance('IPython.nbconvert.post_processors.base.PostProcessorBase',
167 post_processor = Instance('IPython.nbconvert.post_processors.base.PostProcessorBase',
169 help="""Instance of the PostProcessor class used to write the
168 help="""Instance of the PostProcessor class used to write the
170 results of the conversion.""")
169 results of the conversion.""")
171
170
172 post_processor_class = DottedOrNone(config=True,
171 post_processor_class = DottedOrNone(config=True,
173 help="""PostProcessor class used to write the
172 help="""PostProcessor class used to write the
174 results of the conversion""")
173 results of the conversion""")
175 post_processor_aliases = {'pdf': 'IPython.nbconvert.post_processors.pdf.PDFPostProcessor',
174 post_processor_aliases = {'pdf': 'IPython.nbconvert.post_processors.pdf.PDFPostProcessor',
176 'serve': 'IPython.nbconvert.post_processors.serve.ServePostProcessor'}
175 'serve': 'IPython.nbconvert.post_processors.serve.ServePostProcessor'}
177 post_processor_factory = Type()
176 post_processor_factory = Type()
178
177
179 def _post_processor_class_changed(self, name, old, new):
178 def _post_processor_class_changed(self, name, old, new):
180 if new.lower() in self.post_processor_aliases:
179 if new.lower() in self.post_processor_aliases:
181 new = self.post_processor_aliases[new.lower()]
180 new = self.post_processor_aliases[new.lower()]
182 if new:
181 if new:
183 self.post_processor_factory = import_item(new)
182 self.post_processor_factory = import_item(new)
184
183
185
184
186 # Other configurable variables
185 # Other configurable variables
187 export_format = CaselessStrEnum(get_export_names(),
186 export_format = CaselessStrEnum(get_export_names(),
188 default_value="html",
187 default_value="html",
189 config=True,
188 config=True,
190 help="""The export format to be used."""
189 help="""The export format to be used."""
191 )
190 )
192
191
193 notebooks = List([], config=True, help="""List of notebooks to convert.
192 notebooks = List([], config=True, help="""List of notebooks to convert.
194 Wildcards are supported.
193 Wildcards are supported.
195 Filenames passed positionally will be added to the list.
194 Filenames passed positionally will be added to the list.
196 """)
195 """)
197
196
198 @catch_config_error
197 @catch_config_error
199 def initialize(self, argv=None):
198 def initialize(self, argv=None):
200 super(NbConvertApp, self).initialize(argv)
199 super(NbConvertApp, self).initialize(argv)
201 self.init_syspath()
200 self.init_syspath()
202 self.init_notebooks()
201 self.init_notebooks()
203 self.init_writer()
202 self.init_writer()
204 self.init_post_processor()
203 self.init_post_processor()
205
204
206
205
207
206
208 def init_syspath(self):
207 def init_syspath(self):
209 """
208 """
210 Add the cwd to the sys.path ($PYTHONPATH)
209 Add the cwd to the sys.path ($PYTHONPATH)
211 """
210 """
212 sys.path.insert(0, os.getcwd())
211 sys.path.insert(0, os.getcwd())
213
212
214
213
215 def init_notebooks(self):
214 def init_notebooks(self):
216 """Construct the list of notebooks.
215 """Construct the list of notebooks.
217 If notebooks are passed on the command-line,
216 If notebooks are passed on the command-line,
218 they override notebooks specified in config files.
217 they override notebooks specified in config files.
219 Glob each notebook to replace notebook patterns with filenames.
218 Glob each notebook to replace notebook patterns with filenames.
220 """
219 """
221
220
222 # Specifying notebooks on the command-line overrides (rather than adds)
221 # Specifying notebooks on the command-line overrides (rather than adds)
223 # the notebook list
222 # the notebook list
224 if self.extra_args:
223 if self.extra_args:
225 patterns = self.extra_args
224 patterns = self.extra_args
226 else:
225 else:
227 patterns = self.notebooks
226 patterns = self.notebooks
228
227
229 # Use glob to replace all the notebook patterns with filenames.
228 # Use glob to replace all the notebook patterns with filenames.
230 filenames = []
229 filenames = []
231 for pattern in patterns:
230 for pattern in patterns:
232
231
233 # Use glob to find matching filenames. Allow the user to convert
232 # Use glob to find matching filenames. Allow the user to convert
234 # notebooks without having to type the extension.
233 # notebooks without having to type the extension.
235 globbed_files = glob.glob(pattern)
234 globbed_files = glob.glob(pattern)
236 globbed_files.extend(glob.glob(pattern + '.ipynb'))
235 globbed_files.extend(glob.glob(pattern + '.ipynb'))
237 if not globbed_files:
236 if not globbed_files:
238 self.log.warn("pattern %r matched no files", pattern)
237 self.log.warn("pattern %r matched no files", pattern)
239
238
240 for filename in globbed_files:
239 for filename in globbed_files:
241 if not filename in filenames:
240 if not filename in filenames:
242 filenames.append(filename)
241 filenames.append(filename)
243 self.notebooks = filenames
242 self.notebooks = filenames
244
243
245 def init_writer(self):
244 def init_writer(self):
246 """
245 """
247 Initialize the writer (which is stateless)
246 Initialize the writer (which is stateless)
248 """
247 """
249 self._writer_class_changed(None, self.writer_class, self.writer_class)
248 self._writer_class_changed(None, self.writer_class, self.writer_class)
250 self.writer = self.writer_factory(parent=self)
249 self.writer = self.writer_factory(parent=self)
251
250
252 def init_post_processor(self):
251 def init_post_processor(self):
253 """
252 """
254 Initialize the post_processor (which is stateless)
253 Initialize the post_processor (which is stateless)
255 """
254 """
256 self._post_processor_class_changed(None, self.post_processor_class,
255 self._post_processor_class_changed(None, self.post_processor_class,
257 self.post_processor_class)
256 self.post_processor_class)
258 if self.post_processor_factory:
257 if self.post_processor_factory:
259 self.post_processor = self.post_processor_factory(parent=self)
258 self.post_processor = self.post_processor_factory(parent=self)
260
259
261 def start(self):
260 def start(self):
262 """
261 """
263 Ran after initialization completed
262 Ran after initialization completed
264 """
263 """
265 super(NbConvertApp, self).start()
264 super(NbConvertApp, self).start()
266 self.convert_notebooks()
265 self.convert_notebooks()
267
266
268 def convert_notebooks(self):
267 def convert_notebooks(self):
269 """
268 """
270 Convert the notebooks in the self.notebook traitlet
269 Convert the notebooks in the self.notebook traitlet
271 """
270 """
272 # Export each notebook
271 # Export each notebook
273 conversion_success = 0
272 conversion_success = 0
274
273
275 if self.output_base != '' and len(self.notebooks) > 1:
274 if self.output_base != '' and len(self.notebooks) > 1:
276 self.log.error(
275 self.log.error(
277 """UsageError: --output flag or `NbConvertApp.output_base` config option
276 """UsageError: --output flag or `NbConvertApp.output_base` config option
278 cannot be used when converting multiple notebooks.
277 cannot be used when converting multiple notebooks.
279 """)
278 """)
280 self.exit(1)
279 self.exit(1)
281
280
282 exporter = exporter_map[self.export_format](config=self.config)
281 exporter = exporter_map[self.export_format](config=self.config)
283
282
284 for notebook_filename in self.notebooks:
283 for notebook_filename in self.notebooks:
285 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
284 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
286
285
287 # Get a unique key for the notebook and set it in the resources object.
286 # Get a unique key for the notebook and set it in the resources object.
288 basename = os.path.basename(notebook_filename)
287 basename = os.path.basename(notebook_filename)
289 notebook_name = basename[:basename.rfind('.')]
288 notebook_name = basename[:basename.rfind('.')]
290 if self.output_base:
289 if self.output_base:
291 notebook_name = self.output_base
290 notebook_name = self.output_base
292 resources = {}
291 resources = {}
293 resources['unique_key'] = notebook_name
292 resources['unique_key'] = notebook_name
294 resources['output_files_dir'] = '%s_files' % notebook_name
293 resources['output_files_dir'] = '%s_files' % notebook_name
295 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
294 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
296
295
297 # Try to export
296 # Try to export
298 try:
297 try:
299 output, resources = exporter.from_filename(notebook_filename, resources=resources)
298 output, resources = exporter.from_filename(notebook_filename, resources=resources)
300 except ConversionException as e:
299 except ConversionException as e:
301 self.log.error("Error while converting '%s'", notebook_filename,
300 self.log.error("Error while converting '%s'", notebook_filename,
302 exc_info=True)
301 exc_info=True)
303 self.exit(1)
302 self.exit(1)
304 else:
303 else:
305 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
304 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
306
305
307 #Post-process if post processor has been defined.
306 #Post-process if post processor has been defined.
308 if hasattr(self, 'post_processor') and self.post_processor:
307 if hasattr(self, 'post_processor') and self.post_processor:
309 self.post_processor(write_resultes)
308 self.post_processor(write_resultes)
310 conversion_success += 1
309 conversion_success += 1
311
310
312 # If nothing was converted successfully, help the user.
311 # If nothing was converted successfully, help the user.
313 if conversion_success == 0:
312 if conversion_success == 0:
314 self.print_help()
313 self.print_help()
315 sys.exit(-1)
314 sys.exit(-1)
316
315
317 #-----------------------------------------------------------------------------
316 #-----------------------------------------------------------------------------
318 # Main entry point
317 # Main entry point
319 #-----------------------------------------------------------------------------
318 #-----------------------------------------------------------------------------
320
319
321 launch_new_instance = NbConvertApp.launch_instance
320 launch_new_instance = NbConvertApp.launch_instance
@@ -1,143 +1,155 b''
1 """
1 """
2 Contains base test class for nbconvert
2 Contains base test class for nbconvert
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
5 #Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 #Distributed under the terms of the Modified BSD License.
7 #Distributed under the terms of the Modified BSD License.
8 #
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import os
16 import os
17 import glob
17 import glob
18 import shutil
18 import shutil
19
19
20 import IPython
20 import IPython
21 from IPython.utils.tempdir import TemporaryWorkingDirectory
21 from IPython.utils.tempdir import TemporaryWorkingDirectory
22 from IPython.utils.process import get_output_error_code
22 from IPython.utils.process import get_output_error_code
23 from IPython.testing.tools import get_ipython_cmd
23 from IPython.testing.tools import get_ipython_cmd
24 from IPython.testing.ipunittest import ParametricTestCase
24 from IPython.testing.ipunittest import ParametricTestCase
25
25
26 # a trailing space allows for simpler concatenation with the other arguments
26 # a trailing space allows for simpler concatenation with the other arguments
27 ipy_cmd = get_ipython_cmd(as_string=True) + " "
27 ipy_cmd = get_ipython_cmd(as_string=True) + " "
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33
33
34 class TestsBase(ParametricTestCase):
34 class TestsBase(ParametricTestCase):
35 """Base tests class. Contains useful fuzzy comparison and nbconvert
35 """Base tests class. Contains useful fuzzy comparison and nbconvert
36 functions."""
36 functions."""
37
37
38
38
39 def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True,
39 def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True,
40 fuzzy_spacing=True, ignore_spaces=False,
40 fuzzy_spacing=True, ignore_spaces=False,
41 ignore_newlines=False, case_sensitive=False, leave_padding=False):
41 ignore_newlines=False, case_sensitive=False, leave_padding=False):
42 """
42 """
43 Performs a fuzzy comparison of two strings. A fuzzy comparison is a
43 Performs a fuzzy comparison of two strings. A fuzzy comparison is a
44 comparison that ignores insignificant differences in the two comparands.
44 comparison that ignores insignificant differences in the two comparands.
45 The significance of certain differences can be specified via the keyword
45 The significance of certain differences can be specified via the keyword
46 parameters of this method.
46 parameters of this method.
47 """
47 """
48
48
49 if not leave_padding:
49 if not leave_padding:
50 a = a.strip()
50 a = a.strip()
51 b = b.strip()
51 b = b.strip()
52
52
53 if ignore_newlines:
53 if ignore_newlines:
54 a = a.replace('\n', '')
54 a = a.replace('\n', '')
55 b = b.replace('\n', '')
55 b = b.replace('\n', '')
56
56
57 if newlines_are_spaces:
57 if newlines_are_spaces:
58 a = a.replace('\n', ' ')
58 a = a.replace('\n', ' ')
59 b = b.replace('\n', ' ')
59 b = b.replace('\n', ' ')
60
60
61 if tabs_are_spaces:
61 if tabs_are_spaces:
62 a = a.replace('\t', ' ')
62 a = a.replace('\t', ' ')
63 b = b.replace('\t', ' ')
63 b = b.replace('\t', ' ')
64
64
65 if ignore_spaces:
65 if ignore_spaces:
66 a = a.replace(' ', '')
66 a = a.replace(' ', '')
67 b = b.replace(' ', '')
67 b = b.replace(' ', '')
68
68
69 if fuzzy_spacing:
69 if fuzzy_spacing:
70 a = self.recursive_replace(a, ' ', ' ')
70 a = self.recursive_replace(a, ' ', ' ')
71 b = self.recursive_replace(b, ' ', ' ')
71 b = self.recursive_replace(b, ' ', ' ')
72
72
73 if not case_sensitive:
73 if not case_sensitive:
74 a = a.lower()
74 a = a.lower()
75 b = b.lower()
75 b = b.lower()
76
76
77 self.assertEqual(a, b)
77 self.assertEqual(a, b)
78
78
79
79
80 def recursive_replace(self, text, search, replacement):
80 def recursive_replace(self, text, search, replacement):
81 """
81 """
82 Performs a recursive replacement operation. Replaces all instances
82 Performs a recursive replacement operation. Replaces all instances
83 of a search string in a text string with a replacement string until
83 of a search string in a text string with a replacement string until
84 the search string no longer exists. Recursion is needed because the
84 the search string no longer exists. Recursion is needed because the
85 replacement string may generate additional search strings.
85 replacement string may generate additional search strings.
86
86
87 For example:
87 For example:
88 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
88 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
89 Another replacement yields "Hi" (the desired output)
89 Another replacement yields "Hi" (the desired output)
90
90
91 Parameters:
91 Parameters:
92 -----------
92 -----------
93 text : string
93 text : string
94 Text to replace in.
94 Text to replace in.
95 search : string
95 search : string
96 String to search for within "text"
96 String to search for within "text"
97 replacement : string
97 replacement : string
98 String to replace "search" with
98 String to replace "search" with
99 """
99 """
100 while search in text:
100 while search in text:
101 text = text.replace(search, replacement)
101 text = text.replace(search, replacement)
102 return text
102 return text
103
103
104 def create_temp_cwd(self, copy_filenames=None):
104 def create_temp_cwd(self, copy_filenames=None):
105 temp_dir = TemporaryWorkingDirectory()
105 temp_dir = TemporaryWorkingDirectory()
106
106
107 #Copy the files if requested.
107 #Copy the files if requested.
108 if copy_filenames is not None:
108 if copy_filenames is not None:
109 self.copy_files_to(copy_filenames)
109 self.copy_files_to(copy_filenames)
110
110
111 #Return directory handler
111 #Return directory handler
112 return temp_dir
112 return temp_dir
113
113
114
114
115 def copy_files_to(self, copy_filenames, dest='.'):
115 def copy_files_to(self, copy_filenames, dest='.'):
116 "Copy test files into the destination directory"
116 "Copy test files into the destination directory"
117 if not os.path.isdir(dest):
117 if not os.path.isdir(dest):
118 os.makedirs(dest)
118 os.makedirs(dest)
119 files_path = self._get_files_path()
119 files_path = self._get_files_path()
120 for pattern in copy_filenames:
120 for pattern in copy_filenames:
121 for match in glob.glob(os.path.join(files_path, pattern)):
121 for match in glob.glob(os.path.join(files_path, pattern)):
122 shutil.copyfile(match, os.path.join(dest, os.path.basename(match)))
122 shutil.copyfile(match, os.path.join(dest, os.path.basename(match)))
123
123
124
124
125 def _get_files_path(self):
125 def _get_files_path(self):
126
126
127 #Get the relative path to this module in the IPython directory.
127 #Get the relative path to this module in the IPython directory.
128 names = self.__module__.split('.')[1:-1]
128 names = self.__module__.split('.')[1:-1]
129 names.append('files')
129 names.append('files')
130
130
131 #Build a path using the IPython directory and the relative path we just
131 #Build a path using the IPython directory and the relative path we just
132 #found.
132 #found.
133 path = IPython.__path__[0]
133 path = IPython.__path__[0]
134 for name in names:
134 for name in names:
135 path = os.path.join(path, name)
135 path = os.path.join(path, name)
136 return path
136 return path
137
137
138
138
139 def call(self, parameters, raise_on_error=True):
139 def call(self, parameters, ignore_return_code=False):
140 """
141 Execute a, IPython shell command, listening for both Errors and non-zero
142 return codes.
143
144 PARAMETERS:
145 -----------
146 parameters : str
147 List of parameters to pass to IPython.
148 ignore_return_code : optional bool (default False)
149 Throw an OSError if the return code
150 """
151
140 stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters)
152 stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters)
141 if retcode != 0 and raise_on_error:
153 if not (retcode == 0 or ignore_return_code):
142 raise OSError(stderr)
154 raise OSError(stderr)
143 return stdout, stderr
155 return stdout, stderr
@@ -1,160 +1,160 b''
1 """
1 """
2 Contains tests for the nbconvertapp
2 Contains tests for the nbconvertapp
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
5 #Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 #Distributed under the terms of the Modified BSD License.
7 #Distributed under the terms of the Modified BSD License.
8 #
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import os
16 import os
17 import glob
17 import glob
18
18
19 from .base import TestsBase
19 from .base import TestsBase
20
20
21 from IPython.testing import decorators as dec
21 from IPython.testing import decorators as dec
22
22
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Constants
25 # Constants
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 class TestNbConvertApp(TestsBase):
33 class TestNbConvertApp(TestsBase):
34 """Collection of NbConvertApp tests"""
34 """Collection of NbConvertApp tests"""
35
35
36
36
37 def test_notebook_help(self):
37 def test_notebook_help(self):
38 """
38 """
39 Will help show if no notebooks are specified?
39 Will help show if no notebooks are specified?
40 """
40 """
41 with self.create_temp_cwd():
41 with self.create_temp_cwd():
42 out, err = self.call('nbconvert --log-level=0', raise_on_error=False)
42 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
43 assert "see '--help-all'" in out
43 assert "see '--help-all'" in out
44
44
45
45
46 def test_glob(self):
46 def test_glob(self):
47 """
47 """
48 Do search patterns work for notebook names?
48 Do search patterns work for notebook names?
49 """
49 """
50 with self.create_temp_cwd(['notebook*.ipynb']):
50 with self.create_temp_cwd(['notebook*.ipynb']):
51 self.call('nbconvert --to="python" --notebooks=\'["*.ipynb"]\' --log-level=0')
51 self.call('nbconvert --to python *.ipynb --log-level 0')
52 assert os.path.isfile('notebook1.py')
52 assert os.path.isfile('notebook1.py')
53 assert os.path.isfile('notebook2.py')
53 assert os.path.isfile('notebook2.py')
54
54
55
55
56 def test_glob_subdir(self):
56 def test_glob_subdir(self):
57 """
57 """
58 Do search patterns work for subdirectory notebook names?
58 Do search patterns work for subdirectory notebook names?
59 """
59 """
60 with self.create_temp_cwd():
60 with self.create_temp_cwd():
61 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
61 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
62 self.call('nbconvert --to="python" --log-level=0 --notebooks='
62 self.call('nbconvert --to python --log-level 0 ' +
63 '\'["%s"]\'' % os.path.join('subdir', '*.ipynb'))
63 os.path.join('subdir', '*.ipynb'))
64 assert os.path.isfile('notebook1.py')
64 assert os.path.isfile('notebook1.py')
65 assert os.path.isfile('notebook2.py')
65 assert os.path.isfile('notebook2.py')
66
66
67
67
68 def test_explicit(self):
68 def test_explicit(self):
69 """
69 """
70 Do explicit notebook names work?
70 Do explicit notebook names work?
71 """
71 """
72 with self.create_temp_cwd(['notebook*.ipynb']):
72 with self.create_temp_cwd(['notebook*.ipynb']):
73 self.call('nbconvert --log-level=0 --to="python" --notebooks='
73 self.call('nbconvert --log-level 0 --to python notebook2')
74 '\'["notebook2.ipynb"]\'')
75 assert not os.path.isfile('notebook1.py')
74 assert not os.path.isfile('notebook1.py')
76 assert os.path.isfile('notebook2.py')
75 assert os.path.isfile('notebook2.py')
77
76
78
77
79 @dec.onlyif_cmds_exist('pdflatex')
78 @dec.onlyif_cmds_exist('pdflatex')
80 @dec.onlyif_cmds_exist('pandoc')
79 @dec.onlyif_cmds_exist('pandoc')
81 def test_filename_spaces(self):
80 def test_filename_spaces(self):
82 """
81 """
83 Generate PDFs with graphics if notebooks have spaces in the name?
82 Generate PDFs with graphics if notebooks have spaces in the name?
84 """
83 """
85 with self.create_temp_cwd(['notebook2.ipynb']):
84 with self.create_temp_cwd(['notebook2.ipynb']):
86 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
85 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
87 o,e = self.call('nbconvert --log-level=0 --to="latex" "notebook with spaces"'
86 o,e = self.call('nbconvert --log-level 0 --to latex '
88 ' --post="PDF" --PDFPostProcessor.verbose=True')
87 '"notebook with spaces" --post PDF '
88 '--PDFPostProcessor.verbose=True')
89 assert os.path.isfile('notebook with spaces.tex')
89 assert os.path.isfile('notebook with spaces.tex')
90 assert os.path.isdir('notebook with spaces_files')
90 assert os.path.isdir('notebook with spaces_files')
91 assert os.path.isfile('notebook with spaces.pdf')
91 assert os.path.isfile('notebook with spaces.pdf')
92
92
93 @dec.onlyif_cmds_exist('pdflatex')
93 @dec.onlyif_cmds_exist('pdflatex')
94 @dec.onlyif_cmds_exist('pandoc')
94 @dec.onlyif_cmds_exist('pandoc')
95 def test_post_processor(self):
95 def test_post_processor(self):
96 """
96 """
97 Do post processors work?
97 Do post processors work?
98 """
98 """
99 with self.create_temp_cwd(['notebook1.ipynb']):
99 with self.create_temp_cwd(['notebook1.ipynb']):
100 self.call('nbconvert --log-level=0 --to="latex" notebook1'
100 self.call('nbconvert --log-level 0 --to latex notebook1 '
101 ' --post="PDF" --PDFPostProcessor.verbose=True')
101 '--post PDF --PDFPostProcessor.verbose=True')
102 assert os.path.isfile('notebook1.tex')
102 assert os.path.isfile('notebook1.tex')
103 assert os.path.isfile('notebook1.pdf')
103 assert os.path.isfile('notebook1.pdf')
104
104
105
105
106 @dec.onlyif_cmds_exist('pandoc')
106 @dec.onlyif_cmds_exist('pandoc')
107 def test_template(self):
107 def test_template(self):
108 """
108 """
109 Do export templates work?
109 Do export templates work?
110 """
110 """
111 with self.create_temp_cwd(['notebook2.ipynb']):
111 with self.create_temp_cwd(['notebook2.ipynb']):
112 self.call('nbconvert --log-level=0 --to=slides --notebooks='
112 self.call('nbconvert --log-level 0 --to slides '
113 '\'["notebook2.ipynb"]\' --template=reveal')
113 'notebook2.ipynb --template reveal')
114 assert os.path.isfile('notebook2.slides.html')
114 assert os.path.isfile('notebook2.slides.html')
115 with open('notebook2.slides.html') as f:
115 with open('notebook2.slides.html') as f:
116 assert '/reveal.css' in f.read()
116 assert '/reveal.css' in f.read()
117
117
118
118
119 def test_glob_explicit(self):
119 def test_glob_explicit(self):
120 """
120 """
121 Can a search pattern be used along with matching explicit notebook names?
121 Can a search pattern be used along with matching explicit notebook names?
122 """
122 """
123 with self.create_temp_cwd(['notebook*.ipynb']):
123 with self.create_temp_cwd(['notebook*.ipynb']):
124 self.call('nbconvert --log-level=0 --to="python" --notebooks='
124 self.call('nbconvert --log-level 0 --to python '
125 '\'["*.ipynb","notebook1.ipynb","notebook2.ipynb"]\'')
125 '*.ipynb notebook1.ipynb notebook2.ipynb')
126 assert os.path.isfile('notebook1.py')
126 assert os.path.isfile('notebook1.py')
127 assert os.path.isfile('notebook2.py')
127 assert os.path.isfile('notebook2.py')
128
128
129
129
130 def test_explicit_glob(self):
130 def test_explicit_glob(self):
131 """
131 """
132 Can explicit notebook names be used and then a matching search pattern?
132 Can explicit notebook names be used and then a matching search pattern?
133 """
133 """
134 with self.create_temp_cwd(['notebook*.ipynb']):
134 with self.create_temp_cwd(['notebook*.ipynb']):
135 self.call('nbconvert --log-level=0 --to="python" --notebooks='
135 self.call('nbconvert --log-level 0 --to=python '
136 '\'["notebook1.ipynb","notebook2.ipynb","*.ipynb"]\'')
136 'notebook1.ipynb notebook2.ipynb *.ipynb')
137 assert os.path.isfile('notebook1.py')
137 assert os.path.isfile('notebook1.py')
138 assert os.path.isfile('notebook2.py')
138 assert os.path.isfile('notebook2.py')
139
139
140
140
141 def test_default_config(self):
141 def test_default_config(self):
142 """
142 """
143 Does the default config work?
143 Does the default config work?
144 """
144 """
145 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
145 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
146 self.call('nbconvert --log-level=0')
146 self.call('nbconvert --log-level 0')
147 assert os.path.isfile('notebook1.py')
147 assert os.path.isfile('notebook1.py')
148 assert not os.path.isfile('notebook2.py')
148 assert not os.path.isfile('notebook2.py')
149
149
150
150
151 def test_override_config(self):
151 def test_override_config(self):
152 """
152 """
153 Can the default config be overriden?
153 Can the default config be overriden?
154 """
154 """
155 with self.create_temp_cwd(['notebook*.ipynb',
155 with self.create_temp_cwd(['notebook*.ipynb',
156 'ipython_nbconvert_config.py',
156 'ipython_nbconvert_config.py',
157 'override.py']):
157 'override.py']):
158 self.call('nbconvert --log-level=0 --config="override.py"')
158 self.call('nbconvert --log-level 0 --config="override.py"')
159 assert not os.path.isfile('notebook1.py')
159 assert not os.path.isfile('notebook1.py')
160 assert os.path.isfile('notebook2.py')
160 assert os.path.isfile('notebook2.py')
General Comments 0
You need to be logged in to leave comments. Login now