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