##// END OF EJS Templates
preserve trailing newlines in ipynb...
MinRK -
Show More
@@ -1,174 +1,174 b''
1 1 """Base classes and utilities for readers and writers.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 from base64 import encodestring, decodestring
20 20 import pprint
21 21
22 22 from IPython.utils import py3compat
23 23
24 24 str_to_bytes = py3compat.str_to_bytes
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Code
28 28 #-----------------------------------------------------------------------------
29 29
30 30 def restore_bytes(nb):
31 31 """Restore bytes of image data from unicode-only formats.
32 32
33 33 Base64 encoding is handled elsewhere. Bytes objects in the notebook are
34 34 always b64-encoded. We DO NOT encode/decode around file formats.
35 35 """
36 36 for ws in nb.worksheets:
37 37 for cell in ws.cells:
38 38 if cell.cell_type == 'code':
39 39 for output in cell.outputs:
40 40 if 'png' in output:
41 41 output.png = str_to_bytes(output.png, 'ascii')
42 42 if 'jpeg' in output:
43 43 output.jpeg = str_to_bytes(output.jpeg, 'ascii')
44 44 return nb
45 45
46 46 # output keys that are likely to have multiline values
47 47 _multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json']
48 48
49 49 def rejoin_lines(nb):
50 50 """rejoin multiline text into strings
51 51
52 52 For reversing effects of ``split_lines(nb)``.
53 53
54 54 This only rejoins lines that have been split, so if text objects were not split
55 55 they will pass through unchanged.
56 56
57 57 Used when reading JSON files that may have been passed through split_lines.
58 58 """
59 59 for ws in nb.worksheets:
60 60 for cell in ws.cells:
61 61 if cell.cell_type == 'code':
62 62 if 'input' in cell and isinstance(cell.input, list):
63 63 cell.input = u'\n'.join(cell.input)
64 64 for output in cell.outputs:
65 65 for key in _multiline_outputs:
66 66 item = output.get(key, None)
67 67 if isinstance(item, list):
68 68 output[key] = u'\n'.join(item)
69 69 else: # text, heading cell
70 70 for key in ['source', 'rendered']:
71 71 item = cell.get(key, None)
72 72 if isinstance(item, list):
73 73 cell[key] = u'\n'.join(item)
74 74 return nb
75 75
76 76
77 77 def split_lines(nb):
78 78 """split likely multiline text into lists of strings
79 79
80 80 For file output more friendly to line-based VCS. ``rejoin_lines(nb)`` will
81 81 reverse the effects of ``split_lines(nb)``.
82 82
83 83 Used when writing JSON files.
84 84 """
85 85 for ws in nb.worksheets:
86 86 for cell in ws.cells:
87 87 if cell.cell_type == 'code':
88 88 if 'input' in cell and isinstance(cell.input, basestring):
89 cell.input = cell.input.splitlines()
89 cell.input = (cell.input + '\n').splitlines()
90 90 for output in cell.outputs:
91 91 for key in _multiline_outputs:
92 92 item = output.get(key, None)
93 93 if isinstance(item, basestring):
94 output[key] = item.splitlines()
94 output[key] = (item + '\n').splitlines()
95 95 else: # text, heading cell
96 96 for key in ['source', 'rendered']:
97 97 item = cell.get(key, None)
98 98 if isinstance(item, basestring):
99 cell[key] = item.splitlines()
99 cell[key] = (item + '\n').splitlines()
100 100 return nb
101 101
102 102 # b64 encode/decode are never actually used, because all bytes objects in
103 103 # the notebook are already b64-encoded, and we don't need/want to double-encode
104 104
105 105 def base64_decode(nb):
106 106 """Restore all bytes objects in the notebook from base64-encoded strings.
107 107
108 108 Note: This is never used
109 109 """
110 110 for ws in nb.worksheets:
111 111 for cell in ws.cells:
112 112 if cell.cell_type == 'code':
113 113 for output in cell.outputs:
114 114 if 'png' in output:
115 115 if isinstance(output.png, unicode):
116 116 output.png = output.png.encode('ascii')
117 117 output.png = decodestring(output.png)
118 118 if 'jpeg' in output:
119 119 if isinstance(output.jpeg, unicode):
120 120 output.jpeg = output.jpeg.encode('ascii')
121 121 output.jpeg = decodestring(output.jpeg)
122 122 return nb
123 123
124 124
125 125 def base64_encode(nb):
126 126 """Base64 encode all bytes objects in the notebook.
127 127
128 128 These will be b64-encoded unicode strings
129 129
130 130 Note: This is never used
131 131 """
132 132 for ws in nb.worksheets:
133 133 for cell in ws.cells:
134 134 if cell.cell_type == 'code':
135 135 for output in cell.outputs:
136 136 if 'png' in output:
137 137 output.png = encodestring(output.png).decode('ascii')
138 138 if 'jpeg' in output:
139 139 output.jpeg = encodestring(output.jpeg).decode('ascii')
140 140 return nb
141 141
142 142
143 143 class NotebookReader(object):
144 144 """A class for reading notebooks."""
145 145
146 146 def reads(self, s, **kwargs):
147 147 """Read a notebook from a string."""
148 148 raise NotImplementedError("loads must be implemented in a subclass")
149 149
150 150 def read(self, fp, **kwargs):
151 151 """Read a notebook from a file like object"""
152 152 nbs = fp.read()
153 153 if not py3compat.PY3 and not isinstance(nbs, unicode):
154 154 nbs = py3compat.str_to_unicode(nbs)
155 155 return self.reads(nbs, **kwargs)
156 156
157 157
158 158 class NotebookWriter(object):
159 159 """A class for writing notebooks."""
160 160
161 161 def writes(self, nb, **kwargs):
162 162 """Write a notebook to a string."""
163 163 raise NotImplementedError("loads must be implemented in a subclass")
164 164
165 165 def write(self, nb, fp, **kwargs):
166 166 """Write a notebook to a file like object"""
167 167 nbs = self.writes(nb,**kwargs)
168 168 if not py3compat.PY3 and not isinstance(nbs, unicode):
169 169 # this branch is likely only taken for JSON on Python 2
170 170 nbs = py3compat.str_to_unicode(nbs)
171 171 return fp.write(nbs)
172 172
173 173
174 174
@@ -1,129 +1,147 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 import os
4 4 from base64 import encodestring
5 5
6 6 from ..nbbase import (
7 7 NotebookNode,
8 8 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output,
9 9 new_metadata, new_author, new_heading_cell, nbformat
10 10 )
11 11
12 12 # some random base64-encoded *bytes*
13 13 png = encodestring(os.urandom(5))
14 14 jpeg = encodestring(os.urandom(6))
15 15
16 16 ws = new_worksheet(name='worksheet1')
17 17
18 18 ws.cells.append(new_text_cell(
19 19 u'html',
20 20 source='Some NumPy Examples',
21 21 rendered='Some NumPy Examples'
22 22 ))
23 23
24 24
25 25 ws.cells.append(new_code_cell(
26 26 input='import numpy',
27 27 prompt_number=1,
28 28 collapsed=False
29 29 ))
30 30
31 31 ws.cells.append(new_text_cell(
32 32 u'markdown',
33 33 source='A random array',
34 34 rendered='A random array'
35 35 ))
36 36
37 37 ws.cells.append(new_text_cell(
38 38 u'plaintext',
39 39 source='A random array',
40 40 ))
41 41
42 42 ws.cells.append(new_heading_cell(
43 43 u'My Heading',
44 44 level=2
45 45 ))
46 46
47 47 ws.cells.append(new_code_cell(
48 48 input='a = numpy.random.rand(100)',
49 49 prompt_number=2,
50 50 collapsed=True
51 51 ))
52 ws.cells.append(new_code_cell(
53 input='a = 10\nb = 5\n',
54 prompt_number=3,
55 ))
56 ws.cells.append(new_code_cell(
57 input='a = 10\nb = 5',
58 prompt_number=4,
59 ))
52 60
53 61 ws.cells.append(new_code_cell(
54 62 input=u'print "ünîcødé"',
55 63 prompt_number=3,
56 64 collapsed=False,
57 65 outputs=[new_output(
58 66 output_type=u'pyout',
59 67 output_text=u'<array a>',
60 68 output_html=u'The HTML rep',
61 69 output_latex=u'$a$',
62 70 output_png=png,
63 71 output_jpeg=jpeg,
64 72 output_svg=u'<svg>',
65 73 output_json=u'json data',
66 74 output_javascript=u'var i=0;',
67 75 prompt_number=3
68 76 ),new_output(
69 77 output_type=u'display_data',
70 78 output_text=u'<array a>',
71 79 output_html=u'The HTML rep',
72 80 output_latex=u'$a$',
73 81 output_png=png,
74 82 output_jpeg=jpeg,
75 83 output_svg=u'<svg>',
76 84 output_json=u'json data',
77 85 output_javascript=u'var i=0;'
78 86 ),new_output(
79 87 output_type=u'pyerr',
80 88 etype=u'NameError',
81 89 evalue=u'NameError was here',
82 90 traceback=[u'frame 0', u'frame 1', u'frame 2']
83 91 )]
84 92 ))
85 93
86 94 authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com',
87 95 affiliation=u'Fox',url=u'http://www.fox.com')]
88 96 md = new_metadata(name=u'My Notebook',license=u'BSD',created=u'8601_goes_here',
89 97 modified=u'8601_goes_here',gistid=u'21341231',authors=authors)
90 98
91 99 nb0 = new_notebook(
92 100 worksheets=[ws, new_worksheet(name='worksheet2')],
93 101 metadata=md
94 102 )
95 103
96 104 nb0_py = u"""# -*- coding: utf-8 -*-
97 105 # <nbformat>%i</nbformat>
98 106
99 107 # <htmlcell>
100 108
101 109 # Some NumPy Examples
102 110
103 111 # <codecell>
104 112
105 113 import numpy
106 114
107 115 # <markdowncell>
108 116
109 117 # A random array
110 118
111 119 # <plaintextcell>
112 120
113 121 # A random array
114 122
115 123 # <headingcell level=2>
116 124
117 125 # My Heading
118 126
119 127 # <codecell>
120 128
121 129 a = numpy.random.rand(100)
122 130
123 131 # <codecell>
124 132
133 a = 10
134 b = 5
135
136 # <codecell>
137
138 a = 10
139 b = 5
140
141 # <codecell>
142
125 143 print "ünîcødé"
126 144
127 145 """ % nbformat
128 146
129 147
@@ -1,38 +1,43 b''
1 1 # -*- coding: utf8 -*-
2 2 from . import formattest
3 3
4 4 from .. import nbpy
5 5 from .nbexamples import nb0, nb0_py
6 6
7 7
8 8 class TestPy(formattest.NBFormatTestCase):
9 9
10 10 nb0_ref = nb0_py
11 11 ext = 'py'
12 12 mod = nbpy
13 13 ignored_keys = ['collapsed', 'outputs', 'prompt_number', 'metadata']
14 14
15 15 def assertSubset(self, da, db):
16 16 """assert that da is a subset of db, ignoring self.ignored_keys.
17 17
18 18 Called recursively on containers, ultimately comparing individual
19 19 elements.
20 20 """
21 21 if isinstance(da, dict):
22 22 for k,v in da.iteritems():
23 23 if k in self.ignored_keys:
24 24 continue
25 25 self.assertTrue(k in db)
26 26 self.assertSubset(v, db[k])
27 27 elif isinstance(da, list):
28 28 for a,b in zip(da, db):
29 29 self.assertSubset(a,b)
30 30 else:
31 if isinstance(da, basestring) and isinstance(db, basestring):
32 # pyfile is not sensitive to preserving leading/trailing
33 # newlines in blocks through roundtrip
34 da = da.strip('\n')
35 db = db.strip('\n')
31 36 self.assertEquals(da, db)
32 37 return True
33 38
34 39 def assertNBEquals(self, nba, nbb):
35 40 # since roundtrip is lossy, only compare keys that are preserved
36 41 # assumes nba is read from my file format
37 42 return self.assertSubset(nba, nbb)
38 43
General Comments 0
You need to be logged in to leave comments. Login now