##// END OF EJS Templates
Full versioning added to nbformat.
Brian E. Granger -
Show More
@@ -0,0 +1,190 b''
1 import json
2 from xml.etree import ElementTree as ET
3 import re
4
5 from IPython.nbformat import v2
6 from IPython.nbformat import v1
7
8
9 current_nbformat = 2
10
11
12 class NBFormatError(Exception):
13 pass
14
15
16 def parse_json(s, **kwargs):
17 """Parse a string into a (nbformat, dict) tuple."""
18 d = json.loads(s, **kwargs)
19 nbformat = d.get('nbformat',1)
20 return nbformat, d
21
22
23 def parse_xml(s, **kwargs):
24 """Parse a string into a (nbformat, etree) tuple."""
25 root = ET.fromstring(s)
26 nbformat_e = root.find('nbformat')
27 if nbformat_e is not None:
28 nbformat = int(nbformat_e.text)
29 else:
30 raise NBFormatError('No nbformat version found')
31 return nbformat, root
32
33
34 def parse_py(s, **kwargs):
35 """Parse a string into a (nbformat, string) tuple."""
36 pattern = r'# <nbformat>(?P<nbformat>\d+)</nbformat>'
37 m = re.search(pattern,s)
38 if m is not None:
39 nbformat = int(m.group('nbformat'))
40 else:
41 raise NBFormatError('No nbformat version found')
42 return nbformat, s
43
44
45 def reads_json(s, **kwargs):
46 """Read a JSON notebook from a string and return the NotebookNode object."""
47 nbformat, d = parse_json(s, **kwargs)
48 if nbformat == 1:
49 nb = v1.to_notebook_json(d, **kwargs)
50 nb = v2.convert_to_this_nbformat(nb, orig_version=1)
51 elif nbformat == 2:
52 nb = v2.to_notebook_json(d, **kwargs)
53 else:
54 raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat)
55 return nb
56
57
58 def writes_json(nb, **kwargs):
59 return v2.writes_json(nb, **kwargs)
60
61
62 def reads_xml(s, **kwargs):
63 """Read an XML notebook from a string and return the NotebookNode object."""
64 nbformat, root = parse_xml(s, **kwargs)
65 if nbformat == 2:
66 nb = v2.to_notebook_xml(root, **kwargs)
67 else:
68 raise NBFormatError('Unsupported XML nbformat version: %i' % nbformat)
69 return nb
70
71
72 def writes_xml(nb, **kwargs):
73 return v2.writes_xml(nb, **kwargs)
74
75
76 def reads_py(s, **kwargs):
77 """Read a .py notebook from a string and return the NotebookNode object."""
78 nbformat, s = parse_py(s, **kwargs)
79 if nbformat == 2:
80 nb = v2.to_notebook_py(s, **kwargs)
81 else:
82 raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat)
83 return nb
84
85
86 def writes_py(nb, **kwargs):
87 return v2.writes_py(nb, **kwargs)
88
89
90 # High level API
91
92
93 def reads(s, format, **kwargs):
94 """Read a notebook from a string and return the NotebookNode object.
95
96 This function properly handles notebooks of any version. The notebook
97 returned will always be in the current version's format.
98
99 Parameters
100 ----------
101 s : str
102 The raw string to read the notebook from.
103 format : ('xml','json','py')
104 The format that the string is in.
105
106 Returns
107 -------
108 nb : NotebookNode
109 The notebook that was read.
110 """
111 if format == 'xml':
112 return reads_xml(s, **kwargs)
113 elif format == 'json':
114 return reads_json(s, **kwargs)
115 elif format == 'py':
116 return reads_py(s, **kwargs)
117 else:
118 raise NBFormatError('Unsupported format: %s' % format)
119
120
121 def writes(nb, format, **kwargs):
122 """Write a notebook to a string in a given format in the current nbformat version.
123
124 This function always writes the notebook in the current nbformat version.
125
126 Parameters
127 ----------
128 nb : NotebookNode
129 The notebook to write.
130 format : ('xml','json','py')
131 The format to write the notebook in.
132
133 Returns
134 -------
135 s : str
136 The notebook string.
137 """
138 if format == 'xml':
139 return writes_xml(nb, **kwargs)
140 elif format == 'json':
141 return writes_json(nb, **kwargs)
142 elif format == 'py':
143 return writes_py(nb, **kwargs)
144 else:
145 raise NBFormatError('Unsupported format: %s' % format)
146
147
148 def read(fp, format, **kwargs):
149 """Read a notebook from a file and return the NotebookNode object.
150
151 This function properly handles notebooks of any version. The notebook
152 returned will always be in the current version's format.
153
154 Parameters
155 ----------
156 fp : file
157 Any file-like object with a read method.
158 format : ('xml','json','py')
159 The format that the string is in.
160
161 Returns
162 -------
163 nb : NotebookNode
164 The notebook that was read.
165 """
166 return reads(fp.read(), format, **kwargs)
167
168
169 def write(nb, fp, format, **kwargs):
170 """Write a notebook to a file in a given format in the current nbformat version.
171
172 This function always writes the notebook in the current nbformat version.
173
174 Parameters
175 ----------
176 nb : NotebookNode
177 The notebook to write.
178 fp : file
179 Any file-like object with a write method.
180 format : ('xml','json','py')
181 The format to write the notebook in.
182
183 Returns
184 -------
185 s : str
186 The notebook string.
187 """
188 return fp.write(writes(nb, format, **kwargs))
189
190
@@ -0,0 +1,12 b''
1
2 from .nbbase import (
3 NotebookNode,
4 new_code_cell, new_text_cell, new_notebook
5 )
6
7 from .nbjson import reads as reads_json, writes as writes_json
8 from .nbjson import reads as read_json, writes as write_json
9 from .nbjson import to_notebook as to_notebook_json
10
11 from .convert import convert_to_this_nbformat
12
@@ -0,0 +1,5 b''
1
2
3 def convert_to_this_nbformat(nb, orig_version=None):
4 raise ValueError('Cannot convert to v1 notebook format')
5
@@ -0,0 +1,53 b''
1 """The basic dict based notebook format."""
2
3 import pprint
4 import uuid
5
6 from IPython.utils.ipstruct import Struct
7
8
9 class NotebookNode(Struct):
10 pass
11
12
13 def from_dict(d):
14 if isinstance(d, dict):
15 newd = NotebookNode()
16 for k,v in d.items():
17 newd[k] = from_dict(v)
18 return newd
19 elif isinstance(d, (tuple, list)):
20 return [from_dict(i) for i in d]
21 else:
22 return d
23
24
25 def new_code_cell(code=None, prompt_number=None):
26 """Create a new code cell with input and output"""
27 cell = NotebookNode()
28 cell.cell_type = u'code'
29 if code is not None:
30 cell.code = unicode(code)
31 if prompt_number is not None:
32 cell.prompt_number = int(prompt_number)
33 return cell
34
35
36 def new_text_cell(text=None):
37 """Create a new text cell."""
38 cell = NotebookNode()
39 if text is not None:
40 cell.text = unicode(text)
41 cell.cell_type = u'text'
42 return cell
43
44
45 def new_notebook(cells=None):
46 """Create a notebook by name, id and a list of worksheets."""
47 nb = NotebookNode()
48 if cells is not None:
49 nb.cells = cells
50 else:
51 nb.cells = []
52 return nb
53
@@ -0,0 +1,35 b''
1 """Read and write notebooks in JSON format."""
2
3 from base64 import encodestring
4 from .rwbase import NotebookReader, NotebookWriter
5 from .nbbase import from_dict
6 import json
7
8
9 class JSONReader(NotebookReader):
10
11 def reads(self, s, **kwargs):
12 nb = json.loads(s, **kwargs)
13 return self.to_notebook(nb, **kwargs)
14
15 def to_notebook(self, d, **kwargs):
16 """Convert from a raw JSON dict to a nested NotebookNode structure."""
17 return from_dict(d)
18
19
20 class JSONWriter(NotebookWriter):
21
22 def writes(self, nb, **kwargs):
23 kwargs['indent'] = 4
24 return json.dumps(nb, **kwargs)
25
26
27 _reader = JSONReader()
28 _writer = JSONWriter()
29
30 reads = _reader.reads
31 read = _reader.read
32 to_notebook = _reader.to_notebook
33 write = _writer.write
34 writes = _writer.writes
35
@@ -0,0 +1,26 b''
1 from base64 import encodestring, decodestring
2
3
4 class NotebookReader(object):
5
6 def reads(self, s, **kwargs):
7 """Read a notebook from a string."""
8 raise NotImplementedError("loads must be implemented in a subclass")
9
10 def read(self, fp, **kwargs):
11 """Read a notebook from a file like object"""
12 return self.reads(fp.read(), **kwargs)
13
14
15 class NotebookWriter(object):
16
17 def writes(self, nb, **kwargs):
18 """Write a notebook to a string."""
19 raise NotImplementedError("loads must be implemented in a subclass")
20
21 def write(self, nb, fp, **kwargs):
22 """Write a notebook to a file like object"""
23 return fp.write(self.writes(nb,**kwargs))
24
25
26
@@ -0,0 +1,29 b''
1 from ..nbbase import (
2 NotebookNode,
3 new_code_cell, new_text_cell, new_notebook
4 )
5
6
7
8 nb0 = new_notebook()
9
10 nb0.cells.append(new_text_cell(
11 text='Some NumPy Examples'
12 ))
13
14
15 nb0.cells.append(new_code_cell(
16 code='import numpy',
17 prompt_number=1
18 ))
19
20 nb0.cells.append(new_code_cell(
21 code='a = numpy.random.rand(100)',
22 prompt_number=2
23 ))
24
25 nb0.cells.append(new_code_cell(
26 code='print a',
27 prompt_number=3
28 ))
29
@@ -0,0 +1,41 b''
1 from unittest import TestCase
2
3 from ..nbbase import (
4 NotebookNode,
5 new_code_cell, new_text_cell, new_notebook
6 )
7
8 class TestCell(TestCase):
9
10 def test_empty_code_cell(self):
11 cc = new_code_cell()
12 self.assertEquals(cc.cell_type,'code')
13 self.assertEquals('code' not in cc, True)
14 self.assertEquals('prompt_number' not in cc, True)
15
16 def test_code_cell(self):
17 cc = new_code_cell(code='a=10', prompt_number=0)
18 self.assertEquals(cc.code, u'a=10')
19 self.assertEquals(cc.prompt_number, 0)
20
21 def test_empty_text_cell(self):
22 tc = new_text_cell()
23 self.assertEquals(tc.cell_type, 'text')
24 self.assertEquals('text' not in tc, True)
25
26 def test_text_cell(self):
27 tc = new_text_cell('hi')
28 self.assertEquals(tc.text, u'hi')
29
30
31 class TestNotebook(TestCase):
32
33 def test_empty_notebook(self):
34 nb = new_notebook()
35 self.assertEquals(nb.cells, [])
36
37 def test_notebooke(self):
38 cells = [new_code_cell(),new_text_cell()]
39 nb = new_notebook(cells=cells)
40 self.assertEquals(nb.cells,cells)
41
@@ -0,0 +1,21 b''
1
2 from .nbbase import (
3 NotebookNode,
4 new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet
5 )
6
7 from .nbjson import reads as reads_json, writes as writes_json
8 from .nbjson import reads as read_json, writes as write_json
9 from .nbjson import to_notebook as to_notebook_json
10
11 from .nbxml import reads as reads_xml, writes as writes_xml
12 from .nbxml import reads as read_xml, writes as write_xml
13 from .nbxml import to_notebook as to_notebook_xml
14
15 from .nbpy import reads as reads_py, writes as writes_py
16 from .nbpy import reads as read_py, writes as write_py
17 from .nbpy import to_notebook as to_notebook_py
18
19 from .convert import convert_to_this_nbformat
20
21
@@ -0,0 +1,20 b''
1 from .nbbase import (
2 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
3 )
4
5 def convert_to_this_nbformat(nb, orig_version=1):
6 if orig_version == 1:
7 newnb = new_notebook()
8 ws = new_worksheet()
9 for cell in nb.cells:
10 if cell.cell_type == 'code':
11 newcell = new_code_cell(input=cell.get('code'),prompt_number=cell.get('prompt_number'))
12 elif cell.cell_type == 'text':
13 newcell = new_text_cell(text=cell.get('text'))
14 ws.cells.append(newcell)
15 newnb.worksheets.append(ws)
16 return newnb
17 else:
18 raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version)
19
20
1 NO CONTENT: new file 100644
@@ -0,0 +1,15 b''
1
2 from unittest import TestCase
3
4 from ..nbjson import reads, writes
5 from .nbexamples import nb0
6
7
8 class TestJSON(TestCase):
9
10 def test_roundtrip(self):
11 s = writes(nb0)
12 self.assertEquals(reads(s),nb0)
13
14
15
1 NO CONTENT: file renamed from IPython/nbformat/tests/__init__.py to IPython/nbformat/v1/tests/__init__.py
@@ -1,7 +1,7 b''
1 1 from unittest import TestCase
2 2
3 from IPython.nbformat.nbjson import reads, writes
4 from IPython.nbformat.tests.nbexamples import nb0
3 from ..nbjson import reads, writes
4 from .nbexamples import nb0
5 5
6 6
7 7 class TestJSON(TestCase):
@@ -10,6 +10,18 b' class NotebookNode(Struct):'
10 10 pass
11 11
12 12
13 def from_dict(d):
14 if isinstance(d, dict):
15 newd = NotebookNode()
16 for k,v in d.items():
17 newd[k] = from_dict(v)
18 return newd
19 elif isinstance(d, (tuple, list)):
20 return [from_dict(i) for i in d]
21 else:
22 return d
23
24
13 25 def new_output(output_type=None, output_text=None, output_png=None,
14 26 output_html=None, output_svg=None, output_latex=None, output_json=None,
15 27 output_javascript=None):
@@ -76,6 +88,7 b' def new_worksheet(name=None, cells=None):'
76 88 def new_notebook(name=None, id=None, worksheets=None):
77 89 """Create a notebook by name, id and a list of worksheets."""
78 90 nb = NotebookNode()
91 nb.nbformat = 2
79 92 if name is not None:
80 93 nb.name = unicode(name)
81 94 if id is None:
@@ -1,6 +1,7 b''
1 1 """Read and write notebooks in JSON format."""
2 2
3 3 from base64 import encodestring
4 from .nbbase import from_dict
4 5 from .rwbase import NotebookReader, NotebookWriter, base64_decode
5 6 import json
6 7
@@ -16,9 +17,12 b' class JSONReader(NotebookReader):'
16 17
17 18 def reads(self, s, **kwargs):
18 19 nb = json.loads(s, **kwargs)
19 nb = base64_decode(nb)
20 nb = self.to_notebook(nb, **kwargs)
20 21 return nb
21 22
23 def to_notebook(self, d, **kwargs):
24 return base64_decode(from_dict(d))
25
22 26
23 27 class JSONWriter(NotebookWriter):
24 28
@@ -33,6 +37,7 b' _writer = JSONWriter()'
33 37
34 38 reads = _reader.reads
35 39 read = _reader.read
40 to_notebook = _reader.to_notebook
36 41 write = _writer.write
37 42 writes = _writer.writes
38 43
@@ -7,16 +7,20 b' from .nbbase import new_code_cell, new_worksheet, new_notebook'
7 7 class PyReader(NotebookReader):
8 8
9 9 def reads(self, s, **kwargs):
10 return self.to_notebook(s,**kwargs)
11
12 def to_notebook(self, s, **kwargs):
10 13 lines = s.splitlines()
11 14 cells = []
12 15 cell_lines = []
13 16 for line in lines:
14 17 if line.startswith(u'# <codecell>'):
18 cell_lines = []
19 if line.startswith(u'# </codecell>'):
15 20 code = u'\n'.join(cell_lines)
16 21 code = code.strip(u'\n')
17 22 if code:
18 23 cells.append(new_code_cell(input=code))
19 cell_lines = []
20 24 else:
21 25 cell_lines.append(line)
22 26 ws = new_worksheet(cells=cells)
@@ -28,13 +32,15 b' class PyWriter(NotebookWriter):'
28 32
29 33 def writes(self, nb, **kwargs):
30 34 lines = []
35 lines.extend(['# <nbformat>2</nbformat>',''])
31 36 for ws in nb.worksheets:
32 37 for cell in ws.cells:
33 38 if cell.cell_type == 'code':
34 39 input = cell.input
35 40 lines.extend([u'# <codecell>',u''])
36 41 lines.extend(input.splitlines())
37 lines.append(u'')
42 lines.extend([u'',u'# </codecell>'])
43 lines.append('')
38 44 return unicode('\n'.join(lines))
39 45
40 46
@@ -43,5 +49,7 b' _writer = PyWriter()'
43 49
44 50 reads = _reader.reads
45 51 read = _reader.read
52 to_notebook = _reader.to_notebook
46 53 write = _writer.write
47 54 writes = _writer.writes
55
@@ -1,5 +1,6 b''
1 1 """Read and write notebook files as XML."""
2 2
3 from base64 import encodestring, decodestring
3 4 from xml.etree import ElementTree as ET
4 5
5 6 from .rwbase import NotebookReader, NotebookWriter
@@ -51,11 +52,27 b' def _set_int(nbnode, attr, parent, tag):'
51 52 e.text = unicode(nbnode[attr])
52 53
53 54
55 def _get_binary(e, tag):
56 sub_e = e.find(tag)
57 if sub_e is None:
58 return None
59 else:
60 return decodestring(sub_e.text)
61
62
63 def _set_binary(nbnode, attr, parent, tag):
64 if attr in nbnode:
65 e = ET.SubElement(parent, tag)
66 e.text = encodestring(nbnode[attr])
67
68
54 69 class XMLReader(NotebookReader):
55 70
56 71 def reads(self, s, **kwargs):
57 72 root = ET.fromstring(s)
73 return self.to_notebook(root, **kwargs)
58 74
75 def to_notebook(self, root, **kwargs):
59 76 nbname = _get_text(root,'name')
60 77 nbid = _get_text(root,'id')
61 78
@@ -72,7 +89,7 b' class XMLReader(NotebookReader):'
72 89 for output_e in cell_e.find('outputs').getiterator('output'):
73 90 output_type = _get_text(output_e,'output_type')
74 91 output_text = _get_text(output_e,'text')
75 output_png = _get_text(output_e,'png')
92 output_png = _get_binary(output_e,'png')
76 93 output_svg = _get_text(output_e,'svg')
77 94 output_html = _get_text(output_e,'html')
78 95 output_latex = _get_text(output_e,'latex')
@@ -103,6 +120,7 b' class XMLWriter(NotebookWriter):'
103 120 nb_e = ET.Element('notebook')
104 121 _set_text(nb,'name',nb_e,'name')
105 122 _set_text(nb,'id',nb_e,'id')
123 _set_int(nb,'nbformat',nb_e,'nbformat')
106 124 wss_e = ET.SubElement(nb_e,'worksheets')
107 125 for ws in nb.worksheets:
108 126 ws_e = ET.SubElement(wss_e, 'worksheet')
@@ -120,7 +138,7 b' class XMLWriter(NotebookWriter):'
120 138 output_e = ET.SubElement(outputs_e, 'output')
121 139 _set_text(output,'output_type',output_e,'output_type')
122 140 _set_text(output,'text',output_e,'text')
123 _set_text(output,'png',output_e,'png')
141 _set_binary(output,'png',output_e,'png')
124 142 _set_text(output,'html',output_e,'html')
125 143 _set_text(output,'svg',output_e,'svg')
126 144 _set_text(output,'latex',output_e,'latex')
@@ -141,5 +159,7 b' _writer = XMLWriter()'
141 159
142 160 reads = _reader.reads
143 161 read = _reader.read
162 to_notebook = _reader.to_notebook
144 163 write = _writer.write
145 164 writes = _writer.writes
165
@@ -1,23 +1,23 b''
1 1 from base64 import encodestring, decodestring
2
2 import pprint
3 3
4 4 def base64_decode(nb):
5 5 """Base64 encode all bytes objects in the notebook."""
6 for ws in nb['worksheets']:
7 for cell in ws['cells']:
8 if cell['cell_type'] == 'code':
9 if cell.get('image/png',''):
10 cell['image/png'] = bytes(decodestring(cell['image/png']))
6 for ws in nb.worksheets:
7 for cell in ws.cells:
8 if cell.cell_type == 'code':
9 if 'png' in cell:
10 cell.png = bytes(decodestring(cell.png))
11 11 return nb
12 12
13 13
14 14 def base64_encode(nb):
15 15 """Base64 decode all binary objects in the notebook."""
16 for ws in nb['worksheets']:
17 for cell in ws['cells']:
18 if cell['cell_type'] == 'code':
19 if cell.get('image/png',''):
20 cell['image/png'] = unicode(encodestring(cell['image/png']))
16 for ws in nb.worksheets:
17 for cell in ws.cells:
18 if cell.cell_type == 'code':
19 if 'png' in cell:
20 cell.png = unicode(encodestring(cell.png))
21 21 return nb
22 22
23 23
@@ -29,7 +29,7 b' class NotebookReader(object):'
29 29
30 30 def read(self, fp, **kwargs):
31 31 """Read a notebook from a file like object"""
32 return self.loads(fp.read(), **kwargs)
32 return self.read(fp.read(), **kwargs)
33 33
34 34
35 35 class NotebookWriter(object):
@@ -40,7 +40,7 b' class NotebookWriter(object):'
40 40
41 41 def write(self, nb, fp, **kwargs):
42 42 """Write a notebook to a file like object"""
43 return fp.write(self.dumps(nb,**kwargs))
43 return fp.write(self.writes(nb,**kwargs))
44 44
45 45
46 46
@@ -1,4 +1,4 b''
1 from IPython.nbformat.nbbase import (
1 from ..nbbase import (
2 2 NotebookNode,
3 3 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
4 4 )
@@ -51,17 +51,23 b' nb0 = new_notebook('
51 51 worksheets=[ws, new_worksheet(name='worksheet2')]
52 52 )
53 53
54 nb0_py = """# <codecell>
54 nb0_py = """# <nbformat>2</nbformat>
55
56 # <codecell>
55 57
56 58 import numpy
57 59
60 # </codecell>
58 61 # <codecell>
59 62
60 63 a = numpy.random.rand(100)
61 64
65 # </codecell>
62 66 # <codecell>
63 67
64 68 print a
69
70 # </codecell>
65 71 """
66 72
67 73
@@ -1,6 +1,6 b''
1 1 from unittest import TestCase
2 2
3 from IPython.nbformat.nbbase import (
3 from ..nbbase import (
4 4 NotebookNode,
5 5 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
6 6 )
@@ -53,10 +53,12 b' class TestNotebook(TestCase):'
53 53 self.assertEquals('id' in nb, True)
54 54 self.assertEquals(nb.worksheets, [])
55 55 self.assertEquals('name' not in nb, True)
56 self.assertEquals(nb.nbformat,2)
56 57
57 def test_notebooke(self):
58 def test_notebook(self):
58 59 worksheets = [new_worksheet(),new_worksheet()]
59 60 nb = new_notebook(name='foo',worksheets=worksheets)
60 61 self.assertEquals(nb.name,u'foo')
61 62 self.assertEquals(nb.worksheets,worksheets)
63 self.assertEquals(nb.nbformat,2)
62 64
@@ -1,12 +1,12 b''
1 1 from unittest import TestCase
2 2
3 from IPython.nbformat.nbbase import (
3 from ..nbbase import (
4 4 NotebookNode,
5 5 new_code_cell, new_text_cell, new_worksheet, new_notebook
6 6 )
7 7
8 from IPython.nbformat.nbpy import reads, writes
9 from IPython.nbformat.tests.nbexamples import nb0, nb0_py
8 from ..nbpy import reads, writes
9 from .nbexamples import nb0, nb0_py
10 10
11 11
12 12 class TestPy(TestCase):
@@ -1,7 +1,7 b''
1 1 from unittest import TestCase
2 2
3 from IPython.nbformat.nbxml import reads, writes
4 from IPython.nbformat.tests.nbexamples import nb0
3 from ..nbxml import reads, writes
4 from .nbexamples import nb0
5 5 import pprint
6 6
7 7 class TestXML(TestCase):
General Comments 0
You need to be logged in to leave comments. Login now