##// END OF EJS Templates
added documentation to more functions and methods
Paul Ivanov -
Show More
@@ -1,1461 +1,1490 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Convert IPython notebooks to other formats, such as ReST, and HTML.
2 """Convert IPython notebooks to other formats, such as ReST, and HTML.
3
3
4 Example:
4 Example:
5 ./nbconvert.py --format rst file.ipynb
5 ./nbconvert.py --format rst file.ipynb
6
6
7 Produces 'file.rst', along with auto-generated figure files
7 Produces 'file.rst', along with auto-generated figure files
8 called nb_figure_NN.png.
8 called nb_figure_NN.png.
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # Stdlib
15 # Stdlib
16 import codecs
16 import codecs
17 import io
17 import io
18 import logging
18 import logging
19 import os
19 import os
20 import pprint
20 import pprint
21 import re
21 import re
22 import subprocess
22 import subprocess
23 import sys
23 import sys
24 import json
24 import json
25 import copy
25 import copy
26 from shutil import rmtree
26 from shutil import rmtree
27 from markdown import markdown
27 from markdown import markdown
28
28
29 inkscape = 'inkscape'
29 inkscape = 'inkscape'
30 if sys.platform == 'darwin':
30 if sys.platform == 'darwin':
31 inkscape = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
31 inkscape = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
32 if not os.path.exists(inkscape):
32 if not os.path.exists(inkscape):
33 inkscape = None
33 inkscape = None
34
34
35 # From IPython
35 # From IPython
36 from IPython.external import argparse
36 from IPython.external import argparse
37 from IPython.nbformat import current as nbformat
37 from IPython.nbformat import current as nbformat
38 from IPython.utils.text import indent
38 from IPython.utils.text import indent
39 from IPython.nbformat.v3.nbjson import BytesEncoder
39 from IPython.nbformat.v3.nbjson import BytesEncoder
40 from IPython.utils import path, py3compat
40 from IPython.utils import path, py3compat
41
41
42 # local
42 # local
43 from decorators import DocInherit
43 from decorators import DocInherit
44 from lexers import IPythonLexer
44 from lexers import IPythonLexer
45
45
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Utility functions
48 # Utility functions
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 def DocInherit(f):
51 def DocInherit(f):
52 return f
52 return f
53
53
54 def remove_fake_files_url(cell):
54 def remove_fake_files_url(cell):
55 """Remove from the cell source the /files/ pseudo-path we use.
55 """Remove from the cell source the /files/ pseudo-path we use.
56 """
56 """
57 src = cell.source
57 src = cell.source
58 cell.source = src.replace('/files/', '')
58 cell.source = src.replace('/files/', '')
59
59
60
60
61 # ANSI color functions:
61 # ANSI color functions:
62
62
63 def remove_ansi(src):
63 def remove_ansi(src):
64 """Strip all ANSI color escape sequences from input string.
64 """Strip all ANSI color escape sequences from input string.
65
65
66 Parameters
66 Parameters
67 ----------
67 ----------
68 src : string
68 src : string
69
69
70 Returns
70 Returns
71 -------
71 -------
72 string
72 string
73 """
73 """
74 return re.sub(r'\033\[(0|\d;\d\d)m', '', src)
74 return re.sub(r'\033\[(0|\d;\d\d)m', '', src)
75
75
76
76
77 def ansi2html(txt):
77 def ansi2html(txt):
78 """Render ANSI colors as HTML colors
78 """Render ANSI colors as HTML colors
79
79
80 This is equivalent to util.fixConsole in utils.js
80 This is equivalent to util.fixConsole in utils.js
81
81
82 Parameters
82 Parameters
83 ----------
83 ----------
84 txt : string
84 txt : string
85
85
86 Returns
86 Returns
87 -------
87 -------
88 string
88 string
89 """
89 """
90
90
91 ansi_colormap = {
91 ansi_colormap = {
92 '30': 'ansiblack',
92 '30': 'ansiblack',
93 '31': 'ansired',
93 '31': 'ansired',
94 '32': 'ansigreen',
94 '32': 'ansigreen',
95 '33': 'ansiyellow',
95 '33': 'ansiyellow',
96 '34': 'ansiblue',
96 '34': 'ansiblue',
97 '35': 'ansipurple',
97 '35': 'ansipurple',
98 '36': 'ansicyan',
98 '36': 'ansicyan',
99 '37': 'ansigrey',
99 '37': 'ansigrey',
100 '01': 'ansibold',
100 '01': 'ansibold',
101 }
101 }
102
102
103 # do ampersand first
103 # do ampersand first
104 txt = txt.replace('&', '&')
104 txt = txt.replace('&', '&')
105 html_escapes = {
105 html_escapes = {
106 '<': '&lt;',
106 '<': '&lt;',
107 '>': '&gt;',
107 '>': '&gt;',
108 "'": '&apos;',
108 "'": '&apos;',
109 '"': '&quot;',
109 '"': '&quot;',
110 '`': '&#96;',
110 '`': '&#96;',
111 }
111 }
112 for c, escape in html_escapes.iteritems():
112 for c, escape in html_escapes.iteritems():
113 txt = txt.replace(c, escape)
113 txt = txt.replace(c, escape)
114
114
115 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
115 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
116 m = ansi_re.search(txt)
116 m = ansi_re.search(txt)
117 opened = False
117 opened = False
118 cmds = []
118 cmds = []
119 opener = ''
119 opener = ''
120 closer = ''
120 closer = ''
121 while m:
121 while m:
122 cmds = m.groups()[0].split(';')
122 cmds = m.groups()[0].split(';')
123 closer = '</span>' if opened else ''
123 closer = '</span>' if opened else ''
124 opened = len(cmds) > 1 or cmds[0] != '0'*len(cmds[0]);
124 opened = len(cmds) > 1 or cmds[0] != '0'*len(cmds[0]);
125 classes = []
125 classes = []
126 for cmd in cmds:
126 for cmd in cmds:
127 if cmd in ansi_colormap:
127 if cmd in ansi_colormap:
128 classes.append(ansi_colormap.get(cmd))
128 classes.append(ansi_colormap.get(cmd))
129
129
130 if classes:
130 if classes:
131 opener = '<span class="%s">' % (' '.join(classes))
131 opener = '<span class="%s">' % (' '.join(classes))
132 else:
132 else:
133 opener = ''
133 opener = ''
134 txt = re.sub(ansi_re, closer + opener, txt, 1)
134 txt = re.sub(ansi_re, closer + opener, txt, 1)
135
135
136 m = ansi_re.search(txt)
136 m = ansi_re.search(txt)
137
137
138 if opened:
138 if opened:
139 txt += '</span>'
139 txt += '</span>'
140 return txt
140 return txt
141
141
142
142
143 # Pandoc-dependent code
143 # Pandoc-dependent code
144
144
145 def markdown2latex(src):
145 def markdown2latex(src):
146 """Convert a markdown string to LaTeX via pandoc.
146 """Convert a markdown string to LaTeX via pandoc.
147
147
148 This function will raise an error if pandoc is not installed.
148 This function will raise an error if pandoc is not installed.
149
149
150 Any error messages generated by pandoc are printed to stderr.
150 Any error messages generated by pandoc are printed to stderr.
151
151
152 Parameters
152 Parameters
153 ----------
153 ----------
154 src : string
154 src : string
155 Input string, assumed to be valid markdown.
155 Input string, assumed to be valid markdown.
156
156
157 Returns
157 Returns
158 -------
158 -------
159 out : string
159 out : string
160 Output as returned by pandoc.
160 Output as returned by pandoc.
161 """
161 """
162 p = subprocess.Popen('pandoc -f markdown -t latex'.split(),
162 p = subprocess.Popen('pandoc -f markdown -t latex'.split(),
163 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
163 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
164 out, err = p.communicate(src.encode('utf-8'))
164 out, err = p.communicate(src.encode('utf-8'))
165 if err:
165 if err:
166 print(err, file=sys.stderr)
166 print(err, file=sys.stderr)
167 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
167 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
168 return unicode(out,'utf-8')
168 return unicode(out,'utf-8')
169
169
170
170
171 def markdown2rst(src):
171 def markdown2rst(src):
172 """Convert a markdown string to LaTeX via pandoc.
172 """Convert a markdown string to LaTeX via pandoc.
173
173
174 This function will raise an error if pandoc is not installed.
174 This function will raise an error if pandoc is not installed.
175
175
176 Any error messages generated by pandoc are printed to stderr.
176 Any error messages generated by pandoc are printed to stderr.
177
177
178 Parameters
178 Parameters
179 ----------
179 ----------
180 src : string
180 src : string
181 Input string, assumed to be valid markdown.
181 Input string, assumed to be valid markdown.
182
182
183 Returns
183 Returns
184 -------
184 -------
185 out : string
185 out : string
186 Output as returned by pandoc.
186 Output as returned by pandoc.
187 """
187 """
188 p = subprocess.Popen('pandoc -f markdown -t rst'.split(),
188 p = subprocess.Popen('pandoc -f markdown -t rst'.split(),
189 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
189 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
190 out, err = p.communicate(src.encode('utf-8'))
190 out, err = p.communicate(src.encode('utf-8'))
191 if err:
191 if err:
192 print(err, file=sys.stderr)
192 print(err, file=sys.stderr)
193 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
193 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
194 return unicode(out,'utf-8')
194 return unicode(out,'utf-8')
195
195
196
196
197 def rst_directive(directive, text=''):
197 def rst_directive(directive, text=''):
198 """
199 Makes ReST directive block and indents any text passed to it.
200 """
198 out = [directive, '']
201 out = [directive, '']
199 if text:
202 if text:
200 out.extend([indent(text), ''])
203 out.extend([indent(text), ''])
201 return out
204 return out
202
205
203
206
204 def coalesce_streams(outputs):
207 def coalesce_streams(outputs):
205 """merge consecutive sequences of stream output into single stream
208 """merge consecutive sequences of stream output into single stream
206
209
207 to prevent extra newlines inserted at flush calls
210 to prevent extra newlines inserted at flush calls
208
211
209 TODO: handle \r deletion
212 TODO: handle \r deletion
210 """
213 """
211 new_outputs = []
214 new_outputs = []
212 last = outputs[0]
215 last = outputs[0]
213 new_outputs = [last]
216 new_outputs = [last]
214 for output in outputs[1:]:
217 for output in outputs[1:]:
215 if (output.output_type == 'stream' and
218 if (output.output_type == 'stream' and
216 last.output_type == 'stream' and
219 last.output_type == 'stream' and
217 last.stream == output.stream
220 last.stream == output.stream
218 ):
221 ):
219 last.text += output.text
222 last.text += output.text
220 else:
223 else:
221 new_outputs.append(output)
224 new_outputs.append(output)
222
225
223 return new_outputs
226 return new_outputs
224
227
225
228
226 #-----------------------------------------------------------------------------
229 #-----------------------------------------------------------------------------
227 # Class declarations
230 # Class declarations
228 #-----------------------------------------------------------------------------
231 #-----------------------------------------------------------------------------
229
232
230 class ConversionException(Exception):
233 class ConversionException(Exception):
231 pass
234 pass
232
235
233
236
234 class Converter(object):
237 class Converter(object):
235 default_encoding = 'utf-8'
238 default_encoding = 'utf-8'
236 extension = str()
239 extension = str()
237 figures_counter = 0
240 figures_counter = 0
238 infile = str()
241 infile = str()
239 infile_dir = str()
242 infile_dir = str()
240 infile_root = str()
243 infile_root = str()
241 files_dir = str()
244 files_dir = str()
242 with_preamble = True
245 with_preamble = True
243 user_preamble = None
246 user_preamble = None
244 output = str()
247 output = str()
245 raw_as_verbatim = False
248 raw_as_verbatim = False
246
249
247 def __init__(self, infile):
250 def __init__(self, infile):
248 self.infile = infile
251 self.infile = infile
249 self.infile_dir, infile_root = os.path.split(infile)
252 self.infile_dir, infile_root = os.path.split(infile)
250 infile_root = os.path.splitext(infile_root)[0]
253 infile_root = os.path.splitext(infile_root)[0]
251 files_dir = os.path.join(self.infile_dir, infile_root + '_files')
254 files_dir = os.path.join(self.infile_dir, infile_root + '_files')
252 if not os.path.isdir(files_dir):
255 if not os.path.isdir(files_dir):
253 os.mkdir(files_dir)
256 os.mkdir(files_dir)
254 self.infile_root = infile_root
257 self.infile_root = infile_root
255 self.files_dir = files_dir
258 self.files_dir = files_dir
256 self.outbase = os.path.join(self.infile_dir, infile_root)
259 self.outbase = os.path.join(self.infile_dir, infile_root)
257
260
258 def __del__(self):
261 def __del__(self):
259 if not os.listdir(self.files_dir):
262 if not os.listdir(self.files_dir):
260 os.rmdir(self.files_dir)
263 os.rmdir(self.files_dir)
261
264
262 def dispatch(self, cell_type):
265 def dispatch(self, cell_type):
263 """return cell_type dependent render method, for example render_code
266 """return cell_type dependent render method, for example render_code
264 """
267 """
265 return getattr(self, 'render_' + cell_type, self.render_unknown)
268 return getattr(self, 'render_' + cell_type, self.render_unknown)
266
269
267 def dispatch_display_format(self, format):
270 def dispatch_display_format(self, format):
268 """return output_type dependent render method, for example render_output_text
271 """return output_type dependent render method, for example render_output_text
269 """
272 """
270 return getattr(self, 'render_display_format_' + format, self.render_unknown_display)
273 return getattr(self, 'render_display_format_' + format, self.render_unknown_display)
271
274
272 def convert(self, cell_separator='\n'):
275 def convert(self, cell_separator='\n'):
276 """
277 Generic method to converts notebook to a string representation.
278
279 This is accomplished by dispatching on the cell_type, so subclasses of
280 Convereter class do not need to re-implement this method, but just
281 need implementation for the methods that will be dispatched.
282
283 Parameters
284 ----------
285 cell_separator : string
286 Character or string to join cells with. Default is "\n"
287
288 Returns
289 -------
290 out : string
291 """
273 lines = []
292 lines = []
274 lines.extend(self.optional_header())
293 lines.extend(self.optional_header())
275 converted_cells = []
294 converted_cells = []
276 for worksheet in self.nb.worksheets:
295 for worksheet in self.nb.worksheets:
277 for cell in worksheet.cells:
296 for cell in worksheet.cells:
278 #print(cell.cell_type) # dbg
297 #print(cell.cell_type) # dbg
279 conv_fn = self.dispatch(cell.cell_type)
298 conv_fn = self.dispatch(cell.cell_type)
280 if cell.cell_type in ('markdown', 'raw'):
299 if cell.cell_type in ('markdown', 'raw'):
281 remove_fake_files_url(cell)
300 remove_fake_files_url(cell)
282 converted_cells.append('\n'.join(conv_fn(cell)))
301 converted_cells.append('\n'.join(conv_fn(cell)))
283 cell_lines = cell_separator.join(converted_cells).split('\n')
302 cell_lines = cell_separator.join(converted_cells).split('\n')
284 lines.extend(cell_lines)
303 lines.extend(cell_lines)
285 lines.extend(self.optional_footer())
304 lines.extend(self.optional_footer())
286 return u'\n'.join(lines)
305 return u'\n'.join(lines)
287
306
288 def render(self):
307 def render(self):
289 "read, convert, and save self.infile"
308 "read, convert, and save self.infile"
290 if not hasattr(self, 'nb'):
309 if not hasattr(self, 'nb'):
291 self.read()
310 self.read()
292 self.output = self.convert()
311 self.output = self.convert()
293 return self.save()
312 return self.save()
294
313
295 def read(self):
314 def read(self):
296 "read and parse notebook into NotebookNode called self.nb"
315 "read and parse notebook into NotebookNode called self.nb"
297 with open(self.infile) as f:
316 with open(self.infile) as f:
298 self.nb = nbformat.read(f, 'json')
317 self.nb = nbformat.read(f, 'json')
299
318
300 def save(self, outfile=None, encoding=None):
319 def save(self, outfile=None, encoding=None):
301 "read and parse notebook into self.nb"
320 "read and parse notebook into self.nb"
302 if outfile is None:
321 if outfile is None:
303 outfile = self.outbase + '.' + self.extension
322 outfile = self.outbase + '.' + self.extension
304 if encoding is None:
323 if encoding is None:
305 encoding = self.default_encoding
324 encoding = self.default_encoding
306 with io.open(outfile, 'w', encoding=encoding) as f:
325 with io.open(outfile, 'w', encoding=encoding) as f:
307 f.write(self.output)
326 f.write(self.output)
308 return os.path.abspath(outfile)
327 return os.path.abspath(outfile)
309
328
310 def optional_header(self):
329 def optional_header(self):
330 """
331 Optional header to insert at the top of the converted notebook
332
333 Returns a list
334 """
311 return []
335 return []
312
336
313 def optional_footer(self):
337 def optional_footer(self):
338 """
339 Optional footer to insert at the end of the converted notebook
340
341 Returns a list
342 """
314 return []
343 return []
315
344
316 def _new_figure(self, data, fmt):
345 def _new_figure(self, data, fmt):
317 """Create a new figure file in the given format.
346 """Create a new figure file in the given format.
318
347
319 Returns a path relative to the input file.
348 Returns a path relative to the input file.
320 """
349 """
321 figname = '%s_fig_%02i.%s' % (self.infile_root,
350 figname = '%s_fig_%02i.%s' % (self.infile_root,
322 self.figures_counter, fmt)
351 self.figures_counter, fmt)
323 self.figures_counter += 1
352 self.figures_counter += 1
324 fullname = os.path.join(self.files_dir, figname)
353 fullname = os.path.join(self.files_dir, figname)
325
354
326 # Binary files are base64-encoded, SVG is already XML
355 # Binary files are base64-encoded, SVG is already XML
327 if fmt in ('png', 'jpg', 'pdf'):
356 if fmt in ('png', 'jpg', 'pdf'):
328 data = data.decode('base64')
357 data = data.decode('base64')
329 fopen = lambda fname: open(fname, 'wb')
358 fopen = lambda fname: open(fname, 'wb')
330 else:
359 else:
331 fopen = lambda fname: codecs.open(fname, 'wb', self.default_encoding)
360 fopen = lambda fname: codecs.open(fname, 'wb', self.default_encoding)
332
361
333 with fopen(fullname) as f:
362 with fopen(fullname) as f:
334 f.write(data)
363 f.write(data)
335
364
336 return fullname
365 return fullname
337
366
338 def render_heading(self, cell):
367 def render_heading(self, cell):
339 """convert a heading cell
368 """convert a heading cell
340
369
341 Returns list."""
370 Returns list."""
342 raise NotImplementedError
371 raise NotImplementedError
343
372
344 def render_code(self, cell):
373 def render_code(self, cell):
345 """Convert a code cell
374 """Convert a code cell
346
375
347 Returns list."""
376 Returns list."""
348 raise NotImplementedError
377 raise NotImplementedError
349
378
350 def render_markdown(self, cell):
379 def render_markdown(self, cell):
351 """convert a markdown cell
380 """convert a markdown cell
352
381
353 Returns list."""
382 Returns list."""
354 raise NotImplementedError
383 raise NotImplementedError
355
384
356 def _img_lines(self, img_file):
385 def _img_lines(self, img_file):
357 """Return list of lines to include an image file."""
386 """Return list of lines to include an image file."""
358 # Note: subclasses may choose to implement format-specific _FMT_lines
387 # Note: subclasses may choose to implement format-specific _FMT_lines
359 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
388 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
360 raise NotImplementedError
389 raise NotImplementedError
361
390
362 def render_display_data(self, output):
391 def render_display_data(self, output):
363 """convert display data from the output of a code cell
392 """convert display data from the output of a code cell
364
393
365 Returns list.
394 Returns list.
366 """
395 """
367 lines = []
396 lines = []
368
397
369 for fmt in output.keys():
398 for fmt in output.keys():
370 if fmt in ['png', 'svg', 'jpg', 'pdf']:
399 if fmt in ['png', 'svg', 'jpg', 'pdf']:
371 img_file = self._new_figure(output[fmt], fmt)
400 img_file = self._new_figure(output[fmt], fmt)
372 # Subclasses can have format-specific render functions (e.g.,
401 # Subclasses can have format-specific render functions (e.g.,
373 # latex has to auto-convert all SVG to PDF first).
402 # latex has to auto-convert all SVG to PDF first).
374 lines_fun = getattr(self, '_%s_lines' % fmt, None)
403 lines_fun = getattr(self, '_%s_lines' % fmt, None)
375 if not lines_fun:
404 if not lines_fun:
376 lines_fun = self._img_lines
405 lines_fun = self._img_lines
377 lines.extend(lines_fun(img_file))
406 lines.extend(lines_fun(img_file))
378 elif fmt != 'output_type':
407 elif fmt != 'output_type':
379 conv_fn = self.dispatch_display_format(fmt)
408 conv_fn = self.dispatch_display_format(fmt)
380 lines.extend(conv_fn(output))
409 lines.extend(conv_fn(output))
381 return lines
410 return lines
382
411
383 def render_raw(self, cell):
412 def render_raw(self, cell):
384 """convert a cell with raw text
413 """convert a cell with raw text
385
414
386 Returns list."""
415 Returns list."""
387 raise NotImplementedError
416 raise NotImplementedError
388
417
389 def render_unknown(self, cell):
418 def render_unknown(self, cell):
390 """Render cells of unkown type
419 """Render cells of unkown type
391
420
392 Returns list."""
421 Returns list."""
393 data = pprint.pformat(cell)
422 data = pprint.pformat(cell)
394 logging.warning('Unknown cell: %s' % cell.cell_type)
423 logging.warning('Unknown cell: %s' % cell.cell_type)
395 return self._unknown_lines(data)
424 return self._unknown_lines(data)
396
425
397 def render_unknown_display(self, output, type):
426 def render_unknown_display(self, output, type):
398 """Render cells of unkown type
427 """Render cells of unkown type
399
428
400 Returns list."""
429 Returns list."""
401 data = pprint.pformat(output)
430 data = pprint.pformat(output)
402 logging.warning('Unknown output: %s' % output.output_type)
431 logging.warning('Unknown output: %s' % output.output_type)
403 return self._unknown_lines(data)
432 return self._unknown_lines(data)
404
433
405 def render_stream(self, output):
434 def render_stream(self, output):
406 """render the stream part of an output
435 """render the stream part of an output
407
436
408 Returns list.
437 Returns list.
409
438
410 Identical to render_display_format_text
439 Identical to render_display_format_text
411 """
440 """
412 return self.render_display_format_text(output)
441 return self.render_display_format_text(output)
413
442
414 def render_pyout(self, output):
443 def render_pyout(self, output):
415 """convert pyout part of a code cell
444 """convert pyout part of a code cell
416
445
417 Returns list."""
446 Returns list."""
418 raise NotImplementedError
447 raise NotImplementedError
419
448
420
449
421 def render_pyerr(self, output):
450 def render_pyerr(self, output):
422 """convert pyerr part of a code cell
451 """convert pyerr part of a code cell
423
452
424 Returns list."""
453 Returns list."""
425 raise NotImplementedError
454 raise NotImplementedError
426
455
427 def _unknown_lines(self, data):
456 def _unknown_lines(self, data):
428 """Return list of lines for an unknown cell.
457 """Return list of lines for an unknown cell.
429
458
430 Parameters
459 Parameters
431 ----------
460 ----------
432 data : str
461 data : str
433 The content of the unknown data as a single string.
462 The content of the unknown data as a single string.
434 """
463 """
435 raise NotImplementedError
464 raise NotImplementedError
436
465
437 # These are the possible format types in an output node
466 # These are the possible format types in an output node
438
467
439 def render_display_format_text(self, output):
468 def render_display_format_text(self, output):
440 """render the text part of an output
469 """render the text part of an output
441
470
442 Returns list.
471 Returns list.
443 """
472 """
444 raise NotImplementedError
473 raise NotImplementedError
445
474
446 def render_display_format_html(self, output):
475 def render_display_format_html(self, output):
447 """render the html part of an output
476 """render the html part of an output
448
477
449 Returns list.
478 Returns list.
450 """
479 """
451 raise NotImplementedError
480 raise NotImplementedError
452
481
453 def render_display_format_latex(self, output):
482 def render_display_format_latex(self, output):
454 """render the latex part of an output
483 """render the latex part of an output
455
484
456 Returns list.
485 Returns list.
457 """
486 """
458 raise NotImplementedError
487 raise NotImplementedError
459
488
460 def render_display_format_json(self, output):
489 def render_display_format_json(self, output):
461 """render the json part of an output
490 """render the json part of an output
462
491
463 Returns list.
492 Returns list.
464 """
493 """
465 raise NotImplementedError
494 raise NotImplementedError
466
495
467 def render_display_format_javascript(self, output):
496 def render_display_format_javascript(self, output):
468 """render the javascript part of an output
497 """render the javascript part of an output
469
498
470 Returns list.
499 Returns list.
471 """
500 """
472 raise NotImplementedError
501 raise NotImplementedError
473
502
474
503
475 class ConverterRST(Converter):
504 class ConverterRST(Converter):
476 extension = 'rst'
505 extension = 'rst'
477 heading_level = {1: '=', 2: '-', 3: '`', 4: '\'', 5: '.', 6: '~'}
506 heading_level = {1: '=', 2: '-', 3: '`', 4: '\'', 5: '.', 6: '~'}
478
507
479 @DocInherit
508 @DocInherit
480 def render_heading(self, cell):
509 def render_heading(self, cell):
481 marker = self.heading_level[cell.level]
510 marker = self.heading_level[cell.level]
482 return ['{0}\n{1}\n'.format(cell.source, marker * len(cell.source))]
511 return ['{0}\n{1}\n'.format(cell.source, marker * len(cell.source))]
483
512
484 @DocInherit
513 @DocInherit
485 def render_code(self, cell):
514 def render_code(self, cell):
486 if not cell.input:
515 if not cell.input:
487 return []
516 return []
488
517
489 lines = ['In[%s]:' % cell.prompt_number, '']
518 lines = ['In[%s]:' % cell.prompt_number, '']
490 lines.extend(rst_directive('.. code:: python', cell.input))
519 lines.extend(rst_directive('.. code:: python', cell.input))
491
520
492 for output in cell.outputs:
521 for output in cell.outputs:
493 conv_fn = self.dispatch(output.output_type)
522 conv_fn = self.dispatch(output.output_type)
494 lines.extend(conv_fn(output))
523 lines.extend(conv_fn(output))
495
524
496 return lines
525 return lines
497
526
498 @DocInherit
527 @DocInherit
499 def render_markdown(self, cell):
528 def render_markdown(self, cell):
500 #return [cell.source]
529 #return [cell.source]
501 return [markdown2rst(cell.source)]
530 return [markdown2rst(cell.source)]
502
531
503 @DocInherit
532 @DocInherit
504 def render_raw(self, cell):
533 def render_raw(self, cell):
505 if self.raw_as_verbatim:
534 if self.raw_as_verbatim:
506 return ['::', '', indent(cell.source), '']
535 return ['::', '', indent(cell.source), '']
507 else:
536 else:
508 return [cell.source]
537 return [cell.source]
509
538
510 @DocInherit
539 @DocInherit
511 def render_pyout(self, output):
540 def render_pyout(self, output):
512 lines = ['Out[%s]:' % output.prompt_number, '']
541 lines = ['Out[%s]:' % output.prompt_number, '']
513
542
514 # output is a dictionary like object with type as a key
543 # output is a dictionary like object with type as a key
515 if 'latex' in output:
544 if 'latex' in output:
516 lines.extend(rst_directive('.. math::', output.latex))
545 lines.extend(rst_directive('.. math::', output.latex))
517
546
518 if 'text' in output:
547 if 'text' in output:
519 lines.extend(rst_directive('.. parsed-literal::', output.text))
548 lines.extend(rst_directive('.. parsed-literal::', output.text))
520
549
521 return lines
550 return lines
522
551
523 @DocInherit
552 @DocInherit
524 def render_pyerr(self, output):
553 def render_pyerr(self, output):
525 # Note: a traceback is a *list* of frames.
554 # Note: a traceback is a *list* of frames.
526 return ['::', '', indent(remove_ansi('\n'.join(output.traceback))), '']
555 return ['::', '', indent(remove_ansi('\n'.join(output.traceback))), '']
527
556
528 @DocInherit
557 @DocInherit
529 def _img_lines(self, img_file):
558 def _img_lines(self, img_file):
530 return ['.. image:: %s' % img_file, '']
559 return ['.. image:: %s' % img_file, '']
531
560
532 @DocInherit
561 @DocInherit
533 def render_display_format_text(self, output):
562 def render_display_format_text(self, output):
534 return rst_directive('.. parsed-literal::', output.text)
563 return rst_directive('.. parsed-literal::', output.text)
535
564
536 @DocInherit
565 @DocInherit
537 def _unknown_lines(self, data):
566 def _unknown_lines(self, data):
538 return rst_directive('.. warning:: Unknown cell') + [data]
567 return rst_directive('.. warning:: Unknown cell') + [data]
539
568
540 @DocInherit
569 @DocInherit
541 def render_display_format_html(self, output):
570 def render_display_format_html(self, output):
542 return rst_directive('.. raw:: html', output.html)
571 return rst_directive('.. raw:: html', output.html)
543
572
544 @DocInherit
573 @DocInherit
545 def render_display_format_latex(self, output):
574 def render_display_format_latex(self, output):
546 return rst_directive('.. math::', output.latex)
575 return rst_directive('.. math::', output.latex)
547
576
548 @DocInherit
577 @DocInherit
549 def render_display_format_json(self, output):
578 def render_display_format_json(self, output):
550 return rst_directive('.. raw:: json', output.json)
579 return rst_directive('.. raw:: json', output.json)
551
580
552
581
553 @DocInherit
582 @DocInherit
554 def render_display_format_javascript(self, output):
583 def render_display_format_javascript(self, output):
555 return rst_directive('.. raw:: javascript', output.javascript)
584 return rst_directive('.. raw:: javascript', output.javascript)
556
585
557
586
558
587
559
588
560 def highlight(src, lang='ipython'):
589 def highlight(src, lang='ipython'):
561 """Return a syntax-highlighted version of the input source.
590 """Return a syntax-highlighted version of the input source.
562 """
591 """
563 from pygments import highlight
592 from pygments import highlight
564 from pygments.lexers import get_lexer_by_name
593 from pygments.lexers import get_lexer_by_name
565 from pygments.formatters import HtmlFormatter
594 from pygments.formatters import HtmlFormatter
566
595
567 if lang == 'ipython':
596 if lang == 'ipython':
568 lexer = IPythonLexer()
597 lexer = IPythonLexer()
569 else:
598 else:
570 lexer = get_lexer_by_name(lang, stripall=True)
599 lexer = get_lexer_by_name(lang, stripall=True)
571
600
572 return highlight(src, lexer, HtmlFormatter())
601 return highlight(src, lexer, HtmlFormatter())
573
602
574
603
575 class ConverterMarkdown(Converter):
604 class ConverterMarkdown(Converter):
576 extension = 'md'
605 extension = 'md'
577
606
578 def __init__(self, infile, highlight_source=True, show_prompts=False,
607 def __init__(self, infile, highlight_source=True, show_prompts=False,
579 inline_prompt=False):
608 inline_prompt=False):
580 super(ConverterMarkdown, self).__init__(infile)
609 super(ConverterMarkdown, self).__init__(infile)
581 self.highlight_source = highlight_source
610 self.highlight_source = highlight_source
582 self.show_prompts = show_prompts
611 self.show_prompts = show_prompts
583 self.inline_prompt = inline_prompt
612 self.inline_prompt = inline_prompt
584
613
585 @DocInherit
614 @DocInherit
586 def render_heading(self, cell):
615 def render_heading(self, cell):
587 return ['{0} {1}'.format('#'*cell.level, cell.source), '']
616 return ['{0} {1}'.format('#'*cell.level, cell.source), '']
588
617
589 @DocInherit
618 @DocInherit
590 def render_code(self, cell):
619 def render_code(self, cell):
591 if not cell.input:
620 if not cell.input:
592 return []
621 return []
593 lines = []
622 lines = []
594 if self.show_prompts and not self.inline_prompt:
623 if self.show_prompts and not self.inline_prompt:
595 lines.extend(['*In[%s]:*' % cell.prompt_number, ''])
624 lines.extend(['*In[%s]:*' % cell.prompt_number, ''])
596 if self.show_prompts and self.inline_prompt:
625 if self.show_prompts and self.inline_prompt:
597 prompt = 'In[%s]: ' % cell.prompt_number
626 prompt = 'In[%s]: ' % cell.prompt_number
598 input_lines = cell.input.split('\n')
627 input_lines = cell.input.split('\n')
599 src = prompt + input_lines[0] + '\n' + indent('\n'.join(input_lines[1:]), nspaces=len(prompt))
628 src = prompt + input_lines[0] + '\n' + indent('\n'.join(input_lines[1:]), nspaces=len(prompt))
600 else:
629 else:
601 src = cell.input
630 src = cell.input
602 src = highlight(src) if self.highlight_source else indent(src)
631 src = highlight(src) if self.highlight_source else indent(src)
603 lines.extend([src, ''])
632 lines.extend([src, ''])
604 if cell.outputs and self.show_prompts and not self.inline_prompt:
633 if cell.outputs and self.show_prompts and not self.inline_prompt:
605 lines.extend(['*Out[%s]:*' % cell.prompt_number, ''])
634 lines.extend(['*Out[%s]:*' % cell.prompt_number, ''])
606 for output in cell.outputs:
635 for output in cell.outputs:
607 conv_fn = self.dispatch(output.output_type)
636 conv_fn = self.dispatch(output.output_type)
608 lines.extend(conv_fn(output))
637 lines.extend(conv_fn(output))
609
638
610 #lines.append('----')
639 #lines.append('----')
611 lines.append('')
640 lines.append('')
612 return lines
641 return lines
613
642
614 @DocInherit
643 @DocInherit
615 def render_markdown(self, cell):
644 def render_markdown(self, cell):
616 return [cell.source, '']
645 return [cell.source, '']
617
646
618 @DocInherit
647 @DocInherit
619 def render_raw(self, cell):
648 def render_raw(self, cell):
620 if self.raw_as_verbatim:
649 if self.raw_as_verbatim:
621 return [indent(cell.source), '']
650 return [indent(cell.source), '']
622 else:
651 else:
623 return [cell.source, '']
652 return [cell.source, '']
624
653
625 @DocInherit
654 @DocInherit
626 def render_pyout(self, output):
655 def render_pyout(self, output):
627 lines = []
656 lines = []
628
657
629 ## if 'text' in output:
658 ## if 'text' in output:
630 ## lines.extend(['*Out[%s]:*' % output.prompt_number, ''])
659 ## lines.extend(['*Out[%s]:*' % output.prompt_number, ''])
631
660
632 # output is a dictionary like object with type as a key
661 # output is a dictionary like object with type as a key
633 if 'latex' in output:
662 if 'latex' in output:
634 pass
663 pass
635
664
636 if 'text' in output:
665 if 'text' in output:
637 lines.extend(['<pre>', indent(output.text), '</pre>'])
666 lines.extend(['<pre>', indent(output.text), '</pre>'])
638
667
639 lines.append('')
668 lines.append('')
640 return lines
669 return lines
641
670
642 @DocInherit
671 @DocInherit
643 def render_pyerr(self, output):
672 def render_pyerr(self, output):
644 # Note: a traceback is a *list* of frames.
673 # Note: a traceback is a *list* of frames.
645 return [indent(remove_ansi('\n'.join(output.traceback))), '']
674 return [indent(remove_ansi('\n'.join(output.traceback))), '']
646
675
647 @DocInherit
676 @DocInherit
648 def _img_lines(self, img_file):
677 def _img_lines(self, img_file):
649 return ['', '![](%s)' % img_file, '']
678 return ['', '![](%s)' % img_file, '']
650
679
651 @DocInherit
680 @DocInherit
652 def render_display_format_text(self, output):
681 def render_display_format_text(self, output):
653 return [indent(output.text)]
682 return [indent(output.text)]
654
683
655 @DocInherit
684 @DocInherit
656 def _unknown_lines(self, data):
685 def _unknown_lines(self, data):
657 return ['Warning: Unknown cell', data]
686 return ['Warning: Unknown cell', data]
658
687
659 @DocInherit
688 @DocInherit
660 def render_display_format_html(self, output):
689 def render_display_format_html(self, output):
661 return [output.html]
690 return [output.html]
662
691
663 @DocInherit
692 @DocInherit
664 def render_display_format_latex(self, output):
693 def render_display_format_latex(self, output):
665 return ['LaTeX::', indent(output.latex)]
694 return ['LaTeX::', indent(output.latex)]
666
695
667 @DocInherit
696 @DocInherit
668 def render_display_format_json(self, output):
697 def render_display_format_json(self, output):
669 return ['JSON:', indent(output.json)]
698 return ['JSON:', indent(output.json)]
670
699
671 @DocInherit
700 @DocInherit
672 def render_display_format_javascript(self, output):
701 def render_display_format_javascript(self, output):
673 return ['JavaScript:', indent(output.javascript)]
702 return ['JavaScript:', indent(output.javascript)]
674
703
675
704
676 def return_list(x):
705 def return_list(x):
677 """Ensure that x is returned as a list or inside one"""
706 """Ensure that x is returned as a list or inside one"""
678 return x if isinstance(x, list) else [x]
707 return x if isinstance(x, list) else [x]
679
708
680
709
681 # decorators for HTML output
710 # decorators for HTML output
682 def output_container(f):
711 def output_container(f):
683 """add a prompt-area next to an output"""
712 """add a prompt-area next to an output"""
684 def wrapped(self, output):
713 def wrapped(self, output):
685 rendered = f(self, output)
714 rendered = f(self, output)
686 if not rendered:
715 if not rendered:
687 # empty output
716 # empty output
688 return []
717 return []
689 lines = []
718 lines = []
690 lines.append('<div class="hbox output_area">')
719 lines.append('<div class="hbox output_area">')
691 lines.extend(self._out_prompt(output))
720 lines.extend(self._out_prompt(output))
692 classes = "output_subarea output_%s" % output.output_type
721 classes = "output_subarea output_%s" % output.output_type
693 if output.output_type == 'stream':
722 if output.output_type == 'stream':
694 classes += " output_%s" % output.stream
723 classes += " output_%s" % output.stream
695 lines.append('<div class="%s">' % classes)
724 lines.append('<div class="%s">' % classes)
696 lines.extend(rendered)
725 lines.extend(rendered)
697 lines.append('</div>') # subarea
726 lines.append('</div>') # subarea
698 lines.append('</div>') # output_area
727 lines.append('</div>') # output_area
699
728
700 return lines
729 return lines
701
730
702 return wrapped
731 return wrapped
703
732
704 def text_cell(f):
733 def text_cell(f):
705 """wrap text cells in appropriate divs"""
734 """wrap text cells in appropriate divs"""
706 def wrapped(self, cell):
735 def wrapped(self, cell):
707 rendered = f(self, cell)
736 rendered = f(self, cell)
708 classes = "text_cell_render border-box-sizing rendered_html"
737 classes = "text_cell_render border-box-sizing rendered_html"
709 lines = ['<div class="%s">' % classes] + rendered + ['</div>']
738 lines = ['<div class="%s">' % classes] + rendered + ['</div>']
710 return lines
739 return lines
711 return wrapped
740 return wrapped
712
741
713 class ConverterHTML(Converter):
742 class ConverterHTML(Converter):
714 extension = 'html'
743 extension = 'html'
715
744
716 def in_tag(self, tag, src, attrs=None):
745 def in_tag(self, tag, src, attrs=None):
717 """Return a list of elements bracketed by the given tag"""
746 """Return a list of elements bracketed by the given tag"""
718 attr_s = '' if attrs is None else \
747 attr_s = '' if attrs is None else \
719 ' '.join( "%s=%s" % (attr, value)
748 ' '.join( "%s=%s" % (attr, value)
720 for attr, value in attrs.iteritems() )
749 for attr, value in attrs.iteritems() )
721 return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag]
750 return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag]
722
751
723 def _ansi_colored(self, text):
752 def _ansi_colored(self, text):
724 return ['<pre>%s</pre>' % ansi2html(text)]
753 return ['<pre>%s</pre>' % ansi2html(text)]
725
754
726 def _stylesheet(self, fname):
755 def _stylesheet(self, fname):
727 with io.open(fname, encoding='utf-8') as f:
756 with io.open(fname, encoding='utf-8') as f:
728 s = f.read()
757 s = f.read()
729 return self.in_tag('style', s, dict(type='"text/css"'))
758 return self.in_tag('style', s, dict(type='"text/css"'))
730
759
731 def _out_prompt(self, output):
760 def _out_prompt(self, output):
732 if output.output_type == 'pyout':
761 if output.output_type == 'pyout':
733 n = output.prompt_number if output.prompt_number is not None else '&nbsp;'
762 n = output.prompt_number if output.prompt_number is not None else '&nbsp;'
734 content = 'Out [%s]:' % n
763 content = 'Out [%s]:' % n
735 else:
764 else:
736 content = ''
765 content = ''
737 return ['<div class="prompt output_prompt">%s</div>' % content]
766 return ['<div class="prompt output_prompt">%s</div>' % content]
738
767
739 def header_body(self):
768 def header_body(self):
740 """Return the body of the header as a list of strings."""
769 """Return the body of the header as a list of strings."""
741
770
742 from pygments.formatters import HtmlFormatter
771 from pygments.formatters import HtmlFormatter
743
772
744 header = []
773 header = []
745 static = os.path.join(path.get_ipython_package_dir(),
774 static = os.path.join(path.get_ipython_package_dir(),
746 'frontend', 'html', 'notebook', 'static',
775 'frontend', 'html', 'notebook', 'static',
747 )
776 )
748 here = os.path.split(os.path.abspath(__file__))[0]
777 here = os.path.split(os.path.abspath(__file__))[0]
749 css = os.path.join(static, 'css')
778 css = os.path.join(static, 'css')
750 for sheet in [
779 for sheet in [
751 # do we need jquery and prettify?
780 # do we need jquery and prettify?
752 # os.path.join(static, 'jquery', 'css', 'themes', 'base', 'jquery-ui.min.css'),
781 # os.path.join(static, 'jquery', 'css', 'themes', 'base', 'jquery-ui.min.css'),
753 # os.path.join(static, 'prettify', 'prettify.css'),
782 # os.path.join(static, 'prettify', 'prettify.css'),
754 os.path.join(css, 'boilerplate.css'),
783 os.path.join(css, 'boilerplate.css'),
755 os.path.join(css, 'fbm.css'),
784 os.path.join(css, 'fbm.css'),
756 os.path.join(css, 'notebook.css'),
785 os.path.join(css, 'notebook.css'),
757 os.path.join(css, 'renderedhtml.css'),
786 os.path.join(css, 'renderedhtml.css'),
758 # our overrides:
787 # our overrides:
759 os.path.join(here, 'css', 'static_html.css'),
788 os.path.join(here, 'css', 'static_html.css'),
760 ]:
789 ]:
761 header.extend(self._stylesheet(sheet))
790 header.extend(self._stylesheet(sheet))
762
791
763 # pygments css
792 # pygments css
764 pygments_css = HtmlFormatter().get_style_defs('.highlight')
793 pygments_css = HtmlFormatter().get_style_defs('.highlight')
765 header.extend(['<meta charset="UTF-8">'])
794 header.extend(['<meta charset="UTF-8">'])
766 header.extend(self.in_tag('style', pygments_css, dict(type='"text/css"')))
795 header.extend(self.in_tag('style', pygments_css, dict(type='"text/css"')))
767
796
768 # TODO: this should be allowed to use local mathjax:
797 # TODO: this should be allowed to use local mathjax:
769 header.extend(self.in_tag('script', '', {'type':'"text/javascript"',
798 header.extend(self.in_tag('script', '', {'type':'"text/javascript"',
770 'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"',
799 'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"',
771 }))
800 }))
772 with io.open(os.path.join(here, 'js', 'initmathjax.js'),
801 with io.open(os.path.join(here, 'js', 'initmathjax.js'),
773 encoding='utf-8') as f:
802 encoding='utf-8') as f:
774 header.extend(self.in_tag('script', f.read(),
803 header.extend(self.in_tag('script', f.read(),
775 {'type': '"text/javascript"'}))
804 {'type': '"text/javascript"'}))
776 return header
805 return header
777
806
778 def optional_header(self):
807 def optional_header(self):
779 return ['<html>', '<head>'] + self.header_body() + \
808 return ['<html>', '<head>'] + self.header_body() + \
780 ['</head>', '<body>']
809 ['</head>', '<body>']
781
810
782 def optional_footer(self):
811 def optional_footer(self):
783 return ['</body>', '</html>']
812 return ['</body>', '</html>']
784
813
785 @DocInherit
814 @DocInherit
786 @text_cell
815 @text_cell
787 def render_heading(self, cell):
816 def render_heading(self, cell):
788 marker = cell.level
817 marker = cell.level
789 return [u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)]
818 return [u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)]
790
819
791 @DocInherit
820 @DocInherit
792 def render_code(self, cell):
821 def render_code(self, cell):
793 if not cell.input:
822 if not cell.input:
794 return []
823 return []
795
824
796 lines = ['<div class="cell border-box-sizing code_cell vbox">']
825 lines = ['<div class="cell border-box-sizing code_cell vbox">']
797
826
798 lines.append('<div class="input hbox">')
827 lines.append('<div class="input hbox">')
799 n = cell.prompt_number if getattr(cell, 'prompt_number', None) is not None else '&nbsp;'
828 n = cell.prompt_number if getattr(cell, 'prompt_number', None) is not None else '&nbsp;'
800 lines.append('<div class="prompt input_prompt">In [%s]:</div>' % n)
829 lines.append('<div class="prompt input_prompt">In [%s]:</div>' % n)
801 lines.append('<div class="input_area box-flex1">')
830 lines.append('<div class="input_area box-flex1">')
802 lines.append(highlight(cell.input))
831 lines.append(highlight(cell.input))
803 lines.append('</div>') # input_area
832 lines.append('</div>') # input_area
804 lines.append('</div>') # input
833 lines.append('</div>') # input
805
834
806 if cell.outputs:
835 if cell.outputs:
807 lines.append('<div class="vbox output_wrapper">')
836 lines.append('<div class="vbox output_wrapper">')
808 lines.append('<div class="output vbox">')
837 lines.append('<div class="output vbox">')
809
838
810 for output in coalesce_streams(cell.outputs):
839 for output in coalesce_streams(cell.outputs):
811 conv_fn = self.dispatch(output.output_type)
840 conv_fn = self.dispatch(output.output_type)
812 lines.extend(conv_fn(output))
841 lines.extend(conv_fn(output))
813
842
814 lines.append('</div>') # output
843 lines.append('</div>') # output
815 lines.append('</div>') # output_wrapper
844 lines.append('</div>') # output_wrapper
816
845
817 lines.append('</div>') # cell
846 lines.append('</div>') # cell
818
847
819 return lines
848 return lines
820
849
821 @DocInherit
850 @DocInherit
822 @text_cell
851 @text_cell
823 def render_markdown(self, cell):
852 def render_markdown(self, cell):
824 return [markdown(cell.source)]
853 return [markdown(cell.source)]
825
854
826 @DocInherit
855 @DocInherit
827 def render_raw(self, cell):
856 def render_raw(self, cell):
828 if self.raw_as_verbatim:
857 if self.raw_as_verbatim:
829 return self.in_tag('pre', cell.source)
858 return self.in_tag('pre', cell.source)
830 else:
859 else:
831 return [cell.source]
860 return [cell.source]
832
861
833 @DocInherit
862 @DocInherit
834 @output_container
863 @output_container
835 def render_pyout(self, output):
864 def render_pyout(self, output):
836 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
865 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
837 if fmt in output:
866 if fmt in output:
838 conv_fn = self.dispatch_display_format(fmt)
867 conv_fn = self.dispatch_display_format(fmt)
839 return conv_fn(output)
868 return conv_fn(output)
840 return []
869 return []
841
870
842 render_display_data = render_pyout
871 render_display_data = render_pyout
843
872
844 @DocInherit
873 @DocInherit
845 @output_container
874 @output_container
846 def render_stream(self, output):
875 def render_stream(self, output):
847 return self._ansi_colored(output.text)
876 return self._ansi_colored(output.text)
848
877
849
878
850 @DocInherit
879 @DocInherit
851 @output_container
880 @output_container
852 def render_pyerr(self, output):
881 def render_pyerr(self, output):
853 # Note: a traceback is a *list* of frames.
882 # Note: a traceback is a *list* of frames.
854 # lines = []
883 # lines = []
855
884
856 # stb =
885 # stb =
857 return self._ansi_colored('\n'.join(output.traceback))
886 return self._ansi_colored('\n'.join(output.traceback))
858
887
859 @DocInherit
888 @DocInherit
860 def _img_lines(self, img_file):
889 def _img_lines(self, img_file):
861 return ['<img src="%s">' % img_file, '</img>']
890 return ['<img src="%s">' % img_file, '</img>']
862
891
863 @DocInherit
892 @DocInherit
864 def _unknown_lines(self, data):
893 def _unknown_lines(self, data):
865 return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
894 return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
866
895
867
896
868 @DocInherit
897 @DocInherit
869 def render_display_format_png(self, output):
898 def render_display_format_png(self, output):
870 return ['<img src="data:image/png;base64,%s"></img>' % output.png]
899 return ['<img src="data:image/png;base64,%s"></img>' % output.png]
871
900
872 @DocInherit
901 @DocInherit
873 def render_display_format_svg(self, output):
902 def render_display_format_svg(self, output):
874 return [output.svg]
903 return [output.svg]
875
904
876 @DocInherit
905 @DocInherit
877 def render_display_format_jpeg(self, output):
906 def render_display_format_jpeg(self, output):
878 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
907 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
879
908
880 @DocInherit
909 @DocInherit
881 def render_display_format_text(self, output):
910 def render_display_format_text(self, output):
882 return self._ansi_colored(output.text)
911 return self._ansi_colored(output.text)
883
912
884 @DocInherit
913 @DocInherit
885 def render_display_format_html(self, output):
914 def render_display_format_html(self, output):
886 return [output.html]
915 return [output.html]
887
916
888 @DocInherit
917 @DocInherit
889 def render_display_format_latex(self, output):
918 def render_display_format_latex(self, output):
890 return [output.latex]
919 return [output.latex]
891
920
892 @DocInherit
921 @DocInherit
893 def render_display_format_json(self, output):
922 def render_display_format_json(self, output):
894 # html ignores json
923 # html ignores json
895 return []
924 return []
896
925
897
926
898 @DocInherit
927 @DocInherit
899 def render_display_format_javascript(self, output):
928 def render_display_format_javascript(self, output):
900 return [output.javascript]
929 return [output.javascript]
901
930
902
931
903 class ConverterBloggerHTML(ConverterHTML):
932 class ConverterBloggerHTML(ConverterHTML):
904 """Convert a notebook to html suitable for easy pasting into Blogger.
933 """Convert a notebook to html suitable for easy pasting into Blogger.
905
934
906 It generates an html file that has *only* the pure HTML contents, and a
935 It generates an html file that has *only* the pure HTML contents, and a
907 separate file with `_header` appended to the name with all header contents.
936 separate file with `_header` appended to the name with all header contents.
908 Typically, the header file only needs to be used once when setting up a
937 Typically, the header file only needs to be used once when setting up a
909 blog, as the CSS for all posts is stored in a single location in Blogger.
938 blog, as the CSS for all posts is stored in a single location in Blogger.
910 """
939 """
911
940
912 def optional_header(self):
941 def optional_header(self):
913 with io.open(self.outbase + '_header.html', 'w',
942 with io.open(self.outbase + '_header.html', 'w',
914 encoding=self.default_encoding) as f:
943 encoding=self.default_encoding) as f:
915 f.write('\n'.join(self.header_body()))
944 f.write('\n'.join(self.header_body()))
916 return []
945 return []
917
946
918 def optional_footer(self):
947 def optional_footer(self):
919 return []
948 return []
920
949
921
950
922 class ConverterLaTeX(Converter):
951 class ConverterLaTeX(Converter):
923 """Converts a notebook to a .tex file suitable for pdflatex.
952 """Converts a notebook to a .tex file suitable for pdflatex.
924
953
925 Note: this converter *needs*:
954 Note: this converter *needs*:
926
955
927 - `pandoc`: for all conversion of markdown cells. If your notebook only
956 - `pandoc`: for all conversion of markdown cells. If your notebook only
928 has Raw cells, pandoc will not be needed.
957 has Raw cells, pandoc will not be needed.
929
958
930 - `inkscape`: if your notebook has SVG figures. These need to be
959 - `inkscape`: if your notebook has SVG figures. These need to be
931 converted to PDF before inclusion in the TeX file, as LaTeX doesn't
960 converted to PDF before inclusion in the TeX file, as LaTeX doesn't
932 understand SVG natively.
961 understand SVG natively.
933
962
934 You will in general obtain much better final PDF results if you configure
963 You will in general obtain much better final PDF results if you configure
935 the matplotlib backend to create SVG output with
964 the matplotlib backend to create SVG output with
936
965
937 %config InlineBackend.figure_format = 'svg'
966 %config InlineBackend.figure_format = 'svg'
938
967
939 (or set the equivalent flag at startup or in your configuration profile).
968 (or set the equivalent flag at startup or in your configuration profile).
940 """
969 """
941 extension = 'tex'
970 extension = 'tex'
942 documentclass = 'article'
971 documentclass = 'article'
943 documentclass_options = '11pt,english'
972 documentclass_options = '11pt,english'
944 heading_map = {1: r'\section',
973 heading_map = {1: r'\section',
945 2: r'\subsection',
974 2: r'\subsection',
946 3: r'\subsubsection',
975 3: r'\subsubsection',
947 4: r'\paragraph',
976 4: r'\paragraph',
948 5: r'\subparagraph',
977 5: r'\subparagraph',
949 6: r'\subparagraph'}
978 6: r'\subparagraph'}
950
979
951 def in_env(self, environment, lines):
980 def in_env(self, environment, lines):
952 """Return list of environment lines for input lines
981 """Return list of environment lines for input lines
953
982
954 Parameters
983 Parameters
955 ----------
984 ----------
956 env : string
985 env : string
957 Name of the environment to bracket with begin/end.
986 Name of the environment to bracket with begin/end.
958
987
959 lines: """
988 lines: """
960 out = [ur'\begin{%s}' % environment]
989 out = [ur'\begin{%s}' % environment]
961 if isinstance(lines, basestring):
990 if isinstance(lines, basestring):
962 out.append(lines)
991 out.append(lines)
963 else: # list
992 else: # list
964 out.extend(lines)
993 out.extend(lines)
965 out.append(ur'\end{%s}' % environment)
994 out.append(ur'\end{%s}' % environment)
966 return out
995 return out
967
996
968 def convert(self):
997 def convert(self):
969 # The main body is done by the logic in the parent class, and that's
998 # The main body is done by the logic in the parent class, and that's
970 # all we need if preamble support has been turned off.
999 # all we need if preamble support has been turned off.
971 body = super(ConverterLaTeX, self).convert()
1000 body = super(ConverterLaTeX, self).convert()
972 if not self.with_preamble:
1001 if not self.with_preamble:
973 return body
1002 return body
974 # But if preamble is on, then we need to construct a proper, standalone
1003 # But if preamble is on, then we need to construct a proper, standalone
975 # tex file.
1004 # tex file.
976
1005
977 # Tag the document at the top and set latex class
1006 # Tag the document at the top and set latex class
978 final = [ r'%% This file was auto-generated by IPython, do NOT edit',
1007 final = [ r'%% This file was auto-generated by IPython, do NOT edit',
979 r'%% Conversion from the original notebook file:',
1008 r'%% Conversion from the original notebook file:',
980 r'%% {0}'.format(self.infile),
1009 r'%% {0}'.format(self.infile),
981 r'%%',
1010 r'%%',
982 r'\documentclass[%s]{%s}' % (self.documentclass_options,
1011 r'\documentclass[%s]{%s}' % (self.documentclass_options,
983 self.documentclass),
1012 self.documentclass),
984 '',
1013 '',
985 ]
1014 ]
986 # Load our own preamble, which is stored next to the main file. We
1015 # Load our own preamble, which is stored next to the main file. We
987 # need to be careful in case the script entry point is a symlink
1016 # need to be careful in case the script entry point is a symlink
988 myfile = __file__ if not os.path.islink(__file__) else \
1017 myfile = __file__ if not os.path.islink(__file__) else \
989 os.readlink(__file__)
1018 os.readlink(__file__)
990 with open(os.path.join(os.path.dirname(myfile), 'preamble.tex')) as f:
1019 with open(os.path.join(os.path.dirname(myfile), 'preamble.tex')) as f:
991 final.append(f.read())
1020 final.append(f.read())
992
1021
993 # Load any additional user-supplied preamble
1022 # Load any additional user-supplied preamble
994 if self.user_preamble:
1023 if self.user_preamble:
995 final.extend(['', '%% Adding user preamble from file:',
1024 final.extend(['', '%% Adding user preamble from file:',
996 '%% {0}'.format(self.user_preamble), ''])
1025 '%% {0}'.format(self.user_preamble), ''])
997 with open(self.user_preamble) as f:
1026 with open(self.user_preamble) as f:
998 final.append(f.read())
1027 final.append(f.read())
999
1028
1000 # Include document body
1029 # Include document body
1001 final.extend([ r'\begin{document}', '',
1030 final.extend([ r'\begin{document}', '',
1002 body,
1031 body,
1003 r'\end{document}', ''])
1032 r'\end{document}', ''])
1004 # Retun value must be a string
1033 # Retun value must be a string
1005 return '\n'.join(final)
1034 return '\n'.join(final)
1006
1035
1007 @DocInherit
1036 @DocInherit
1008 def render_heading(self, cell):
1037 def render_heading(self, cell):
1009 marker = self.heading_map[cell.level]
1038 marker = self.heading_map[cell.level]
1010 return ['%s{%s}' % (marker, cell.source) ]
1039 return ['%s{%s}' % (marker, cell.source) ]
1011
1040
1012 @DocInherit
1041 @DocInherit
1013 def render_code(self, cell):
1042 def render_code(self, cell):
1014 if not cell.input:
1043 if not cell.input:
1015 return []
1044 return []
1016
1045
1017 # Cell codes first carry input code, we use lstlisting for that
1046 # Cell codes first carry input code, we use lstlisting for that
1018 lines = [ur'\begin{codecell}']
1047 lines = [ur'\begin{codecell}']
1019
1048
1020 lines.extend(self.in_env('codeinput',
1049 lines.extend(self.in_env('codeinput',
1021 self.in_env('lstlisting', cell.input)))
1050 self.in_env('lstlisting', cell.input)))
1022
1051
1023 outlines = []
1052 outlines = []
1024 for output in cell.outputs:
1053 for output in cell.outputs:
1025 conv_fn = self.dispatch(output.output_type)
1054 conv_fn = self.dispatch(output.output_type)
1026 outlines.extend(conv_fn(output))
1055 outlines.extend(conv_fn(output))
1027
1056
1028 # And then output of many possible types; use a frame for all of it.
1057 # And then output of many possible types; use a frame for all of it.
1029 if outlines:
1058 if outlines:
1030 lines.extend(self.in_env('codeoutput', outlines))
1059 lines.extend(self.in_env('codeoutput', outlines))
1031
1060
1032 lines.append(ur'\end{codecell}')
1061 lines.append(ur'\end{codecell}')
1033
1062
1034 return lines
1063 return lines
1035
1064
1036
1065
1037 @DocInherit
1066 @DocInherit
1038 def _img_lines(self, img_file):
1067 def _img_lines(self, img_file):
1039 return self.in_env('center',
1068 return self.in_env('center',
1040 [r'\includegraphics[width=6in]{%s}' % img_file, r'\par'])
1069 [r'\includegraphics[width=6in]{%s}' % img_file, r'\par'])
1041
1070
1042 def _svg_lines(self, img_file):
1071 def _svg_lines(self, img_file):
1043 base_file = os.path.splitext(img_file)[0]
1072 base_file = os.path.splitext(img_file)[0]
1044 pdf_file = base_file + '.pdf'
1073 pdf_file = base_file + '.pdf'
1045 subprocess.check_call([ inkscape, '--export-pdf=%s' % pdf_file,
1074 subprocess.check_call([ inkscape, '--export-pdf=%s' % pdf_file,
1046 img_file])
1075 img_file])
1047 return self._img_lines(pdf_file)
1076 return self._img_lines(pdf_file)
1048
1077
1049 @DocInherit
1078 @DocInherit
1050 def render_markdown(self, cell):
1079 def render_markdown(self, cell):
1051 return [markdown2latex(cell.source)]
1080 return [markdown2latex(cell.source)]
1052
1081
1053 @DocInherit
1082 @DocInherit
1054 def render_pyout(self, output):
1083 def render_pyout(self, output):
1055 lines = []
1084 lines = []
1056
1085
1057 # output is a dictionary like object with type as a key
1086 # output is a dictionary like object with type as a key
1058 if 'latex' in output:
1087 if 'latex' in output:
1059 lines.extend(output.latex)
1088 lines.extend(output.latex)
1060
1089
1061 if 'text' in output:
1090 if 'text' in output:
1062 lines.extend(self.in_env('verbatim', output.text))
1091 lines.extend(self.in_env('verbatim', output.text))
1063
1092
1064 return lines
1093 return lines
1065
1094
1066 @DocInherit
1095 @DocInherit
1067 def render_pyerr(self, output):
1096 def render_pyerr(self, output):
1068 # Note: a traceback is a *list* of frames.
1097 # Note: a traceback is a *list* of frames.
1069 return self.in_env('traceback',
1098 return self.in_env('traceback',
1070 self.in_env('verbatim',
1099 self.in_env('verbatim',
1071 remove_ansi('\n'.join(output.traceback))))
1100 remove_ansi('\n'.join(output.traceback))))
1072
1101
1073 @DocInherit
1102 @DocInherit
1074 def render_raw(self, cell):
1103 def render_raw(self, cell):
1075 if self.raw_as_verbatim:
1104 if self.raw_as_verbatim:
1076 return self.in_env('verbatim', cell.source)
1105 return self.in_env('verbatim', cell.source)
1077 else:
1106 else:
1078 return [cell.source]
1107 return [cell.source]
1079
1108
1080 @DocInherit
1109 @DocInherit
1081 def _unknown_lines(self, data):
1110 def _unknown_lines(self, data):
1082 return [r'{\vspace{5mm}\bf WARNING:: unknown cell:}'] + \
1111 return [r'{\vspace{5mm}\bf WARNING:: unknown cell:}'] + \
1083 self.in_env('verbatim', data)
1112 self.in_env('verbatim', data)
1084
1113
1085
1114
1086 @DocInherit
1115 @DocInherit
1087 def render_display_format_text(self, output):
1116 def render_display_format_text(self, output):
1088 lines = []
1117 lines = []
1089
1118
1090 if 'text' in output:
1119 if 'text' in output:
1091 lines.extend(self.in_env('verbatim', output.text.strip()))
1120 lines.extend(self.in_env('verbatim', output.text.strip()))
1092
1121
1093 return lines
1122 return lines
1094
1123
1095 @DocInherit
1124 @DocInherit
1096 def render_display_format_html(self, output):
1125 def render_display_format_html(self, output):
1097 return []
1126 return []
1098
1127
1099 @DocInherit
1128 @DocInherit
1100 def render_display_format_latex(self, output):
1129 def render_display_format_latex(self, output):
1101 if type(output.latex) == type([]):
1130 if type(output.latex) == type([]):
1102 return output.latex
1131 return output.latex
1103 return [output.latex]
1132 return [output.latex]
1104
1133
1105 @DocInherit
1134 @DocInherit
1106 def render_display_format_json(self, output):
1135 def render_display_format_json(self, output):
1107 # latex ignores json
1136 # latex ignores json
1108 return []
1137 return []
1109
1138
1110
1139
1111 @DocInherit
1140 @DocInherit
1112 def render_display_format_javascript(self, output):
1141 def render_display_format_javascript(self, output):
1113 # latex ignores javascript
1142 # latex ignores javascript
1114 return []
1143 return []
1115
1144
1116 class ConverterNotebook(Converter):
1145 class ConverterNotebook(Converter):
1117 """
1146 """
1118 A converter that is essentially a null-op.
1147 A converter that is essentially a null-op.
1119 This exists so it can be subclassed
1148 This exists so it can be subclassed
1120 for custom handlers of .ipynb files
1149 for custom handlers of .ipynb files
1121 that create new .ipynb files.
1150 that create new .ipynb files.
1122
1151
1123 What distinguishes this from JSONWriter is that
1152 What distinguishes this from JSONWriter is that
1124 subclasses can specify what to do with each type of cell.
1153 subclasses can specify what to do with each type of cell.
1125
1154
1126 Writes out a notebook file.
1155 Writes out a notebook file.
1127
1156
1128 """
1157 """
1129 extension = 'ipynb'
1158 extension = 'ipynb'
1130
1159
1131 def __init__(self, infile, outbase):
1160 def __init__(self, infile, outbase):
1132 Converter.__init__(self, infile)
1161 Converter.__init__(self, infile)
1133 self.outbase = outbase
1162 self.outbase = outbase
1134 rmtree(self.files_dir)
1163 rmtree(self.files_dir)
1135
1164
1136 def convert(self):
1165 def convert(self):
1137 return json.dumps(json.loads(Converter.convert(self, ',')), indent=1, sort_keys=True)
1166 return json.dumps(json.loads(Converter.convert(self, ',')), indent=1, sort_keys=True)
1138
1167
1139 def optional_header(self):
1168 def optional_header(self):
1140 s = \
1169 s = \
1141 """{
1170 """{
1142 "metadata": {
1171 "metadata": {
1143 "name": "%(name)s"
1172 "name": "%(name)s"
1144 },
1173 },
1145 "nbformat": 3,
1174 "nbformat": 3,
1146 "worksheets": [
1175 "worksheets": [
1147 {
1176 {
1148 "cells": [""" % {'name':self.outbase}
1177 "cells": [""" % {'name':self.outbase}
1149
1178
1150 return s.split('\n')
1179 return s.split('\n')
1151
1180
1152 def optional_footer(self):
1181 def optional_footer(self):
1153 s = \
1182 s = \
1154 """]
1183 """]
1155 }
1184 }
1156 ]
1185 ]
1157 }"""
1186 }"""
1158 return s.split('\n')
1187 return s.split('\n')
1159
1188
1160 @DocInherit
1189 @DocInherit
1161 def render_heading(self, cell):
1190 def render_heading(self, cell):
1162 return cell_to_lines(cell)
1191 return cell_to_lines(cell)
1163
1192
1164 @DocInherit
1193 @DocInherit
1165 def render_code(self, cell):
1194 def render_code(self, cell):
1166 return cell_to_lines(cell)
1195 return cell_to_lines(cell)
1167
1196
1168 @DocInherit
1197 @DocInherit
1169 def render_markdown(self, cell):
1198 def render_markdown(self, cell):
1170 return cell_to_lines(cell)
1199 return cell_to_lines(cell)
1171
1200
1172 @DocInherit
1201 @DocInherit
1173 def render_raw(self, cell):
1202 def render_raw(self, cell):
1174 return cell_to_lines(cell)
1203 return cell_to_lines(cell)
1175
1204
1176 @DocInherit
1205 @DocInherit
1177 def render_pyout(self, output):
1206 def render_pyout(self, output):
1178 return cell_to_lines(output)
1207 return cell_to_lines(output)
1179
1208
1180 @DocInherit
1209 @DocInherit
1181 def render_pyerr(self, output):
1210 def render_pyerr(self, output):
1182 return cell_to_lines(output)
1211 return cell_to_lines(output)
1183
1212
1184 @DocInherit
1213 @DocInherit
1185 def render_display_format_text(self, output):
1214 def render_display_format_text(self, output):
1186 return [output.text]
1215 return [output.text]
1187
1216
1188 @DocInherit
1217 @DocInherit
1189 def render_display_format_html(self, output):
1218 def render_display_format_html(self, output):
1190 return [output.html]
1219 return [output.html]
1191
1220
1192 @DocInherit
1221 @DocInherit
1193 def render_display_format_latex(self, output):
1222 def render_display_format_latex(self, output):
1194 return [output.latex]
1223 return [output.latex]
1195
1224
1196 @DocInherit
1225 @DocInherit
1197 def render_display_format_json(self, output):
1226 def render_display_format_json(self, output):
1198 return [output.json]
1227 return [output.json]
1199
1228
1200
1229
1201 @DocInherit
1230 @DocInherit
1202 def render_display_format_javascript(self, output):
1231 def render_display_format_javascript(self, output):
1203 return [output.javascript]
1232 return [output.javascript]
1204
1233
1205 class ConverterPy(Converter):
1234 class ConverterPy(Converter):
1206 """
1235 """
1207 A converter that takes a notebook and converts it to a .py file.
1236 A converter that takes a notebook and converts it to a .py file.
1208
1237
1209 What distinguishes this from PyWriter and PyReader in IPython.nbformat is
1238 What distinguishes this from PyWriter and PyReader in IPython.nbformat is
1210 that subclasses can specify what to do with each type of cell.
1239 that subclasses can specify what to do with each type of cell.
1211 Additionally, unlike PyWriter, this does not preserve the '# <markdown>'
1240 Additionally, unlike PyWriter, this does not preserve the '# <markdown>'
1212 opening and closing comments style comments in favor of a cleaner looking
1241 opening and closing comments style comments in favor of a cleaner looking
1213 python program.
1242 python program.
1214
1243
1215 Note:
1244 Note:
1216 Even though this produces a .py file, it is not guaranteed to be valid
1245 Even though this produces a .py file, it is not guaranteed to be valid
1217 python file, since the notebook may be using magics and even cell
1246 python file, since the notebook may be using magics and even cell
1218 magics.
1247 magics.
1219 """
1248 """
1220 extension = 'py'
1249 extension = 'py'
1221
1250
1222 def __init__(self, infile, show_prompts=True, show_output=True):
1251 def __init__(self, infile, show_prompts=True, show_output=True):
1223 super(ConverterPy, self).__init__(infile)
1252 super(ConverterPy, self).__init__(infile)
1224 self.show_prompts = show_prompts
1253 self.show_prompts = show_prompts
1225 self.show_output = show_output
1254 self.show_output = show_output
1226
1255
1227 @staticmethod
1256 @staticmethod
1228 def comment(input):
1257 def comment(input):
1229 "returns every line in input as commented out"
1258 "returns every line in input as commented out"
1230 return "# "+input.replace("\n", "\n# ")
1259 return "# "+input.replace("\n", "\n# ")
1231
1260
1232 @DocInherit
1261 @DocInherit
1233 def render_heading(self, cell):
1262 def render_heading(self, cell):
1234 return ['#{0} {1}'.format('#'*cell.level, cell.source), '']
1263 return ['#{0} {1}'.format('#'*cell.level, cell.source), '']
1235
1264
1236 @DocInherit
1265 @DocInherit
1237 def render_code(self, cell):
1266 def render_code(self, cell):
1238 if not cell.input:
1267 if not cell.input:
1239 return []
1268 return []
1240 lines = []
1269 lines = []
1241 if self.show_prompts:
1270 if self.show_prompts:
1242 lines.extend(['# In[%s]:' % cell.prompt_number])
1271 lines.extend(['# In[%s]:' % cell.prompt_number])
1243 src = cell.input
1272 src = cell.input
1244 lines.extend([src, ''])
1273 lines.extend([src, ''])
1245 if self.show_output:
1274 if self.show_output:
1246 if cell.outputs :
1275 if cell.outputs :
1247 lines.extend(['# Out[%s]:' % cell.prompt_number])
1276 lines.extend(['# Out[%s]:' % cell.prompt_number])
1248 for output in cell.outputs:
1277 for output in cell.outputs:
1249 conv_fn = self.dispatch(output.output_type)
1278 conv_fn = self.dispatch(output.output_type)
1250 lines.extend(conv_fn(output))
1279 lines.extend(conv_fn(output))
1251 return lines
1280 return lines
1252
1281
1253 @DocInherit
1282 @DocInherit
1254 def render_markdown(self, cell):
1283 def render_markdown(self, cell):
1255 return [self.comment(cell.source), '']
1284 return [self.comment(cell.source), '']
1256
1285
1257 @DocInherit
1286 @DocInherit
1258 def render_raw(self, cell):
1287 def render_raw(self, cell):
1259 if self.raw_as_verbatim:
1288 if self.raw_as_verbatim:
1260 return [self.comment(indent(cell.source)), '']
1289 return [self.comment(indent(cell.source)), '']
1261 else:
1290 else:
1262 return [self.comment(cell.source), '']
1291 return [self.comment(cell.source), '']
1263
1292
1264 @DocInherit
1293 @DocInherit
1265 def render_pyout(self, output):
1294 def render_pyout(self, output):
1266 lines = []
1295 lines = []
1267
1296
1268 ## if 'text' in output:
1297 ## if 'text' in output:
1269 ## lines.extend(['*Out[%s]:*' % output.prompt_number, ''])
1298 ## lines.extend(['*Out[%s]:*' % output.prompt_number, ''])
1270
1299
1271 # output is a dictionary like object with type as a key
1300 # output is a dictionary like object with type as a key
1272 if 'latex' in output:
1301 if 'latex' in output:
1273 pass
1302 pass
1274
1303
1275 if 'text' in output:
1304 if 'text' in output:
1276 lines.extend([self.comment(indent(output.text)), ''])
1305 lines.extend([self.comment(indent(output.text)), ''])
1277
1306
1278 lines.append('')
1307 lines.append('')
1279 return lines
1308 return lines
1280
1309
1281 @DocInherit
1310 @DocInherit
1282 def render_pyerr(self, output):
1311 def render_pyerr(self, output):
1283 # Note: a traceback is a *list* of frames.
1312 # Note: a traceback is a *list* of frames.
1284 return [indent(remove_ansi('\n'.join(output.traceback))), '']
1313 return [indent(remove_ansi('\n'.join(output.traceback))), '']
1285
1314
1286 @DocInherit
1315 @DocInherit
1287 def _img_lines(self, img_file):
1316 def _img_lines(self, img_file):
1288 return [ self.comment('image file: %s' % img_file), '']
1317 return [ self.comment('image file: %s' % img_file), '']
1289
1318
1290 @DocInherit
1319 @DocInherit
1291 def render_display_format_text(self, output):
1320 def render_display_format_text(self, output):
1292 return [self.comment(indent(output.text))]
1321 return [self.comment(indent(output.text))]
1293
1322
1294 @DocInherit
1323 @DocInherit
1295 def _unknown_lines(self, data):
1324 def _unknown_lines(self, data):
1296 return [self.comment('Warning: Unknown cell'+ str(data))]
1325 return [self.comment('Warning: Unknown cell'+ str(data))]
1297
1326
1298 @DocInherit
1327 @DocInherit
1299 def render_display_format_html(self, output):
1328 def render_display_format_html(self, output):
1300 return [self.comment(output.html)]
1329 return [self.comment(output.html)]
1301
1330
1302 @DocInherit
1331 @DocInherit
1303 def render_display_format_latex(self, output):
1332 def render_display_format_latex(self, output):
1304 return []
1333 return []
1305
1334
1306 @DocInherit
1335 @DocInherit
1307 def render_display_format_json(self, output):
1336 def render_display_format_json(self, output):
1308 return []
1337 return []
1309
1338
1310 @DocInherit
1339 @DocInherit
1311 def render_display_format_javascript(self, output):
1340 def render_display_format_javascript(self, output):
1312 return []
1341 return []
1313
1342
1314 #-----------------------------------------------------------------------------
1343 #-----------------------------------------------------------------------------
1315 # Standalone conversion functions
1344 # Standalone conversion functions
1316 #-----------------------------------------------------------------------------
1345 #-----------------------------------------------------------------------------
1317
1346
1318 def rst2simplehtml(infile):
1347 def rst2simplehtml(infile):
1319 """Convert a rst file to simplified html suitable for blogger.
1348 """Convert a rst file to simplified html suitable for blogger.
1320
1349
1321 This just runs rst2html with certain parameters to produce really simple
1350 This just runs rst2html with certain parameters to produce really simple
1322 html and strips the document header, so the resulting file can be easily
1351 html and strips the document header, so the resulting file can be easily
1323 pasted into a blogger edit window.
1352 pasted into a blogger edit window.
1324 """
1353 """
1325
1354
1326 # This is the template for the rst2html call that produces the cleanest,
1355 # This is the template for the rst2html call that produces the cleanest,
1327 # simplest html I could find. This should help in making it easier to
1356 # simplest html I could find. This should help in making it easier to
1328 # paste into the blogspot html window, though I'm still having problems
1357 # paste into the blogspot html window, though I'm still having problems
1329 # with linebreaks there...
1358 # with linebreaks there...
1330 cmd_template = ("rst2html --link-stylesheet --no-xml-declaration "
1359 cmd_template = ("rst2html --link-stylesheet --no-xml-declaration "
1331 "--no-generator --no-datestamp --no-source-link "
1360 "--no-generator --no-datestamp --no-source-link "
1332 "--no-toc-backlinks --no-section-numbering "
1361 "--no-toc-backlinks --no-section-numbering "
1333 "--strip-comments ")
1362 "--strip-comments ")
1334
1363
1335 cmd = "%s %s" % (cmd_template, infile)
1364 cmd = "%s %s" % (cmd_template, infile)
1336 proc = subprocess.Popen(cmd,
1365 proc = subprocess.Popen(cmd,
1337 stdout=subprocess.PIPE,
1366 stdout=subprocess.PIPE,
1338 stderr=subprocess.PIPE,
1367 stderr=subprocess.PIPE,
1339 shell=True)
1368 shell=True)
1340 html, stderr = proc.communicate()
1369 html, stderr = proc.communicate()
1341 if stderr:
1370 if stderr:
1342 raise IOError(stderr)
1371 raise IOError(stderr)
1343
1372
1344 # Make an iterator so breaking out holds state. Our implementation of
1373 # Make an iterator so breaking out holds state. Our implementation of
1345 # searching for the html body below is basically a trivial little state
1374 # searching for the html body below is basically a trivial little state
1346 # machine, so we need this.
1375 # machine, so we need this.
1347 walker = iter(html.splitlines())
1376 walker = iter(html.splitlines())
1348
1377
1349 # Find start of main text, break out to then print until we find end /div.
1378 # Find start of main text, break out to then print until we find end /div.
1350 # This may only work if there's a real title defined so we get a 'div class'
1379 # This may only work if there's a real title defined so we get a 'div class'
1351 # tag, I haven't really tried.
1380 # tag, I haven't really tried.
1352 for line in walker:
1381 for line in walker:
1353 if line.startswith('<body>'):
1382 if line.startswith('<body>'):
1354 break
1383 break
1355
1384
1356 newfname = os.path.splitext(infile)[0] + '.html'
1385 newfname = os.path.splitext(infile)[0] + '.html'
1357 with open(newfname, 'w') as f:
1386 with open(newfname, 'w') as f:
1358 for line in walker:
1387 for line in walker:
1359 if line.startswith('</body>'):
1388 if line.startswith('</body>'):
1360 break
1389 break
1361 f.write(line)
1390 f.write(line)
1362 f.write('\n')
1391 f.write('\n')
1363
1392
1364 return newfname
1393 return newfname
1365
1394
1366 #-----------------------------------------------------------------------------
1395 #-----------------------------------------------------------------------------
1367 # Cell-level functions -- similar to IPython.nbformat.v3.rwbase functions
1396 # Cell-level functions -- similar to IPython.nbformat.v3.rwbase functions
1368 # but at cell level instead of whole notebook level
1397 # but at cell level instead of whole notebook level
1369 #-----------------------------------------------------------------------------
1398 #-----------------------------------------------------------------------------
1370
1399
1371 def writes_cell(cell, **kwargs):
1400 def writes_cell(cell, **kwargs):
1372 kwargs['cls'] = BytesEncoder
1401 kwargs['cls'] = BytesEncoder
1373 kwargs['indent'] = 3
1402 kwargs['indent'] = 3
1374 kwargs['sort_keys'] = True
1403 kwargs['sort_keys'] = True
1375 kwargs['separators'] = (',',': ')
1404 kwargs['separators'] = (',',': ')
1376 if kwargs.pop('split_lines', True):
1405 if kwargs.pop('split_lines', True):
1377 cell = split_lines_cell(copy.deepcopy(cell))
1406 cell = split_lines_cell(copy.deepcopy(cell))
1378 return py3compat.str_to_unicode(json.dumps(cell, **kwargs), 'utf-8')
1407 return py3compat.str_to_unicode(json.dumps(cell, **kwargs), 'utf-8')
1379
1408
1380
1409
1381 _multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json']
1410 _multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json']
1382
1411
1383
1412
1384 def split_lines_cell(cell):
1413 def split_lines_cell(cell):
1385 """
1414 """
1386 Split lines within a cell as in
1415 Split lines within a cell as in
1387 IPython.nbformat.v3.rwbase.split_lines
1416 IPython.nbformat.v3.rwbase.split_lines
1388
1417
1389 """
1418 """
1390 if cell.cell_type == 'code':
1419 if cell.cell_type == 'code':
1391 if 'input' in cell and isinstance(cell.input, basestring):
1420 if 'input' in cell and isinstance(cell.input, basestring):
1392 cell.input = (cell.input + '\n').splitlines()
1421 cell.input = (cell.input + '\n').splitlines()
1393 for output in cell.outputs:
1422 for output in cell.outputs:
1394 for key in _multiline_outputs:
1423 for key in _multiline_outputs:
1395 item = output.get(key, None)
1424 item = output.get(key, None)
1396 if isinstance(item, basestring):
1425 if isinstance(item, basestring):
1397 output[key] = (item + '\n').splitlines()
1426 output[key] = (item + '\n').splitlines()
1398 else: # text, heading cell
1427 else: # text, heading cell
1399 for key in ['source', 'rendered']:
1428 for key in ['source', 'rendered']:
1400 item = cell.get(key, None)
1429 item = cell.get(key, None)
1401 if isinstance(item, basestring):
1430 if isinstance(item, basestring):
1402 cell[key] = (item + '\n').splitlines()
1431 cell[key] = (item + '\n').splitlines()
1403 return cell
1432 return cell
1404
1433
1405
1434
1406 def cell_to_lines(cell):
1435 def cell_to_lines(cell):
1407 '''
1436 '''
1408 Write a cell to json, returning the split lines.
1437 Write a cell to json, returning the split lines.
1409 '''
1438 '''
1410 split_lines_cell(cell)
1439 split_lines_cell(cell)
1411 s = writes_cell(cell).strip()
1440 s = writes_cell(cell).strip()
1412 return s.split('\n')
1441 return s.split('\n')
1413
1442
1414
1443
1415 known_formats = "rst (default), html, blogger-html, latex, markdown, py"
1444 known_formats = "rst (default), html, blogger-html, latex, markdown, py"
1416
1445
1417 def main(infile, format='rst'):
1446 def main(infile, format='rst'):
1418 """Convert a notebook to html in one step"""
1447 """Convert a notebook to html in one step"""
1419 # XXX: this is just quick and dirty for now. When adding a new format,
1448 # XXX: this is just quick and dirty for now. When adding a new format,
1420 # make sure to add it to the `known_formats` string above, which gets
1449 # make sure to add it to the `known_formats` string above, which gets
1421 # printed in in the catch-all else, as well as in the help
1450 # printed in in the catch-all else, as well as in the help
1422 if format == 'rst':
1451 if format == 'rst':
1423 converter = ConverterRST(infile)
1452 converter = ConverterRST(infile)
1424 converter.render()
1453 converter.render()
1425 elif format == 'markdown':
1454 elif format == 'markdown':
1426 converter = ConverterMarkdown(infile)
1455 converter = ConverterMarkdown(infile)
1427 converter.render()
1456 converter.render()
1428 elif format == 'html':
1457 elif format == 'html':
1429 converter = ConverterHTML(infile)
1458 converter = ConverterHTML(infile)
1430 htmlfname = converter.render()
1459 htmlfname = converter.render()
1431 elif format == 'blogger-html':
1460 elif format == 'blogger-html':
1432 converter = ConverterBloggerHTML(infile)
1461 converter = ConverterBloggerHTML(infile)
1433 htmlfname = converter.render()
1462 htmlfname = converter.render()
1434 elif format == 'latex':
1463 elif format == 'latex':
1435 converter = ConverterLaTeX(infile)
1464 converter = ConverterLaTeX(infile)
1436 latexfname = converter.render()
1465 latexfname = converter.render()
1437 elif format == 'py':
1466 elif format == 'py':
1438 converter = ConverterPy(infile)
1467 converter = ConverterPy(infile)
1439 converter.render()
1468 converter.render()
1440 else:
1469 else:
1441 raise SystemExit("Unknown format '%s', " % format +
1470 raise SystemExit("Unknown format '%s', " % format +
1442 "known formats are: " + known_formats)
1471 "known formats are: " + known_formats)
1443
1472
1444 #-----------------------------------------------------------------------------
1473 #-----------------------------------------------------------------------------
1445 # Script main
1474 # Script main
1446 #-----------------------------------------------------------------------------
1475 #-----------------------------------------------------------------------------
1447
1476
1448 if __name__ == '__main__':
1477 if __name__ == '__main__':
1449 parser = argparse.ArgumentParser(description=__doc__,
1478 parser = argparse.ArgumentParser(description=__doc__,
1450 formatter_class=argparse.RawTextHelpFormatter)
1479 formatter_class=argparse.RawTextHelpFormatter)
1451 # TODO: consider passing file like object around, rather than filenames
1480 # TODO: consider passing file like object around, rather than filenames
1452 # would allow us to process stdin, or even http streams
1481 # would allow us to process stdin, or even http streams
1453 #parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
1482 #parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
1454
1483
1455 #Require a filename as a positional argument
1484 #Require a filename as a positional argument
1456 parser.add_argument('infile', nargs=1)
1485 parser.add_argument('infile', nargs=1)
1457 parser.add_argument('-f', '--format', default='rst',
1486 parser.add_argument('-f', '--format', default='rst',
1458 help='Output format. Supported formats: \n' +
1487 help='Output format. Supported formats: \n' +
1459 known_formats)
1488 known_formats)
1460 args = parser.parse_args()
1489 args = parser.parse_args()
1461 main(infile=args.infile[0], format=args.format)
1490 main(infile=args.infile[0], format=args.format)
General Comments 0
You need to be logged in to leave comments. Login now