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