##// END OF EJS Templates
PEP8
David Warde-Farley -
Show More
@@ -1,323 +1,323 b''
1 1 from __future__ import print_function, absolute_import
2 2 from converters.utils import remove_fake_files_url
3 3
4 4 # Stdlib
5 5 import codecs
6 6 import io
7 7 import logging
8 8 import os
9 9 import pprint
10 10 from types import FunctionType
11 11
12 12 # From IPython
13 13 from IPython.nbformat import current as nbformat
14 14
15 15 # local
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Class declarations
19 19 #-----------------------------------------------------------------------------
20 20
21 21 class ConversionException(Exception):
22 22 pass
23 23
24 24 class DocStringInheritor(type):
25 25 """
26 26 This metaclass will walk the list of bases until the desired
27 27 superclass method is found AND if that method has a docstring and only
28 28 THEN does it attach the superdocstring to the derived class method.
29 29
30 30 Please use carefully, I just did the metaclass thing by following
31 31 Michael Foord's Metaclass tutorial
32 32 (http://www.voidspace.org.uk/python/articles/metaclasses.shtml), I may
33 33 have missed a step or two.
34 34
35 35 source:
36 36 http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
37 37 by Paul McGuire
38 38 """
39 39 def __new__(meta, classname, bases, classDict):
40 40 newClassDict = {}
41 41 for attributeName, attribute in classDict.items():
42 42 if type(attribute) == FunctionType:
43 43 # look through bases for matching function by name
44 44 for baseclass in bases:
45 45 if hasattr(baseclass, attributeName):
46 46 basefn = getattr(baseclass, attributeName)
47 47 if basefn.__doc__:
48 48 attribute.__doc__ = basefn.__doc__
49 49 break
50 50 newClassDict[attributeName] = attribute
51 51 return type.__new__(meta, classname, bases, newClassDict)
52 52
53 53 class Converter(object):
54 54 __metaclass__ = DocStringInheritor
55 55 default_encoding = 'utf-8'
56 56 extension = str()
57 57 figures_counter = 0
58 58 infile = str()
59 59 infile_dir = str()
60 60 infile_root = str()
61 61 files_dir = str()
62 62 with_preamble = True
63 63 user_preamble = None
64 64 output = unicode()
65 65 raw_as_verbatim = False
66
66
67 67 def __init__(self, infile):
68 68 self.infile = infile
69 69 self.infile_dir, infile_root = os.path.split(infile)
70 70 infile_root = os.path.splitext(infile_root)[0]
71 71 files_dir = os.path.join(self.infile_dir, infile_root + '_files')
72 72 if not os.path.isdir(files_dir):
73 73 os.mkdir(files_dir)
74 74 self.infile_root = infile_root
75 75 self.files_dir = files_dir
76 76 self.outbase = os.path.join(self.infile_dir, infile_root)
77 77
78 78 def __del__(self):
79 79 if os.path.isdir(self.files_dir) and not os.listdir(self.files_dir):
80 80 os.rmdir(self.files_dir)
81 81
82 82 def dispatch(self, cell_type):
83 83 """return cell_type dependent render method, for example render_code
84 84 """
85 85 return getattr(self, 'render_' + cell_type, self.render_unknown)
86 86
87 87 def dispatch_display_format(self, format):
88 88 """return output_type dependent render method, for example render_output_text
89 89 """
90 90 return getattr(self, 'render_display_format_' + format, self.render_unknown_display)
91 91
92 92 def convert(self, cell_separator='\n'):
93 93 """
94 94 Generic method to converts notebook to a string representation.
95 95
96 96 This is accomplished by dispatching on the cell_type, so subclasses of
97 97 Convereter class do not need to re-implement this method, but just
98 98 need implementation for the methods that will be dispatched.
99 99
100 100 Parameters
101 101 ----------
102 102 cell_separator : string
103 103 Character or string to join cells with. Default is "\n"
104 104
105 105 Returns
106 106 -------
107 107 out : string
108 108 """
109 109 lines = []
110 110 lines.extend(self.optional_header())
111 111 lines.extend(self.main_body(cell_separator))
112 112 lines.extend(self.optional_footer())
113 113 return u'\n'.join(lines)
114 114
115 115 def main_body(self, cell_separator='\n'):
116 116 converted_cells = []
117 117 for worksheet in self.nb.worksheets:
118 118 for cell in worksheet.cells:
119 119 #print(cell.cell_type) # dbg
120 120 conv_fn = self.dispatch(cell.cell_type)
121 121 if cell.cell_type in ('markdown', 'raw'):
122 122 remove_fake_files_url(cell)
123 123 converted_cells.append('\n'.join(conv_fn(cell)))
124 124 cell_lines = cell_separator.join(converted_cells).split('\n')
125 125 return cell_lines
126 126
127 127 def render(self):
128 128 "read, convert, and save self.infile"
129 129 if not hasattr(self, 'nb'):
130 130 self.read()
131 131 self.output = self.convert()
132 132 assert(type(self.output) == unicode)
133 133 return self.save()
134 134
135 135 def read(self):
136 136 "read and parse notebook into NotebookNode called self.nb"
137 137 with open(self.infile) as f:
138 138 self.nb = nbformat.read(f, 'json')
139 139
140 140 def save(self, outfile=None, encoding=None):
141 141 "read and parse notebook into self.nb"
142 142 if outfile is None:
143 143 outfile = self.outbase + '.' + self.extension
144 144 if encoding is None:
145 145 encoding = self.default_encoding
146 146 with io.open(outfile, 'w', encoding=encoding) as f:
147 147 f.write(self.output)
148 148 return os.path.abspath(outfile)
149 149
150 150 def optional_header(self):
151 151 """
152 152 Optional header to insert at the top of the converted notebook
153 153
154 154 Returns a list
155 155 """
156 156 return []
157 157
158 158 def optional_footer(self):
159 159 """
160 160 Optional footer to insert at the end of the converted notebook
161 161
162 162 Returns a list
163 163 """
164 164 return []
165 165
166 166 def _new_figure(self, data, fmt):
167 167 """Create a new figure file in the given format.
168 168
169 169 Returns a path relative to the input file.
170 170 """
171 figname = '%s_fig_%02i.%s' % (self.infile_root,
171 figname = '%s_fig_%02i.%s' % (self.infile_root,
172 172 self.figures_counter, fmt)
173 173 self.figures_counter += 1
174 174 fullname = os.path.join(self.files_dir, figname)
175 175
176 176 # Binary files are base64-encoded, SVG is already XML
177 177 if fmt in ('png', 'jpg', 'pdf'):
178 178 data = data.decode('base64')
179 179 fopen = lambda fname: open(fname, 'wb')
180 180 else:
181 181 fopen = lambda fname: codecs.open(fname, 'wb', self.default_encoding)
182
182
183 183 with fopen(fullname) as f:
184 184 f.write(data)
185
185
186 186 return fullname
187 187
188 188 def render_heading(self, cell):
189 189 """convert a heading cell
190 190
191 191 Returns list."""
192 192 raise NotImplementedError
193 193
194 194 def render_code(self, cell):
195 195 """Convert a code cell
196 196
197 197 Returns list."""
198 198 raise NotImplementedError
199 199
200 200 def render_markdown(self, cell):
201 201 """convert a markdown cell
202 202
203 203 Returns list."""
204 204 raise NotImplementedError
205 205
206 206 def _img_lines(self, img_file):
207 207 """Return list of lines to include an image file."""
208 208 # Note: subclasses may choose to implement format-specific _FMT_lines
209 209 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
210 210 raise NotImplementedError
211 211
212 212 def render_display_data(self, output):
213 213 """convert display data from the output of a code cell
214 214
215 215 Returns list.
216 216 """
217 217 lines = []
218 218
219 219 for fmt in output.keys():
220 220 if fmt in ['png', 'svg', 'jpg', 'pdf']:
221 221 img_file = self._new_figure(output[fmt], fmt)
222 222 # Subclasses can have format-specific render functions (e.g.,
223 223 # latex has to auto-convert all SVG to PDF first).
224 224 lines_fun = getattr(self, '_%s_lines' % fmt, None)
225 225 if not lines_fun:
226 226 lines_fun = self._img_lines
227 227 lines.extend(lines_fun(img_file))
228 228 elif fmt != 'output_type':
229 229 conv_fn = self.dispatch_display_format(fmt)
230 230 lines.extend(conv_fn(output))
231 231 return lines
232 232
233 233 def render_raw(self, cell):
234 234 """convert a cell with raw text
235 235
236 236 Returns list."""
237 237 raise NotImplementedError
238 238
239 239 def render_unknown(self, cell):
240 240 """Render cells of unkown type
241 241
242 242 Returns list."""
243 243 data = pprint.pformat(cell)
244 244 logging.warning('Unknown cell: %s' % cell.cell_type)
245 245 return self._unknown_lines(data)
246 246
247 247 def render_unknown_display(self, output, type):
248 248 """Render cells of unkown type
249 249
250 250 Returns list."""
251 251 data = pprint.pformat(output)
252 252 logging.warning('Unknown output: %s' % output.output_type)
253 253 return self._unknown_lines(data)
254 254
255 255 def render_stream(self, output):
256 256 """render the stream part of an output
257 257
258 258 Returns list.
259 259
260 260 Identical to render_display_format_text
261 261 """
262 262 return self.render_display_format_text(output)
263 263
264 264 def render_pyout(self, output):
265 265 """convert pyout part of a code cell
266 266
267 267 Returns list."""
268 268 raise NotImplementedError
269 269
270 270
271 271 def render_pyerr(self, output):
272 272 """convert pyerr part of a code cell
273 273
274 274 Returns list."""
275 275 raise NotImplementedError
276 276
277 277 def _unknown_lines(self, data):
278 278 """Return list of lines for an unknown cell.
279 279
280 280 Parameters
281 281 ----------
282 282 data : str
283 283 The content of the unknown data as a single string.
284 284 """
285 285 raise NotImplementedError
286 286
287 287 # These are the possible format types in an output node
288 288
289 289 def render_display_format_text(self, output):
290 290 """render the text part of an output
291 291
292 292 Returns list.
293 293 """
294 294 raise NotImplementedError
295 295
296 296 def render_display_format_html(self, output):
297 297 """render the html part of an output
298 298
299 299 Returns list.
300 300 """
301 301 raise NotImplementedError
302 302
303 303 def render_display_format_latex(self, output):
304 304 """render the latex part of an output
305 305
306 306 Returns list.
307 307 """
308 308 raise NotImplementedError
309 309
310 310 def render_display_format_json(self, output):
311 311 """render the json part of an output
312 312
313 313 Returns list.
314 314 """
315 315 raise NotImplementedError
316 316
317 317 def render_display_format_javascript(self, output):
318 318 """render the javascript part of an output
319 319
320 320 Returns list.
321 321 """
322 322 raise NotImplementedError
323 323
@@ -1,82 +1,82 b''
1 1 from converters.base import Converter
2 2 from converters.utils import cell_to_lines
3 3 from shutil import rmtree
4 4 import json
5 5
6 6 class ConverterNotebook(Converter):
7 7 """
8 8 A converter that is essentially a null-op.
9 9 This exists so it can be subclassed
10 for custom handlers of .ipynb files
10 for custom handlers of .ipynb files
11 11 that create new .ipynb files.
12 12
13 13 What distinguishes this from JSONWriter is that
14 14 subclasses can specify what to do with each type of cell.
15 15
16 16 Writes out a notebook file.
17 17
18 18 """
19 19 extension = 'ipynb'
20 20
21 21 def __init__(self, infile, outbase):
22 22 Converter.__init__(self, infile)
23 23 self.outbase = outbase
24 24 rmtree(self.files_dir)
25 25
26 26 def convert(self):
27 27 return unicode(json.dumps(json.loads(Converter.convert(self, ',')), indent=1, sort_keys=True))
28 28
29 29 def optional_header(self):
30 30 s = \
31 31 """{
32 32 "metadata": {
33 33 "name": "%(name)s"
34 34 },
35 35 "nbformat": 3,
36 36 "worksheets": [
37 37 {
38 38 "cells": [""" % {'name':self.outbase}
39 39
40 40 return s.split('\n')
41 41
42 42 def optional_footer(self):
43 43 s = \
44 44 """]
45 45 }
46 46 ]
47 47 }"""
48 48 return s.split('\n')
49 49
50 50 def render_heading(self, cell):
51 51 return cell_to_lines(cell)
52 52
53 53 def render_code(self, cell):
54 54 return cell_to_lines(cell)
55 55
56 56 def render_markdown(self, cell):
57 57 return cell_to_lines(cell)
58 58
59 59 def render_raw(self, cell):
60 60 return cell_to_lines(cell)
61 61
62 62 def render_pyout(self, output):
63 63 return cell_to_lines(output)
64 64
65 65 def render_pyerr(self, output):
66 66 return cell_to_lines(output)
67 67
68 68 def render_display_format_text(self, output):
69 69 return [output.text]
70 70
71 71 def render_display_format_html(self, output):
72 72 return [output.html]
73 73
74 74 def render_display_format_latex(self, output):
75 75 return [output.latex]
76 76
77 77 def render_display_format_json(self, output):
78 78 return [output.json]
79 79
80 80
81 81 def render_display_format_javascript(self, output):
82 82 return [output.javascript]
@@ -1,46 +1,48 b''
1 1 import os
2 2 import io
3 3 import nose.tools as nt
4 from nbconvert import *
4 from nbconvert import (
5 ConverterLaTeX, ConverterMarkdown, ConverterPy, ConverterHTML
6 )
5 7 from nose.tools import nottest
6 8
7 9
8 10 def test_evens():
9 ######
10 # for now, we don't need to really run inkscape to extract svg
11 ######
12 # for now, we don't need to really run inkscape to extract svg
11 13 # from file, on unix, for test, we monkeypathc it to 'true'
12 14 # which does not fail as doing anything.
13 15 ####
14 16 ConverterLaTeX.inkscape = 'true'
15 17
16 # commenting rst for now as travis build
18 # commenting rst for now as travis build
17 19 # fail because of pandoc version.
18 20 converters = [
19 21 #(ConverterRST,'rst'),
20 (ConverterMarkdown,'md'),
21 (ConverterLaTeX,'tex'),
22 (ConverterPy,'py'),
23 (ConverterHTML,'html')
22 (ConverterMarkdown, 'md'),
23 (ConverterLaTeX, 'tex'),
24 (ConverterPy, 'py'),
25 (ConverterHTML, 'html')
24 26 ]
25 27 reflist = [
26 28 'tests/ipynbref/IntroNumPy.orig'
27 29 ]
28 for root in reflist :
29 for conv,ext in converters:
30 yield test_conversion, conv,root+'.ipynb',root+'.'+ext
30 for root in reflist:
31 for conv, ext in converters:
32 yield test_conversion, conv, root + '.ipynb', root + '.' + ext
33
31 34
32 35 @nottest
33 36 def compfiles(stra, strb):
34 nt.assert_equal(map(unicode.strip,stra.split('\n')),map(unicode.strip,strb.split('\n')))
37 nt.assert_equal(map(unicode.strip, stra.split('\n')),
38 map(unicode.strip, strb.split('\n')))
39
35 40
36 41 @nottest
37 42 def test_conversion(ConverterClass, ipynb, ref_file):
38
39 43 converter = ConverterClass(ipynb)
40 44 converter.read()
41 cv =converter.convert()
45 cv = converter.convert()
42 46 with io.open(ref_file) as ref:
43 47 value = ref.read()
44 compfiles(cv,value)
45
46
48 compfiles(cv, value)
@@ -1,70 +1,71 b''
1 1 from nbconvert import ConverterRST, main
2 2 import nose.tools as nt
3 3
4 4 import os
5 5 import glob
6 6 from IPython.nbformat import current as nbformat
7 7
8 8 fname = 'tests/test.ipynb'
9 9 out_fname = 'tests/test.rst'
10 10
11 11
12 12 def clean_dir():
13 13 "Remove .rst files created during conversion"
14 14 map(os.remove, glob.glob("./tests/*.rst"))
15 15 map(os.remove, glob.glob("./tests/*.png"))
16 16 map(os.remove, glob.glob("./tests/*.html"))
17 17 map(os.remove, glob.glob("./tests/test_files/*"))
18 18
19 19
20 20 @nt.with_setup(clean_dir, clean_dir)
21 21 def test_simple():
22 22 c = ConverterRST(fname)
23 23 f = c.render()
24 24 nt.assert_true('rst' in f, 'changed file extension to rst')
25 25
26 26
27 27 @nt.with_setup(clean_dir, clean_dir)
28 28 def test_main():
29 29 """
30 30 Test main entry point
31 31 """
32 32 main(fname)
33 33 nt.assert_true(os.path.exists(out_fname))
34 34
35 35
36 36 def test_render_heading():
37 37 """ Unit test for cell type "heading" """
38 38 # Generate and test heading cells level 1-6
39 39 for level in xrange(1, 7):
40 40 cell = {
41 41 'cell_type': 'heading',
42 'level' : level,
43 'source' : ['Test for heading type H{0}'.format(level)]
42 'level': level,
43 'source': ['Test for heading type H{0}'.format(level)]
44 44 }
45 45 # Convert cell dictionaries to NotebookNode
46 46 cell_nb = nbformat.NotebookNode(cell)
47 47 # Make sure "source" attribute is uniconde not list.
48 48 # For some reason, creating a NotebookNode manually like
49 49 # this isn't converting source to a string like using
50 50 # the create-from-file routine.
51 51 if type(cell_nb.source) is list:
52 52 cell_nb.source = '\n'.join(cell_nb.source)
53 53 # Render to rst
54 54 c = ConverterRST('')
55 55 rst_list = c.render_heading(cell_nb)
56 nt.assert_true(isinstance(rst_list, list)) # render should return a list
56 # render should return a list
57 nt.assert_true(isinstance(rst_list, list))
57 58 rst_str = "".join(rst_list)
58 59 # Confirm rst content
59 60 chk_str = "Test for heading type H{0}\n{1}\n".format(
60 61 level, c.heading_level[level] * 24)
61 62 nt.assert_equal(rst_str, chk_str)
62 63
63 64
64 65 @nt.with_setup(clean_dir, clean_dir)
65 66 def test_main_html():
66 67 """
67 68 Test main entry point
68 69 """
69 70 main(fname, format='html')
70 71 nt.assert_true(os.path.exists('tests/test.html'))
General Comments 0
You need to be logged in to leave comments. Login now