##// END OF EJS Templates
fix failing deprecated test
Matthias BUSSONNIER -
Show More
@@ -1,425 +1,425 b''
1 """Base classes for the notebook conversion pipeline.
1 """Base classes for the notebook conversion pipeline.
2
2
3 This module defines Converter, from which all objects designed to implement
3 This module defines Converter, from which all objects designed to implement
4 a conversion of IPython notebooks to some other format should inherit.
4 a conversion of IPython notebooks to some other format should inherit.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2012, the IPython Development Team.
7 # Copyright (c) 2012, 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 from __future__ import print_function, absolute_import
18 from __future__ import print_function, absolute_import
19
19
20 # Stdlib imports
20 # Stdlib imports
21 import codecs
21 import codecs
22 import io
22 import io
23 import logging
23 import logging
24 import os
24 import os
25 import pprint
25 import pprint
26 import re
26 import re
27 from types import FunctionType
27 from types import FunctionType
28
28
29 # IPython imports
29 # IPython imports
30 from IPython.nbformat import current as nbformat
30 from IPython.nbformat import current as nbformat
31 from IPython.config.configurable import Configurable, SingletonConfigurable
31 from IPython.config.configurable import Configurable, SingletonConfigurable
32 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
32 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
33
33
34 # Our own imports
34 # Our own imports
35 from .utils import remove_fake_files_url
35 from .utils import remove_fake_files_url
36
36
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Local utilities
39 # Local utilities
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 def clean_filename(filename):
42 def clean_filename(filename):
43 """
43 """
44 Remove non-alphanumeric characters from filenames.
44 Remove non-alphanumeric characters from filenames.
45
45
46 Parameters
46 Parameters
47 ----------
47 ----------
48 filename : str
48 filename : str
49 The filename to be sanitized.
49 The filename to be sanitized.
50
50
51 Returns
51 Returns
52 -------
52 -------
53 clean : str
53 clean : str
54 A sanitized filename that contains only alphanumeric
54 A sanitized filename that contains only alphanumeric
55 characters and underscores.
55 characters and underscores.
56 """
56 """
57 filename = re.sub(r'[^a-zA-Z0-9_]', '_', filename)
57 filename = re.sub(r'[^a-zA-Z0-9_]', '_', filename)
58 return filename
58 return filename
59
59
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Class declarations
62 # Class declarations
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 class ConversionException(Exception):
65 class ConversionException(Exception):
66 pass
66 pass
67
67
68
68
69 class DocStringInheritor(type):
69 class DocStringInheritor(type):
70 """
70 """
71 This metaclass will walk the list of bases until the desired
71 This metaclass will walk the list of bases until the desired
72 superclass method is found AND if that method has a docstring and only
72 superclass method is found AND if that method has a docstring and only
73 THEN does it attach the superdocstring to the derived class method.
73 THEN does it attach the superdocstring to the derived class method.
74
74
75 Please use carefully, I just did the metaclass thing by following
75 Please use carefully, I just did the metaclass thing by following
76 Michael Foord's Metaclass tutorial
76 Michael Foord's Metaclass tutorial
77 (http://www.voidspace.org.uk/python/articles/metaclasses.shtml), I may
77 (http://www.voidspace.org.uk/python/articles/metaclasses.shtml), I may
78 have missed a step or two.
78 have missed a step or two.
79
79
80 source:
80 source:
81 http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
81 http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
82 by Paul McGuire
82 by Paul McGuire
83 """
83 """
84 def __new__(meta, classname, bases, classDict):
84 def __new__(meta, classname, bases, classDict):
85 newClassDict = {}
85 newClassDict = {}
86 for attributeName, attribute in classDict.items():
86 for attributeName, attribute in classDict.items():
87 if type(attribute) == FunctionType:
87 if type(attribute) == FunctionType:
88 # look through bases for matching function by name
88 # look through bases for matching function by name
89 for baseclass in bases:
89 for baseclass in bases:
90 if hasattr(baseclass, attributeName):
90 if hasattr(baseclass, attributeName):
91 basefn = getattr(baseclass, attributeName)
91 basefn = getattr(baseclass, attributeName)
92 if basefn.__doc__:
92 if basefn.__doc__:
93 attribute.__doc__ = basefn.__doc__
93 attribute.__doc__ = basefn.__doc__
94 break
94 break
95 newClassDict[attributeName] = attribute
95 newClassDict[attributeName] = attribute
96 return type.__new__(meta, classname, bases, newClassDict)
96 return type.__new__(meta, classname, bases, newClassDict)
97
97
98
98
99
99
100 class Converter(Configurable):
100 class Converter(Configurable):
101 #__metaclass__ = DocStringInheritor
101 #__metaclass__ = DocStringInheritor
102 #-------------------------------------------------------------------------
102 #-------------------------------------------------------------------------
103 # Class-level attributes determining the behaviour of the class but
103 # Class-level attributes determining the behaviour of the class but
104 # probably not varying from instance to instance.
104 # probably not varying from instance to instance.
105 #-------------------------------------------------------------------------
105 #-------------------------------------------------------------------------
106 default_encoding = 'utf-8'
106 default_encoding = 'utf-8'
107 extension = str()
107 extension = str()
108 blank_symbol = " "
108 blank_symbol = " "
109 # Which display data format is best? Subclasses can override if
109 # Which display data format is best? Subclasses can override if
110 # they have specific requirements.
110 # they have specific requirements.
111 display_data_priority = ['pdf', 'svg', 'png', 'jpg', 'text']
111 display_data_priority = ['pdf', 'svg', 'png', 'jpg', 'text']
112 #-------------------------------------------------------------------------
112 #-------------------------------------------------------------------------
113 # Instance-level attributes that are set in the constructor for this
113 # Instance-level attributes that are set in the constructor for this
114 # class.
114 # class.
115 #-------------------------------------------------------------------------
115 #-------------------------------------------------------------------------
116 infile = Unicode()
116 infile = Unicode()
117
117
118 highlight_source = Bool(True,
118 highlight_source = Bool(True,
119 config=True,
119 config=True,
120 help="Enable syntax highlighting for code blocks.")
120 help="Enable syntax highlighting for code blocks.")
121
121
122 preamble = Unicode("" ,
122 preamble = Unicode("" ,
123 config=True,
123 config=True,
124 help="Path to a user-specified preamble file")
124 help="Path to a user-specified preamble file")
125
125
126 infile_dir = Unicode()
126 infile_dir = Unicode()
127 infile_root = Unicode()
127 infile_root = Unicode()
128 clean_name = Unicode()
128 clean_name = Unicode()
129 files_dir = Unicode()
129 files_dir = Unicode()
130 outbase = Unicode()
130 outbase = Unicode()
131 #-------------------------------------------------------------------------
131 #-------------------------------------------------------------------------
132 # Instance-level attributes that are set by other methods in the base
132 # Instance-level attributes that are set by other methods in the base
133 # class.
133 # class.
134 #-------------------------------------------------------------------------
134 #-------------------------------------------------------------------------
135 figures_counter = 0
135 figures_counter = 0
136 output = Unicode()
136 output = Unicode()
137 #-------------------------------------------------------------------------
137 #-------------------------------------------------------------------------
138 # Instance-level attributes that are not actually mentioned further
138 # Instance-level attributes that are not actually mentioned further
139 # in this class. TODO: Could they be usefully moved to a subclass?
139 # in this class. TODO: Could they be usefully moved to a subclass?
140 #-------------------------------------------------------------------------
140 #-------------------------------------------------------------------------
141 with_preamble = Bool(True,config=True)
141 with_preamble = Bool(True,config=True)
142 user_preamble = None
142 user_preamble = None
143 raw_as_verbatim = False
143 raw_as_verbatim = False
144
144
145
145
146 def __init__(self, infile=None, config=None, exclude=[], **kw):
146 def __init__(self, infile='', config=None, exclude=[], **kw):
147 super(Converter,self).__init__(config=config)
147 super(Converter,self).__init__(config=config)
148
148
149 #DocStringInheritor.__init__(self=config)
149 #DocStringInheritor.__init__(self=config)
150 # N.B. Initialized in the same order as defined above. Please try to
150 # N.B. Initialized in the same order as defined above. Please try to
151 # keep in this way for readability's sake.
151 # keep in this way for readability's sake.
152 self.exclude_cells = exclude
152 self.exclude_cells = exclude
153 self.infile = infile
153 self.infile = infile
154 self.infile_dir, infile_root = os.path.split(infile)
154 self.infile_dir, infile_root = os.path.split(infile)
155 self.infile_root = os.path.splitext(infile_root)[0]
155 self.infile_root = os.path.splitext(infile_root)[0]
156 self.clean_name = clean_filename(self.infile_root)
156 self.clean_name = clean_filename(self.infile_root)
157 # Handle the creation of a directory for ancillary files, for
157 # Handle the creation of a directory for ancillary files, for
158 # formats that need one.
158 # formats that need one.
159 files_dir = os.path.join(self.infile_dir, self.clean_name + '_files')
159 files_dir = os.path.join(self.infile_dir, self.clean_name + '_files')
160 if not os.path.isdir(files_dir):
160 if not os.path.isdir(files_dir):
161 os.mkdir(files_dir)
161 os.mkdir(files_dir)
162 self.files_dir = files_dir
162 self.files_dir = files_dir
163 self.outbase = os.path.join(self.infile_dir, self.infile_root)
163 self.outbase = os.path.join(self.infile_dir, self.infile_root)
164
164
165 def __del__(self):
165 def __del__(self):
166 if os.path.isdir(self.files_dir) and not os.listdir(self.files_dir):
166 if os.path.isdir(self.files_dir) and not os.listdir(self.files_dir):
167 os.rmdir(self.files_dir)
167 os.rmdir(self.files_dir)
168
168
169 def _get_prompt_number(self, cell):
169 def _get_prompt_number(self, cell):
170 return cell.prompt_number if hasattr(cell, 'prompt_number') \
170 return cell.prompt_number if hasattr(cell, 'prompt_number') \
171 else self.blank_symbol
171 else self.blank_symbol
172
172
173 def dispatch(self, cell_type):
173 def dispatch(self, cell_type):
174 """return cell_type dependent render method, for example render_code
174 """return cell_type dependent render method, for example render_code
175 """
175 """
176 return getattr(self, 'render_' + cell_type, self.render_unknown)
176 return getattr(self, 'render_' + cell_type, self.render_unknown)
177
177
178 def dispatch_display_format(self, format):
178 def dispatch_display_format(self, format):
179 """
179 """
180 return output_type dependent render method, for example
180 return output_type dependent render method, for example
181 render_output_text
181 render_output_text
182 """
182 """
183 return getattr(self, 'render_display_format_' + format,
183 return getattr(self, 'render_display_format_' + format,
184 self.render_unknown_display)
184 self.render_unknown_display)
185
185
186 def convert(self, cell_separator='\n'):
186 def convert(self, cell_separator='\n'):
187 """
187 """
188 Generic method to converts notebook to a string representation.
188 Generic method to converts notebook to a string representation.
189
189
190 This is accomplished by dispatching on the cell_type, so subclasses of
190 This is accomplished by dispatching on the cell_type, so subclasses of
191 Convereter class do not need to re-implement this method, but just
191 Convereter class do not need to re-implement this method, but just
192 need implementation for the methods that will be dispatched.
192 need implementation for the methods that will be dispatched.
193
193
194 Parameters
194 Parameters
195 ----------
195 ----------
196 cell_separator : string
196 cell_separator : string
197 Character or string to join cells with. Default is "\n"
197 Character or string to join cells with. Default is "\n"
198
198
199 Returns
199 Returns
200 -------
200 -------
201 out : string
201 out : string
202 """
202 """
203 lines = []
203 lines = []
204 lines.extend(self.optional_header())
204 lines.extend(self.optional_header())
205 lines.extend(self.main_body(cell_separator))
205 lines.extend(self.main_body(cell_separator))
206 lines.extend(self.optional_footer())
206 lines.extend(self.optional_footer())
207 return u'\n'.join(lines)
207 return u'\n'.join(lines)
208
208
209 def main_body(self, cell_separator='\n'):
209 def main_body(self, cell_separator='\n'):
210 converted_cells = []
210 converted_cells = []
211 for worksheet in self.nb.worksheets:
211 for worksheet in self.nb.worksheets:
212 for cell in worksheet.cells:
212 for cell in worksheet.cells:
213 #print(cell.cell_type) # dbg
213 #print(cell.cell_type) # dbg
214 conv_fn = self.dispatch(cell.cell_type)
214 conv_fn = self.dispatch(cell.cell_type)
215 if cell.cell_type in ('markdown', 'raw'):
215 if cell.cell_type in ('markdown', 'raw'):
216 remove_fake_files_url(cell)
216 remove_fake_files_url(cell)
217 converted_cells.append('\n'.join(conv_fn(cell)))
217 converted_cells.append('\n'.join(conv_fn(cell)))
218 cell_lines = cell_separator.join(converted_cells).split('\n')
218 cell_lines = cell_separator.join(converted_cells).split('\n')
219 return cell_lines
219 return cell_lines
220
220
221 def render(self):
221 def render(self):
222 "read, convert, and save self.infile"
222 "read, convert, and save self.infile"
223 if not hasattr(self, 'nb'):
223 if not hasattr(self, 'nb'):
224 self.read()
224 self.read()
225 self.output = self.convert()
225 self.output = self.convert()
226 assert(type(self.output) == unicode)
226 assert(type(self.output) == unicode)
227 return self.save()
227 return self.save()
228
228
229 def read(self):
229 def read(self):
230 "read and parse notebook into NotebookNode called self.nb"
230 "read and parse notebook into NotebookNode called self.nb"
231 with open(self.infile) as f:
231 with open(self.infile) as f:
232 self.nb = nbformat.read(f, 'json')
232 self.nb = nbformat.read(f, 'json')
233
233
234 def save(self, outfile=None, encoding=None):
234 def save(self, outfile=None, encoding=None):
235 "read and parse notebook into self.nb"
235 "read and parse notebook into self.nb"
236 if outfile is None:
236 if outfile is None:
237 outfile = self.outbase + '.' + self.extension
237 outfile = self.outbase + '.' + self.extension
238 if encoding is None:
238 if encoding is None:
239 encoding = self.default_encoding
239 encoding = self.default_encoding
240 with io.open(outfile, 'w', encoding=encoding) as f:
240 with io.open(outfile, 'w', encoding=encoding) as f:
241 f.write(self.output)
241 f.write(self.output)
242 return os.path.abspath(outfile)
242 return os.path.abspath(outfile)
243
243
244 def optional_header(self):
244 def optional_header(self):
245 """
245 """
246 Optional header to insert at the top of the converted notebook
246 Optional header to insert at the top of the converted notebook
247
247
248 Returns a list
248 Returns a list
249 """
249 """
250 return []
250 return []
251
251
252 def optional_footer(self):
252 def optional_footer(self):
253 """
253 """
254 Optional footer to insert at the end of the converted notebook
254 Optional footer to insert at the end of the converted notebook
255
255
256 Returns a list
256 Returns a list
257 """
257 """
258 return []
258 return []
259
259
260 def _new_figure(self, data, fmt):
260 def _new_figure(self, data, fmt):
261 """Create a new figure file in the given format.
261 """Create a new figure file in the given format.
262
262
263 Returns a path relative to the input file.
263 Returns a path relative to the input file.
264 """
264 """
265 figname = '%s_fig_%02i.%s' % (self.clean_name,
265 figname = '%s_fig_%02i.%s' % (self.clean_name,
266 self.figures_counter, fmt)
266 self.figures_counter, fmt)
267 self.figures_counter += 1
267 self.figures_counter += 1
268 fullname = os.path.join(self.files_dir, figname)
268 fullname = os.path.join(self.files_dir, figname)
269
269
270 # Binary files are base64-encoded, SVG is already XML
270 # Binary files are base64-encoded, SVG is already XML
271 if fmt in ('png', 'jpg', 'pdf'):
271 if fmt in ('png', 'jpg', 'pdf'):
272 data = data.decode('base64')
272 data = data.decode('base64')
273 fopen = lambda fname: open(fname, 'wb')
273 fopen = lambda fname: open(fname, 'wb')
274 else:
274 else:
275 fopen = lambda fname: codecs.open(fname, 'wb',
275 fopen = lambda fname: codecs.open(fname, 'wb',
276 self.default_encoding)
276 self.default_encoding)
277
277
278 with fopen(fullname) as f:
278 with fopen(fullname) as f:
279 f.write(data)
279 f.write(data)
280
280
281 return fullname
281 return fullname
282
282
283 def render_heading(self, cell):
283 def render_heading(self, cell):
284 """convert a heading cell
284 """convert a heading cell
285
285
286 Returns list."""
286 Returns list."""
287 raise NotImplementedError
287 raise NotImplementedError
288
288
289 def render_code(self, cell):
289 def render_code(self, cell):
290 """Convert a code cell
290 """Convert a code cell
291
291
292 Returns list."""
292 Returns list."""
293 raise NotImplementedError
293 raise NotImplementedError
294
294
295 def render_markdown(self, cell):
295 def render_markdown(self, cell):
296 """convert a markdown cell
296 """convert a markdown cell
297
297
298 Returns list."""
298 Returns list."""
299 raise NotImplementedError
299 raise NotImplementedError
300
300
301 def _img_lines(self, img_file):
301 def _img_lines(self, img_file):
302 """Return list of lines to include an image file."""
302 """Return list of lines to include an image file."""
303 # Note: subclasses may choose to implement format-specific _FMT_lines
303 # Note: subclasses may choose to implement format-specific _FMT_lines
304 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
304 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
305 raise NotImplementedError
305 raise NotImplementedError
306
306
307 def render_display_data(self, output):
307 def render_display_data(self, output):
308 """convert display data from the output of a code cell
308 """convert display data from the output of a code cell
309
309
310 Returns list.
310 Returns list.
311 """
311 """
312 for fmt in self.display_data_priority:
312 for fmt in self.display_data_priority:
313 if fmt in output:
313 if fmt in output:
314 break
314 break
315 else:
315 else:
316 for fmt in output:
316 for fmt in output:
317 if fmt != 'output_type':
317 if fmt != 'output_type':
318 break
318 break
319 else:
319 else:
320 raise RuntimeError('no display data')
320 raise RuntimeError('no display data')
321
321
322 # Is it an image?
322 # Is it an image?
323 if fmt in ['png', 'svg', 'jpg', 'pdf']:
323 if fmt in ['png', 'svg', 'jpg', 'pdf']:
324 img_file = self._new_figure(output[fmt], fmt)
324 img_file = self._new_figure(output[fmt], fmt)
325 # Subclasses can have format-specific render functions (e.g.,
325 # Subclasses can have format-specific render functions (e.g.,
326 # latex has to auto-convert all SVG to PDF first).
326 # latex has to auto-convert all SVG to PDF first).
327 lines_fun = getattr(self, '_%s_lines' % fmt, None)
327 lines_fun = getattr(self, '_%s_lines' % fmt, None)
328 if not lines_fun:
328 if not lines_fun:
329 lines_fun = self._img_lines
329 lines_fun = self._img_lines
330 lines = lines_fun(img_file)
330 lines = lines_fun(img_file)
331 else:
331 else:
332 lines_fun = self.dispatch_display_format(fmt)
332 lines_fun = self.dispatch_display_format(fmt)
333 lines = lines_fun(output)
333 lines = lines_fun(output)
334
334
335 return lines
335 return lines
336
336
337 def render_raw(self, cell):
337 def render_raw(self, cell):
338 """convert a cell with raw text
338 """convert a cell with raw text
339
339
340 Returns list."""
340 Returns list."""
341 raise NotImplementedError
341 raise NotImplementedError
342
342
343 def render_unknown(self, cell):
343 def render_unknown(self, cell):
344 """Render cells of unkown type
344 """Render cells of unkown type
345
345
346 Returns list."""
346 Returns list."""
347 data = pprint.pformat(cell)
347 data = pprint.pformat(cell)
348 logging.warning('Unknown cell: %s' % cell.cell_type)
348 logging.warning('Unknown cell: %s' % cell.cell_type)
349 return self._unknown_lines(data)
349 return self._unknown_lines(data)
350
350
351 def render_unknown_display(self, output, type):
351 def render_unknown_display(self, output, type):
352 """Render cells of unkown type
352 """Render cells of unkown type
353
353
354 Returns list."""
354 Returns list."""
355 data = pprint.pformat(output)
355 data = pprint.pformat(output)
356 logging.warning('Unknown output: %s' % output.output_type)
356 logging.warning('Unknown output: %s' % output.output_type)
357 return self._unknown_lines(data)
357 return self._unknown_lines(data)
358
358
359 def render_stream(self, output):
359 def render_stream(self, output):
360 """render the stream part of an output
360 """render the stream part of an output
361
361
362 Returns list.
362 Returns list.
363
363
364 Identical to render_display_format_text
364 Identical to render_display_format_text
365 """
365 """
366 return self.render_display_format_text(output)
366 return self.render_display_format_text(output)
367
367
368 def render_pyout(self, output):
368 def render_pyout(self, output):
369 """convert pyout part of a code cell
369 """convert pyout part of a code cell
370
370
371 Returns list."""
371 Returns list."""
372 raise NotImplementedError
372 raise NotImplementedError
373
373
374 def render_pyerr(self, output):
374 def render_pyerr(self, output):
375 """convert pyerr part of a code cell
375 """convert pyerr part of a code cell
376
376
377 Returns list."""
377 Returns list."""
378 raise NotImplementedError
378 raise NotImplementedError
379
379
380 def _unknown_lines(self, data):
380 def _unknown_lines(self, data):
381 """Return list of lines for an unknown cell.
381 """Return list of lines for an unknown cell.
382
382
383 Parameters
383 Parameters
384 ----------
384 ----------
385 data : str
385 data : str
386 The content of the unknown data as a single string.
386 The content of the unknown data as a single string.
387 """
387 """
388 raise NotImplementedError
388 raise NotImplementedError
389
389
390 # These are the possible format types in an output node
390 # These are the possible format types in an output node
391
391
392 def render_display_format_text(self, output):
392 def render_display_format_text(self, output):
393 """render the text part of an output
393 """render the text part of an output
394
394
395 Returns list.
395 Returns list.
396 """
396 """
397 raise NotImplementedError
397 raise NotImplementedError
398
398
399 def render_display_format_html(self, output):
399 def render_display_format_html(self, output):
400 """render the html part of an output
400 """render the html part of an output
401
401
402 Returns list.
402 Returns list.
403 """
403 """
404 raise NotImplementedError
404 raise NotImplementedError
405
405
406 def render_display_format_latex(self, output):
406 def render_display_format_latex(self, output):
407 """render the latex part of an output
407 """render the latex part of an output
408
408
409 Returns list.
409 Returns list.
410 """
410 """
411 raise NotImplementedError
411 raise NotImplementedError
412
412
413 def render_display_format_json(self, output):
413 def render_display_format_json(self, output):
414 """render the json part of an output
414 """render the json part of an output
415
415
416 Returns list.
416 Returns list.
417 """
417 """
418 raise NotImplementedError
418 raise NotImplementedError
419
419
420 def render_display_format_javascript(self, output):
420 def render_display_format_javascript(self, output):
421 """render the javascript part of an output
421 """render the javascript part of an output
422
422
423 Returns list.
423 Returns list.
424 """
424 """
425 raise NotImplementedError
425 raise NotImplementedError
@@ -1,98 +1,98 b''
1 from nbconvert import main, converters , NbconvertApp
1 from nbconvert import converters, NbconvertApp
2 from converters.rst import ConverterRST
2 from converters.rst import ConverterRST
3 import nose.tools as nt
3 import nose.tools as nt
4
4
5 import os
5 import os
6 import glob
6 import glob
7 import tempfile
7 import tempfile
8 import shutil
8 import shutil
9 from IPython.nbformat import current as nbformat
9 from IPython.nbformat import current as nbformat
10
10
11
11
12 class TestSimple(object):
12 class TestSimple(object):
13 def setUp(self):
13 def setUp(self):
14 """
14 """
15 Create a new temporary directory and copy the input file
15 Create a new temporary directory and copy the input file
16 'test.ipynb' there.
16 'test.ipynb' there.
17 """
17 """
18 self.fname = 'test.ipynb'
18 self.fname = 'test.ipynb'
19 self.basename = os.path.splitext(self.fname)[0]
19 self.basename = os.path.splitext(self.fname)[0]
20 self.wrkdir = tempfile.mkdtemp()
20 self.wrkdir = tempfile.mkdtemp()
21 self.infile = os.path.join(self.wrkdir, self.fname)
21 self.infile = os.path.join(self.wrkdir, self.fname)
22 shutil.copy(os.path.join('tests', self.fname), self.infile)
22 shutil.copy(os.path.join('tests', self.fname), self.infile)
23
23
24 def tearDown(self):
24 def tearDown(self):
25 shutil.rmtree(self.wrkdir)
25 shutil.rmtree(self.wrkdir)
26
26
27 def outfile_exists(self, fmt):
27 def outfile_exists(self, fmt):
28 extension = converters[fmt].extension
28 extension = converters[fmt].extension
29 print("==out to ==>>",os.path.join(self.wrkdir,
29 print("==out to ==>>",os.path.join(self.wrkdir,
30 self.basename + '.' + extension))
30 self.basename + '.' + extension))
31 return os.path.exists(os.path.join(self.wrkdir,
31 return os.path.exists(os.path.join(self.wrkdir,
32 self.basename + '.' + extension))
32 self.basename + '.' + extension))
33
33
34 def test_simple(self):
34 def test_simple(self):
35 c = ConverterRST(infile=self.infile)
35 c = ConverterRST(infile=self.infile)
36 f = c.render()
36 f = c.render()
37 nt.assert_true(f.endswith('.rst'), 'changed file extension to rst')
37 nt.assert_true(f.endswith('.rst'), 'changed file extension to rst')
38
38
39 def run_main(self, fmt):
39 def run_main(self, fmt):
40 """
40 """
41 Run the 'main' method to convert the input file to the given
41 Run the 'main' method to convert the input file to the given
42 format and check that the expected output file exists.
42 format and check that the expected output file exists.
43 """
43 """
44 app = NbconvertApp.instance()
44 app = NbconvertApp.instance()
45 app.initialize()
45 app.initialize()
46 app.extra_args = [self.infile]
46 app.extra_args = [self.infile]
47 app.fmt = fmt
47 app.fmt = fmt
48 app.start()
48 app.start()
49
49
50 app.run()
50 app.run()
51 nt.assert_true(self.outfile_exists(fmt))
51 nt.assert_true(self.outfile_exists(fmt))
52
52
53 def test_main(self):
53 def test_main(self):
54 """
54 """
55 Test main entry point with all known formats.
55 Test main entry point with all known formats.
56 """
56 """
57 # Exclude reveal from this test because:
57 # Exclude reveal from this test because:
58 # 1- AssertionError exception when there is not slideshow metadata.
58 # 1- AssertionError exception when there is not slideshow metadata.
59 # 2- outfile_exists method can not find properly the html file.
59 # 2- outfile_exists method can not find properly the html file.
60 converters_copy = dict(converters)
60 converters_copy = dict(converters)
61 del converters_copy['reveal']
61 del converters_copy['reveal']
62 for fmt in converters_copy:
62 for fmt in converters_copy:
63 yield self.run_main, fmt
63 yield self.run_main, fmt
64
64
65 def test_render_heading(self):
65 def test_render_heading(self):
66 """
66 """
67 Unit test for cell type "heading"
67 Unit test for cell type "heading"
68 """
68 """
69 # Generate and test heading cells level 1-6
69 # Generate and test heading cells level 1-6
70 for level in xrange(1, 7):
70 for level in xrange(1, 7):
71 cell = {
71 cell = {
72 'cell_type': 'heading',
72 'cell_type': 'heading',
73 'level': level,
73 'level': level,
74 'source': ['Test for heading type H{0}'.format(level)]
74 'source': ['Test for heading type H{0}'.format(level)]
75 }
75 }
76
76
77 # Convert cell dictionaries to NotebookNode
77 # Convert cell dictionaries to NotebookNode
78 cell_nb = nbformat.NotebookNode(cell)
78 cell_nb = nbformat.NotebookNode(cell)
79
79
80 # Make sure "source" attribute is uniconde not list.
80 # Make sure "source" attribute is uniconde not list.
81 # For some reason, creating a NotebookNode manually like
81 # For some reason, creating a NotebookNode manually like
82 # this isn't converting source to a string like using
82 # this isn't converting source to a string like using
83 # the create-from-file routine.
83 # the create-from-file routine.
84 if type(cell_nb.source) is list:
84 if type(cell_nb.source) is list:
85 cell_nb.source = '\n'.join(cell_nb.source)
85 cell_nb.source = '\n'.join(cell_nb.source)
86
86
87 # Render to rst
87 # Render to rst
88 c = ConverterRST()
88 c = ConverterRST()
89 rst_list = c.render_heading(cell_nb)
89 rst_list = c.render_heading(cell_nb)
90
90
91 # render should return a list
91 # render should return a list
92 nt.assert_true(isinstance(rst_list, list))
92 nt.assert_true(isinstance(rst_list, list))
93 rst_str = "".join(rst_list)
93 rst_str = "".join(rst_list)
94
94
95 # Confirm rst content
95 # Confirm rst content
96 chk_str = "Test for heading type H{0}\n{1}\n".format(
96 chk_str = "Test for heading type H{0}\n{1}\n".format(
97 level, c.heading_level[level] * 24)
97 level, c.heading_level[level] * 24)
98 nt.assert_equal(rst_str, chk_str)
98 nt.assert_equal(rst_str, chk_str)
General Comments 0
You need to be logged in to leave comments. Login now