Show More
@@ -18,9 +18,11 b' from IPython.nbformat import current as nbformat' | |||||
18 | # Class declarations |
|
18 | # Class declarations | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
|
21 | ||||
21 | class ConversionException(Exception): |
|
22 | class ConversionException(Exception): | |
22 | pass |
|
23 | pass | |
23 |
|
24 | |||
|
25 | ||||
24 | class DocStringInheritor(type): |
|
26 | class DocStringInheritor(type): | |
25 | """ |
|
27 | """ | |
26 | This metaclass will walk the list of bases until the desired |
|
28 | This metaclass will walk the list of bases until the desired | |
@@ -50,6 +52,7 b' class DocStringInheritor(type):' | |||||
50 | newClassDict[attributeName] = attribute |
|
52 | newClassDict[attributeName] = attribute | |
51 | return type.__new__(meta, classname, bases, newClassDict) |
|
53 | return type.__new__(meta, classname, bases, newClassDict) | |
52 |
|
54 | |||
|
55 | ||||
53 | class Converter(object): |
|
56 | class Converter(object): | |
54 | __metaclass__ = DocStringInheritor |
|
57 | __metaclass__ = DocStringInheritor | |
55 | default_encoding = 'utf-8' |
|
58 | default_encoding = 'utf-8' | |
@@ -85,9 +88,12 b' class Converter(object):' | |||||
85 | return getattr(self, 'render_' + cell_type, self.render_unknown) |
|
88 | return getattr(self, 'render_' + cell_type, self.render_unknown) | |
86 |
|
89 | |||
87 | def dispatch_display_format(self, format): |
|
90 | def dispatch_display_format(self, format): | |
88 | """return output_type dependent render method, for example render_output_text |
|
|||
89 |
|
|
91 | """ | |
90 | return getattr(self, 'render_display_format_' + format, self.render_unknown_display) |
|
92 | return output_type dependent render method, for example | |
|
93 | render_output_text | |||
|
94 | """ | |||
|
95 | return getattr(self, 'render_display_format_' + format, | |||
|
96 | self.render_unknown_display) | |||
91 |
|
97 | |||
92 | def convert(self, cell_separator='\n'): |
|
98 | def convert(self, cell_separator='\n'): | |
93 | """ |
|
99 | """ | |
@@ -178,7 +184,8 b' class Converter(object):' | |||||
178 | data = data.decode('base64') |
|
184 | data = data.decode('base64') | |
179 | fopen = lambda fname: open(fname, 'wb') |
|
185 | fopen = lambda fname: open(fname, 'wb') | |
180 | else: |
|
186 | else: | |
181 |
fopen = lambda fname: codecs.open(fname, 'wb', |
|
187 | fopen = lambda fname: codecs.open(fname, 'wb', | |
|
188 | self.default_encoding) | |||
182 |
|
189 | |||
183 | with fopen(fullname) as f: |
|
190 | with fopen(fullname) as f: | |
184 | f.write(data) |
|
191 | f.write(data) | |
@@ -267,7 +274,6 b' class Converter(object):' | |||||
267 | Returns list.""" |
|
274 | Returns list.""" | |
268 | raise NotImplementedError |
|
275 | raise NotImplementedError | |
269 |
|
276 | |||
270 |
|
||||
271 | def render_pyerr(self, output): |
|
277 | def render_pyerr(self, output): | |
272 | """convert pyerr part of a code cell |
|
278 | """convert pyerr part of a code cell | |
273 |
|
279 | |||
@@ -320,4 +326,3 b' class Converter(object):' | |||||
320 | Returns list. |
|
326 | Returns list. | |
321 | """ |
|
327 | """ | |
322 | raise NotImplementedError |
|
328 | raise NotImplementedError | |
323 |
|
@@ -1,6 +1,7 b'' | |||||
1 |
from converters.html import ConverterHTML |
|
1 | from converters.html import ConverterHTML | |
2 | import io |
|
2 | import io | |
3 |
|
3 | |||
|
4 | ||||
4 | class ConverterBloggerHTML(ConverterHTML): |
|
5 | class ConverterBloggerHTML(ConverterHTML): | |
5 | """Convert a notebook to html suitable for easy pasting into Blogger. |
|
6 | """Convert a notebook to html suitable for easy pasting into Blogger. | |
6 |
|
7 | |||
@@ -9,7 +10,7 b' class ConverterBloggerHTML(ConverterHTML):' | |||||
9 | Typically, the header file only needs to be used once when setting up a |
|
10 | Typically, the header file only needs to be used once when setting up a | |
10 | blog, as the CSS for all posts is stored in a single location in Blogger. |
|
11 | blog, as the CSS for all posts is stored in a single location in Blogger. | |
11 | """ |
|
12 | """ | |
12 |
|
13 | |||
13 | def optional_header(self): |
|
14 | def optional_header(self): | |
14 | with io.open(self.outbase + '_header.html', 'w', |
|
15 | with io.open(self.outbase + '_header.html', 'w', | |
15 | encoding=self.default_encoding) as f: |
|
16 | encoding=self.default_encoding) as f: | |
@@ -18,4 +19,3 b' class ConverterBloggerHTML(ConverterHTML):' | |||||
18 |
|
19 | |||
19 | def optional_footer(self): |
|
20 | def optional_footer(self): | |
20 | return [] |
|
21 | return [] | |
21 |
|
@@ -2,6 +2,7 b' from converters.base import Converter' | |||||
2 | from converters.utils import markdown2rst, rst_directive, remove_ansi |
|
2 | from converters.utils import markdown2rst, rst_directive, remove_ansi | |
3 | from IPython.utils.text import indent |
|
3 | from IPython.utils.text import indent | |
4 |
|
4 | |||
|
5 | ||||
5 | class ConverterRST(Converter): |
|
6 | class ConverterRST(Converter): | |
6 | extension = 'rst' |
|
7 | extension = 'rst' | |
7 | heading_level = {1: '=', 2: '-', 3: '`', 4: '\'', 5: '.', 6: '~'} |
|
8 | heading_level = {1: '=', 2: '-', 3: '`', 4: '\'', 5: '.', 6: '~'} | |
@@ -51,7 +52,7 b' class ConverterRST(Converter):' | |||||
51 |
|
52 | |||
52 | def _img_lines(self, img_file): |
|
53 | def _img_lines(self, img_file): | |
53 | return ['.. image:: %s' % img_file, ''] |
|
54 | return ['.. image:: %s' % img_file, ''] | |
54 |
|
55 | |||
55 | def render_display_format_text(self, output): |
|
56 | def render_display_format_text(self, output): | |
56 | return rst_directive('.. parsed-literal::', output.text) |
|
57 | return rst_directive('.. parsed-literal::', output.text) | |
57 |
|
58 | |||
@@ -67,7 +68,5 b' class ConverterRST(Converter):' | |||||
67 | def render_display_format_json(self, output): |
|
68 | def render_display_format_json(self, output): | |
68 | return rst_directive('.. raw:: json', output.json) |
|
69 | return rst_directive('.. raw:: json', output.json) | |
69 |
|
70 | |||
70 |
|
||||
71 | def render_display_format_javascript(self, output): |
|
71 | def render_display_format_javascript(self, output): | |
72 | return rst_directive('.. raw:: javascript', output.javascript) |
|
72 | return rst_directive('.. raw:: javascript', output.javascript) | |
73 |
|
@@ -12,23 +12,26 b' from IPython.utils.text import indent' | |||||
12 | from IPython.utils import path, py3compat |
|
12 | from IPython.utils import path, py3compat | |
13 | from IPython.nbformat.v3.nbjson import BytesEncoder |
|
13 | from IPython.nbformat.v3.nbjson import BytesEncoder | |
14 |
|
14 | |||
|
15 | ||||
15 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
16 | # Utility functions |
|
17 | # Utility functions | |
17 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
18 | def highlight(src, lang='ipython'): |
|
19 | def highlight(src, lang='ipython'): | |
19 | """Return a syntax-highlighted version of the input source. |
|
20 | """ | |
|
21 | Return a syntax-highlighted version of the input source. | |||
20 | """ |
|
22 | """ | |
21 | from pygments import highlight |
|
23 | from pygments import highlight | |
22 | from pygments.lexers import get_lexer_by_name |
|
24 | from pygments.lexers import get_lexer_by_name | |
23 | from pygments.formatters import HtmlFormatter |
|
25 | from pygments.formatters import HtmlFormatter | |
24 |
|
26 | |||
25 | if lang == 'ipython': |
|
27 | if lang == 'ipython': | |
26 | lexer = IPythonLexer() |
|
28 | lexer = IPythonLexer() | |
27 | else: |
|
29 | else: | |
28 | lexer = get_lexer_by_name(lang, stripall=True) |
|
30 | lexer = get_lexer_by_name(lang, stripall=True) | |
29 |
|
31 | |||
30 | return highlight(src, lexer, HtmlFormatter()) |
|
32 | return highlight(src, lexer, HtmlFormatter()) | |
31 |
|
33 | |||
|
34 | ||||
32 | def output_container(f): |
|
35 | def output_container(f): | |
33 | """add a prompt-area next to an output""" |
|
36 | """add a prompt-area next to an output""" | |
34 | def wrapped(self, output): |
|
37 | def wrapped(self, output): | |
@@ -41,18 +44,19 b' def output_container(f):' | |||||
41 | lines.extend(self._out_prompt(output)) |
|
44 | lines.extend(self._out_prompt(output)) | |
42 | classes = "output_subarea output_%s" % output.output_type |
|
45 | classes = "output_subarea output_%s" % output.output_type | |
43 | if 'html' in output.keys(): |
|
46 | if 'html' in output.keys(): | |
44 |
classes += |
|
47 | classes += ' output_html rendered_html' | |
45 | if output.output_type == 'stream': |
|
48 | if output.output_type == 'stream': | |
46 | classes += " output_%s" % output.stream |
|
49 | classes += " output_%s" % output.stream | |
47 | lines.append('<div class="%s">' % classes) |
|
50 | lines.append('<div class="%s">' % classes) | |
48 | lines.extend(rendered) |
|
51 | lines.extend(rendered) | |
49 | lines.append('</div>') # subarea |
|
52 | lines.append('</div>') # subarea | |
50 | lines.append('</div>') # output_area |
|
53 | lines.append('</div>') # output_area | |
51 |
|
54 | |||
52 | return lines |
|
55 | return lines | |
53 |
|
56 | |||
54 | return wrapped |
|
57 | return wrapped | |
55 |
|
58 | |||
|
59 | ||||
56 | def text_cell(f): |
|
60 | def text_cell(f): | |
57 | """wrap text cells in appropriate divs""" |
|
61 | """wrap text cells in appropriate divs""" | |
58 | def wrapped(self, cell): |
|
62 | def wrapped(self, cell): | |
@@ -88,9 +92,9 b' def remove_ansi(src):' | |||||
88 |
|
92 | |||
89 | def ansi2html(txt): |
|
93 | def ansi2html(txt): | |
90 | """Render ANSI colors as HTML colors |
|
94 | """Render ANSI colors as HTML colors | |
91 |
|
95 | |||
92 | This is equivalent to util.fixConsole in utils.js |
|
96 | This is equivalent to util.fixConsole in utils.js | |
93 |
|
97 | |||
94 | Parameters |
|
98 | Parameters | |
95 | ---------- |
|
99 | ---------- | |
96 | txt : string |
|
100 | txt : string | |
@@ -99,7 +103,7 b' def ansi2html(txt):' | |||||
99 | ------- |
|
103 | ------- | |
100 | string |
|
104 | string | |
101 | """ |
|
105 | """ | |
102 |
|
106 | |||
103 | ansi_colormap = { |
|
107 | ansi_colormap = { | |
104 | '30': 'ansiblack', |
|
108 | '30': 'ansiblack', | |
105 | '31': 'ansired', |
|
109 | '31': 'ansired', | |
@@ -111,7 +115,7 b' def ansi2html(txt):' | |||||
111 | '37': 'ansigrey', |
|
115 | '37': 'ansigrey', | |
112 | '01': 'ansibold', |
|
116 | '01': 'ansibold', | |
113 | } |
|
117 | } | |
114 |
|
118 | |||
115 | # do ampersand first |
|
119 | # do ampersand first | |
116 | txt = txt.replace('&', '&') |
|
120 | txt = txt.replace('&', '&') | |
117 | html_escapes = { |
|
121 | html_escapes = { | |
@@ -123,7 +127,7 b' def ansi2html(txt):' | |||||
123 | } |
|
127 | } | |
124 | for c, escape in html_escapes.iteritems(): |
|
128 | for c, escape in html_escapes.iteritems(): | |
125 | txt = txt.replace(c, escape) |
|
129 | txt = txt.replace(c, escape) | |
126 |
|
130 | |||
127 | ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m') |
|
131 | ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m') | |
128 | m = ansi_re.search(txt) |
|
132 | m = ansi_re.search(txt) | |
129 | opened = False |
|
133 | opened = False | |
@@ -133,20 +137,22 b' def ansi2html(txt):' | |||||
133 | while m: |
|
137 | while m: | |
134 | cmds = m.groups()[0].split(';') |
|
138 | cmds = m.groups()[0].split(';') | |
135 | closer = '</span>' if opened else '' |
|
139 | closer = '</span>' if opened else '' | |
136 | opened = len(cmds) > 1 or cmds[0] != '0'*len(cmds[0]); |
|
140 | # True if there is there more than one element in cmds, *or* | |
|
141 | # if there is only one but it is not equal to a string of zeroes. | |||
|
142 | opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0]) | |||
137 | classes = [] |
|
143 | classes = [] | |
138 | for cmd in cmds: |
|
144 | for cmd in cmds: | |
139 | if cmd in ansi_colormap: |
|
145 | if cmd in ansi_colormap: | |
140 | classes.append(ansi_colormap.get(cmd)) |
|
146 | classes.append(ansi_colormap.get(cmd)) | |
141 |
|
147 | |||
142 | if classes: |
|
148 | if classes: | |
143 | opener = '<span class="%s">' % (' '.join(classes)) |
|
149 | opener = '<span class="%s">' % (' '.join(classes)) | |
144 | else: |
|
150 | else: | |
145 | opener = '' |
|
151 | opener = '' | |
146 | txt = re.sub(ansi_re, closer + opener, txt, 1) |
|
152 | txt = re.sub(ansi_re, closer + opener, txt, 1) | |
147 |
|
153 | |||
148 | m = ansi_re.search(txt) |
|
154 | m = ansi_re.search(txt) | |
149 |
|
155 | |||
150 | if opened: |
|
156 | if opened: | |
151 | txt += '</span>' |
|
157 | txt += '</span>' | |
152 | return txt |
|
158 | return txt | |
@@ -177,7 +183,7 b' def markdown2latex(src):' | |||||
177 | if err: |
|
183 | if err: | |
178 | print(err, file=sys.stderr) |
|
184 | print(err, file=sys.stderr) | |
179 | #print('*'*20+'\n', out, '\n'+'*'*20) # dbg |
|
185 | #print('*'*20+'\n', out, '\n'+'*'*20) # dbg | |
180 | return unicode(out,'utf-8') |
|
186 | return unicode(out, 'utf-8') | |
181 |
|
187 | |||
182 |
|
188 | |||
183 | def markdown2rst(src): |
|
189 | def markdown2rst(src): | |
@@ -203,7 +209,7 b' def markdown2rst(src):' | |||||
203 | if err: |
|
209 | if err: | |
204 | print(err, file=sys.stderr) |
|
210 | print(err, file=sys.stderr) | |
205 | #print('*'*20+'\n', out, '\n'+'*'*20) # dbg |
|
211 | #print('*'*20+'\n', out, '\n'+'*'*20) # dbg | |
206 | return unicode(out,'utf-8') |
|
212 | return unicode(out, 'utf-8') | |
207 |
|
213 | |||
208 |
|
214 | |||
209 | def rst_directive(directive, text=''): |
|
215 | def rst_directive(directive, text=''): | |
@@ -218,9 +224,9 b" def rst_directive(directive, text=''):" | |||||
218 |
|
224 | |||
219 | def coalesce_streams(outputs): |
|
225 | def coalesce_streams(outputs): | |
220 | """merge consecutive sequences of stream output into single stream |
|
226 | """merge consecutive sequences of stream output into single stream | |
221 |
|
227 | |||
222 | to prevent extra newlines inserted at flush calls |
|
228 | to prevent extra newlines inserted at flush calls | |
223 |
|
229 | |||
224 | TODO: handle \r deletion |
|
230 | TODO: handle \r deletion | |
225 | """ |
|
231 | """ | |
226 | new_outputs = [] |
|
232 | new_outputs = [] | |
@@ -234,7 +240,7 b' def coalesce_streams(outputs):' | |||||
234 | last.text += output.text |
|
240 | last.text += output.text | |
235 | else: |
|
241 | else: | |
236 | new_outputs.append(output) |
|
242 | new_outputs.append(output) | |
237 |
|
243 | |||
238 | return new_outputs |
|
244 | return new_outputs | |
239 |
|
245 | |||
240 |
|
246 | |||
@@ -270,8 +276,8 b' def rst2simplehtml(infile):' | |||||
270 | walker = iter(html.splitlines()) |
|
276 | walker = iter(html.splitlines()) | |
271 |
|
277 | |||
272 | # Find start of main text, break out to then print until we find end /div. |
|
278 | # Find start of main text, break out to then print until we find end /div. | |
273 |
# This may only work if there's a real title defined so we get a 'div |
|
279 | # This may only work if there's a real title defined so we get a 'div | |
274 | # tag, I haven't really tried. |
|
280 | # class' tag, I haven't really tried. | |
275 | for line in walker: |
|
281 | for line in walker: | |
276 | if line.startswith('<body>'): |
|
282 | if line.startswith('<body>'): | |
277 | break |
|
283 | break | |
@@ -286,6 +292,7 b' def rst2simplehtml(infile):' | |||||
286 |
|
292 | |||
287 | return newfname |
|
293 | return newfname | |
288 |
|
294 | |||
|
295 | ||||
289 | #----------------------------------------------------------------------------- |
|
296 | #----------------------------------------------------------------------------- | |
290 | # Cell-level functions -- similar to IPython.nbformat.v3.rwbase functions |
|
297 | # Cell-level functions -- similar to IPython.nbformat.v3.rwbase functions | |
291 | # but at cell level instead of whole notebook level |
|
298 | # but at cell level instead of whole notebook level | |
@@ -295,7 +302,7 b' def writes_cell(cell, **kwargs):' | |||||
295 | kwargs['cls'] = BytesEncoder |
|
302 | kwargs['cls'] = BytesEncoder | |
296 | kwargs['indent'] = 3 |
|
303 | kwargs['indent'] = 3 | |
297 | kwargs['sort_keys'] = True |
|
304 | kwargs['sort_keys'] = True | |
298 | kwargs['separators'] = (',',': ') |
|
305 | kwargs['separators'] = (',', ': ') | |
299 | if kwargs.pop('split_lines', True): |
|
306 | if kwargs.pop('split_lines', True): | |
300 | cell = split_lines_cell(copy.deepcopy(cell)) |
|
307 | cell = split_lines_cell(copy.deepcopy(cell)) | |
301 | return py3compat.str_to_unicode(json.dumps(cell, **kwargs), 'utf-8') |
|
308 | return py3compat.str_to_unicode(json.dumps(cell, **kwargs), 'utf-8') | |
@@ -306,7 +313,7 b" _multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json']" | |||||
306 |
|
313 | |||
307 | def split_lines_cell(cell): |
|
314 | def split_lines_cell(cell): | |
308 | """ |
|
315 | """ | |
309 |
Split lines within a cell as in |
|
316 | Split lines within a cell as in | |
310 | IPython.nbformat.v3.rwbase.split_lines |
|
317 | IPython.nbformat.v3.rwbase.split_lines | |
311 |
|
318 | |||
312 | """ |
|
319 | """ | |
@@ -318,7 +325,7 b' def split_lines_cell(cell):' | |||||
318 | item = output.get(key, None) |
|
325 | item = output.get(key, None) | |
319 | if isinstance(item, basestring): |
|
326 | if isinstance(item, basestring): | |
320 | output[key] = (item + '\n').splitlines() |
|
327 | output[key] = (item + '\n').splitlines() | |
321 | else: # text, heading cell |
|
328 | else: # text, heading cell | |
322 | for key in ['source', 'rendered']: |
|
329 | for key in ['source', 'rendered']: | |
323 | item = cell.get(key, None) |
|
330 | item = cell.get(key, None) | |
324 | if isinstance(item, basestring): |
|
331 | if isinstance(item, basestring): |
General Comments 0
You need to be logged in to leave comments.
Login now