Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Full versioning added to nbformat.

  • Loading branch information...
commit 0251893c243203bee3d0d49e24e4b3c87b5ae595 1 parent 4b0e672
@ellisonbg ellisonbg authored
View
190 IPython/nbformat/current.py
@@ -0,0 +1,190 @@
+import json
+from xml.etree import ElementTree as ET
+import re
+
+from IPython.nbformat import v2
+from IPython.nbformat import v1
+
+
+current_nbformat = 2
+
+
+class NBFormatError(Exception):
+ pass
+
+
+def parse_json(s, **kwargs):
+ """Parse a string into a (nbformat, dict) tuple."""
+ d = json.loads(s, **kwargs)
+ nbformat = d.get('nbformat',1)
+ return nbformat, d
+
+
+def parse_xml(s, **kwargs):
+ """Parse a string into a (nbformat, etree) tuple."""
+ root = ET.fromstring(s)
+ nbformat_e = root.find('nbformat')
+ if nbformat_e is not None:
+ nbformat = int(nbformat_e.text)
+ else:
+ raise NBFormatError('No nbformat version found')
+ return nbformat, root
+
+
+def parse_py(s, **kwargs):
+ """Parse a string into a (nbformat, string) tuple."""
+ pattern = r'# <nbformat>(?P<nbformat>\d+)</nbformat>'
+ m = re.search(pattern,s)
+ if m is not None:
+ nbformat = int(m.group('nbformat'))
+ else:
+ raise NBFormatError('No nbformat version found')
+ return nbformat, s
+
+
+def reads_json(s, **kwargs):
+ """Read a JSON notebook from a string and return the NotebookNode object."""
+ nbformat, d = parse_json(s, **kwargs)
+ if nbformat == 1:
+ nb = v1.to_notebook_json(d, **kwargs)
+ nb = v2.convert_to_this_nbformat(nb, orig_version=1)
+ elif nbformat == 2:
+ nb = v2.to_notebook_json(d, **kwargs)
+ else:
+ raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat)
+ return nb
+
+
+def writes_json(nb, **kwargs):
+ return v2.writes_json(nb, **kwargs)
+
+
+def reads_xml(s, **kwargs):
+ """Read an XML notebook from a string and return the NotebookNode object."""
+ nbformat, root = parse_xml(s, **kwargs)
+ if nbformat == 2:
+ nb = v2.to_notebook_xml(root, **kwargs)
+ else:
+ raise NBFormatError('Unsupported XML nbformat version: %i' % nbformat)
+ return nb
+
+
+def writes_xml(nb, **kwargs):
+ return v2.writes_xml(nb, **kwargs)
+
+
+def reads_py(s, **kwargs):
+ """Read a .py notebook from a string and return the NotebookNode object."""
+ nbformat, s = parse_py(s, **kwargs)
+ if nbformat == 2:
+ nb = v2.to_notebook_py(s, **kwargs)
+ else:
+ raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat)
+ return nb
+
+
+def writes_py(nb, **kwargs):
+ return v2.writes_py(nb, **kwargs)
+
+
+# High level API
+
+
+def reads(s, format, **kwargs):
+ """Read a notebook from a string and return the NotebookNode object.
+
+ This function properly handles notebooks of any version. The notebook
+ returned will always be in the current version's format.
+
+ Parameters
+ ----------
+ s : str
+ The raw string to read the notebook from.
+ format : ('xml','json','py')
+ The format that the string is in.
+
+ Returns
+ -------
+ nb : NotebookNode
+ The notebook that was read.
+ """
+ if format == 'xml':
+ return reads_xml(s, **kwargs)
+ elif format == 'json':
+ return reads_json(s, **kwargs)
+ elif format == 'py':
+ return reads_py(s, **kwargs)
+ else:
+ raise NBFormatError('Unsupported format: %s' % format)
+
+
+def writes(nb, format, **kwargs):
+ """Write a notebook to a string in a given format in the current nbformat version.
+
+ This function always writes the notebook in the current nbformat version.
+
+ Parameters
+ ----------
+ nb : NotebookNode
+ The notebook to write.
+ format : ('xml','json','py')
+ The format to write the notebook in.
+
+ Returns
+ -------
+ s : str
+ The notebook string.
+ """
+ if format == 'xml':
+ return writes_xml(nb, **kwargs)
+ elif format == 'json':
+ return writes_json(nb, **kwargs)
+ elif format == 'py':
+ return writes_py(nb, **kwargs)
+ else:
+ raise NBFormatError('Unsupported format: %s' % format)
+
+
+def read(fp, format, **kwargs):
+ """Read a notebook from a file and return the NotebookNode object.
+
+ This function properly handles notebooks of any version. The notebook
+ returned will always be in the current version's format.
+
+ Parameters
+ ----------
+ fp : file
+ Any file-like object with a read method.
+ format : ('xml','json','py')
+ The format that the string is in.
+
+ Returns
+ -------
+ nb : NotebookNode
+ The notebook that was read.
+ """
+ return reads(fp.read(), format, **kwargs)
+
+
+def write(nb, fp, format, **kwargs):
+ """Write a notebook to a file in a given format in the current nbformat version.
+
+ This function always writes the notebook in the current nbformat version.
+
+ Parameters
+ ----------
+ nb : NotebookNode
+ The notebook to write.
+ fp : file
+ Any file-like object with a write method.
+ format : ('xml','json','py')
+ The format to write the notebook in.
+
+ Returns
+ -------
+ s : str
+ The notebook string.
+ """
+ return fp.write(writes(nb, format, **kwargs))
+
+
View
12 IPython/nbformat/v1/__init__.py
@@ -0,0 +1,12 @@
+
+from .nbbase import (
+ NotebookNode,
+ new_code_cell, new_text_cell, new_notebook
+)
+
+from .nbjson import reads as reads_json, writes as writes_json
+from .nbjson import reads as read_json, writes as write_json
+from .nbjson import to_notebook as to_notebook_json
+
+from .convert import convert_to_this_nbformat
+
View
5 IPython/nbformat/v1/convert.py
@@ -0,0 +1,5 @@
+
+
+def convert_to_this_nbformat(nb, orig_version=None):
+ raise ValueError('Cannot convert to v1 notebook format')
+
View
53 IPython/nbformat/v1/nbbase.py
@@ -0,0 +1,53 @@
+"""The basic dict based notebook format."""
+
+import pprint
+import uuid
+
+from IPython.utils.ipstruct import Struct
+
+
+class NotebookNode(Struct):
+ pass
+
+
+def from_dict(d):
+ if isinstance(d, dict):
+ newd = NotebookNode()
+ for k,v in d.items():
+ newd[k] = from_dict(v)
+ return newd
+ elif isinstance(d, (tuple, list)):
+ return [from_dict(i) for i in d]
+ else:
+ return d
+
+
+def new_code_cell(code=None, prompt_number=None):
+ """Create a new code cell with input and output"""
+ cell = NotebookNode()
+ cell.cell_type = u'code'
+ if code is not None:
+ cell.code = unicode(code)
+ if prompt_number is not None:
+ cell.prompt_number = int(prompt_number)
+ return cell
+
+
+def new_text_cell(text=None):
+ """Create a new text cell."""
+ cell = NotebookNode()
+ if text is not None:
+ cell.text = unicode(text)
+ cell.cell_type = u'text'
+ return cell
+
+
+def new_notebook(cells=None):
+ """Create a notebook by name, id and a list of worksheets."""
+ nb = NotebookNode()
+ if cells is not None:
+ nb.cells = cells
+ else:
+ nb.cells = []
+ return nb
+
View
35 IPython/nbformat/v1/nbjson.py
@@ -0,0 +1,35 @@
+"""Read and write notebooks in JSON format."""
+
+from base64 import encodestring
+from .rwbase import NotebookReader, NotebookWriter
+from .nbbase import from_dict
+import json
+
+
+class JSONReader(NotebookReader):
+
+ def reads(self, s, **kwargs):
+ nb = json.loads(s, **kwargs)
+ return self.to_notebook(nb, **kwargs)
+
+ def to_notebook(self, d, **kwargs):
+ """Convert from a raw JSON dict to a nested NotebookNode structure."""
+ return from_dict(d)
+
+
+class JSONWriter(NotebookWriter):
+
+ def writes(self, nb, **kwargs):
+ kwargs['indent'] = 4
+ return json.dumps(nb, **kwargs)
+
+
+_reader = JSONReader()
+_writer = JSONWriter()
+
+reads = _reader.reads
+read = _reader.read
+to_notebook = _reader.to_notebook
+write = _writer.write
+writes = _writer.writes
+
View
26 IPython/nbformat/v1/rwbase.py
@@ -0,0 +1,26 @@
+from base64 import encodestring, decodestring
+
+
+class NotebookReader(object):
+
+ def reads(self, s, **kwargs):
+ """Read a notebook from a string."""
+ raise NotImplementedError("loads must be implemented in a subclass")
+
+ def read(self, fp, **kwargs):
+ """Read a notebook from a file like object"""
+ return self.reads(fp.read(), **kwargs)
+
+
+class NotebookWriter(object):
+
+ def writes(self, nb, **kwargs):
+ """Write a notebook to a string."""
+ raise NotImplementedError("loads must be implemented in a subclass")
+
+ def write(self, nb, fp, **kwargs):
+ """Write a notebook to a file like object"""
+ return fp.write(self.writes(nb,**kwargs))
+
+
+
View
0  IPython/nbformat/tests/__init__.py → IPython/nbformat/v1/tests/__init__.py
File renamed without changes
View
29 IPython/nbformat/v1/tests/nbexamples.py
@@ -0,0 +1,29 @@
+from ..nbbase import (
+ NotebookNode,
+ new_code_cell, new_text_cell, new_notebook
+)
+
+
+
+nb0 = new_notebook()
+
+nb0.cells.append(new_text_cell(
+ text='Some NumPy Examples'
+))
+
+
+nb0.cells.append(new_code_cell(
+ code='import numpy',
+ prompt_number=1
+))
+
+nb0.cells.append(new_code_cell(
+ code='a = numpy.random.rand(100)',
+ prompt_number=2
+))
+
+nb0.cells.append(new_code_cell(
+ code='print a',
+ prompt_number=3
+))
+
View
4 IPython/nbformat/tests/test_json.py → IPython/nbformat/v1/tests/test_json.py
@@ -1,7 +1,7 @@
from unittest import TestCase
-from IPython.nbformat.nbjson import reads, writes
-from IPython.nbformat.tests.nbexamples import nb0
+from ..nbjson import reads, writes
+from .nbexamples import nb0
class TestJSON(TestCase):
View
41 IPython/nbformat/v1/tests/test_nbbase.py
@@ -0,0 +1,41 @@
+from unittest import TestCase
+
+from ..nbbase import (
+ NotebookNode,
+ new_code_cell, new_text_cell, new_notebook
+)
+
+class TestCell(TestCase):
+
+ def test_empty_code_cell(self):
+ cc = new_code_cell()
+ self.assertEquals(cc.cell_type,'code')
+ self.assertEquals('code' not in cc, True)
+ self.assertEquals('prompt_number' not in cc, True)
+
+ def test_code_cell(self):
+ cc = new_code_cell(code='a=10', prompt_number=0)
+ self.assertEquals(cc.code, u'a=10')
+ self.assertEquals(cc.prompt_number, 0)
+
+ def test_empty_text_cell(self):
+ tc = new_text_cell()
+ self.assertEquals(tc.cell_type, 'text')
+ self.assertEquals('text' not in tc, True)
+
+ def test_text_cell(self):
+ tc = new_text_cell('hi')
+ self.assertEquals(tc.text, u'hi')
+
+
+class TestNotebook(TestCase):
+
+ def test_empty_notebook(self):
+ nb = new_notebook()
+ self.assertEquals(nb.cells, [])
+
+ def test_notebooke(self):
+ cells = [new_code_cell(),new_text_cell()]
+ nb = new_notebook(cells=cells)
+ self.assertEquals(nb.cells,cells)
+
View
21 IPython/nbformat/v2/__init__.py
@@ -0,0 +1,21 @@
+
+from .nbbase import (
+ NotebookNode,
+ new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet
+)
+
+from .nbjson import reads as reads_json, writes as writes_json
+from .nbjson import reads as read_json, writes as write_json
+from .nbjson import to_notebook as to_notebook_json
+
+from .nbxml import reads as reads_xml, writes as writes_xml
+from .nbxml import reads as read_xml, writes as write_xml
+from .nbxml import to_notebook as to_notebook_xml
+
+from .nbpy import reads as reads_py, writes as writes_py
+from .nbpy import reads as read_py, writes as write_py
+from .nbpy import to_notebook as to_notebook_py
+
+from .convert import convert_to_this_nbformat
+
+
View
20 IPython/nbformat/v2/convert.py
@@ -0,0 +1,20 @@
+from .nbbase import (
+ new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
+)
+
+def convert_to_this_nbformat(nb, orig_version=1):
+ if orig_version == 1:
+ newnb = new_notebook()
+ ws = new_worksheet()
+ for cell in nb.cells:
+ if cell.cell_type == 'code':
+ newcell = new_code_cell(input=cell.get('code'),prompt_number=cell.get('prompt_number'))
+ elif cell.cell_type == 'text':
+ newcell = new_text_cell(text=cell.get('text'))
+ ws.cells.append(newcell)
+ newnb.worksheets.append(ws)
+ return newnb
+ else:
+ raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version)
+
+
View
13 IPython/nbformat/nbbase.py → IPython/nbformat/v2/nbbase.py
@@ -10,6 +10,18 @@ class NotebookNode(Struct):
pass
+def from_dict(d):
+ if isinstance(d, dict):
+ newd = NotebookNode()
+ for k,v in d.items():
+ newd[k] = from_dict(v)
+ return newd
+ elif isinstance(d, (tuple, list)):
+ return [from_dict(i) for i in d]
+ else:
+ return d
+
+
def new_output(output_type=None, output_text=None, output_png=None,
output_html=None, output_svg=None, output_latex=None, output_json=None,
output_javascript=None):
@@ -76,6 +88,7 @@ def new_worksheet(name=None, cells=None):
def new_notebook(name=None, id=None, worksheets=None):
"""Create a notebook by name, id and a list of worksheets."""
nb = NotebookNode()
+ nb.nbformat = 2
if name is not None:
nb.name = unicode(name)
if id is None:
View
7 IPython/nbformat/nbjson.py → IPython/nbformat/v2/nbjson.py
@@ -1,6 +1,7 @@
"""Read and write notebooks in JSON format."""
from base64 import encodestring
+from .nbbase import from_dict
from .rwbase import NotebookReader, NotebookWriter, base64_decode
import json
@@ -16,9 +17,12 @@ class JSONReader(NotebookReader):
def reads(self, s, **kwargs):
nb = json.loads(s, **kwargs)
- nb = base64_decode(nb)
+ nb = self.to_notebook(nb, **kwargs)
return nb
+ def to_notebook(self, d, **kwargs):
+ return base64_decode(from_dict(d))
+
class JSONWriter(NotebookWriter):
@@ -33,6 +37,7 @@ def writes(self, nb, **kwargs):
reads = _reader.reads
read = _reader.read
+to_notebook = _reader.to_notebook
write = _writer.write
writes = _writer.writes
View
12 IPython/nbformat/nbpy.py → IPython/nbformat/v2/nbpy.py
@@ -7,16 +7,20 @@
class PyReader(NotebookReader):
def reads(self, s, **kwargs):
+ return self.to_notebook(s,**kwargs)
+
+ def to_notebook(self, s, **kwargs):
lines = s.splitlines()
cells = []
cell_lines = []
for line in lines:
if line.startswith(u'# <codecell>'):
+ cell_lines = []
+ if line.startswith(u'# </codecell>'):
code = u'\n'.join(cell_lines)
code = code.strip(u'\n')
if code:
cells.append(new_code_cell(input=code))
- cell_lines = []
else:
cell_lines.append(line)
ws = new_worksheet(cells=cells)
@@ -28,13 +32,15 @@ class PyWriter(NotebookWriter):
def writes(self, nb, **kwargs):
lines = []
+ lines.extend(['# <nbformat>2</nbformat>',''])
for ws in nb.worksheets:
for cell in ws.cells:
if cell.cell_type == 'code':
input = cell.input
lines.extend([u'# <codecell>',u''])
lines.extend(input.splitlines())
- lines.append(u'')
+ lines.extend([u'',u'# </codecell>'])
+ lines.append('')
return unicode('\n'.join(lines))
@@ -43,5 +49,7 @@ def writes(self, nb, **kwargs):
reads = _reader.reads
read = _reader.read
+to_notebook = _reader.to_notebook
write = _writer.write
writes = _writer.writes
+
View
24 IPython/nbformat/nbxml.py → IPython/nbformat/v2/nbxml.py
@@ -1,5 +1,6 @@
"""Read and write notebook files as XML."""
+from base64 import encodestring, decodestring
from xml.etree import ElementTree as ET
from .rwbase import NotebookReader, NotebookWriter
@@ -51,11 +52,27 @@ def _set_int(nbnode, attr, parent, tag):
e.text = unicode(nbnode[attr])
+def _get_binary(e, tag):
+ sub_e = e.find(tag)
+ if sub_e is None:
+ return None
+ else:
+ return decodestring(sub_e.text)
+
+
+def _set_binary(nbnode, attr, parent, tag):
+ if attr in nbnode:
+ e = ET.SubElement(parent, tag)
+ e.text = encodestring(nbnode[attr])
+
+
class XMLReader(NotebookReader):
def reads(self, s, **kwargs):
root = ET.fromstring(s)
+ return self.to_notebook(root, **kwargs)
+ def to_notebook(self, root, **kwargs):
nbname = _get_text(root,'name')
nbid = _get_text(root,'id')
@@ -72,7 +89,7 @@ def reads(self, s, **kwargs):
for output_e in cell_e.find('outputs').getiterator('output'):
output_type = _get_text(output_e,'output_type')
output_text = _get_text(output_e,'text')
- output_png = _get_text(output_e,'png')
+ output_png = _get_binary(output_e,'png')
output_svg = _get_text(output_e,'svg')
output_html = _get_text(output_e,'html')
output_latex = _get_text(output_e,'latex')
@@ -103,6 +120,7 @@ def writes(self, nb, **kwargs):
nb_e = ET.Element('notebook')
_set_text(nb,'name',nb_e,'name')
_set_text(nb,'id',nb_e,'id')
+ _set_int(nb,'nbformat',nb_e,'nbformat')
wss_e = ET.SubElement(nb_e,'worksheets')
for ws in nb.worksheets:
ws_e = ET.SubElement(wss_e, 'worksheet')
@@ -120,7 +138,7 @@ def writes(self, nb, **kwargs):
output_e = ET.SubElement(outputs_e, 'output')
_set_text(output,'output_type',output_e,'output_type')
_set_text(output,'text',output_e,'text')
- _set_text(output,'png',output_e,'png')
+ _set_binary(output,'png',output_e,'png')
_set_text(output,'html',output_e,'html')
_set_text(output,'svg',output_e,'svg')
_set_text(output,'latex',output_e,'latex')
@@ -141,5 +159,7 @@ def writes(self, nb, **kwargs):
reads = _reader.reads
read = _reader.read
+to_notebook = _reader.to_notebook
write = _writer.write
writes = _writer.writes
+
View
26 IPython/nbformat/rwbase.py → IPython/nbformat/v2/rwbase.py
@@ -1,23 +1,23 @@
from base64 import encodestring, decodestring
-
+import pprint
def base64_decode(nb):
"""Base64 encode all bytes objects in the notebook."""
- for ws in nb['worksheets']:
- for cell in ws['cells']:
- if cell['cell_type'] == 'code':
- if cell.get('image/png',''):
- cell['image/png'] = bytes(decodestring(cell['image/png']))
+ for ws in nb.worksheets:
+ for cell in ws.cells:
+ if cell.cell_type == 'code':
+ if 'png' in cell:
+ cell.png = bytes(decodestring(cell.png))
return nb
def base64_encode(nb):
"""Base64 decode all binary objects in the notebook."""
- for ws in nb['worksheets']:
- for cell in ws['cells']:
- if cell['cell_type'] == 'code':
- if cell.get('image/png',''):
- cell['image/png'] = unicode(encodestring(cell['image/png']))
+ for ws in nb.worksheets:
+ for cell in ws.cells:
+ if cell.cell_type == 'code':
+ if 'png' in cell:
+ cell.png = unicode(encodestring(cell.png))
return nb
@@ -29,7 +29,7 @@ def reads(self, s, **kwargs):
def read(self, fp, **kwargs):
"""Read a notebook from a file like object"""
- return self.loads(fp.read(), **kwargs)
+ return self.read(fp.read(), **kwargs)
class NotebookWriter(object):
@@ -40,7 +40,7 @@ def writes(self, nb, **kwargs):
def write(self, nb, fp, **kwargs):
"""Write a notebook to a file like object"""
- return fp.write(self.dumps(nb,**kwargs))
+ return fp.write(self.writes(nb,**kwargs))
View
0  IPython/nbformat/v2/tests/__init__.py
No changes.
View
10 IPython/nbformat/tests/nbexamples.py → IPython/nbformat/v2/tests/nbexamples.py
@@ -1,4 +1,4 @@
-from IPython.nbformat.nbbase import (
+from ..nbbase import (
NotebookNode,
new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
)
@@ -51,17 +51,23 @@
worksheets=[ws, new_worksheet(name='worksheet2')]
)
-nb0_py = """# <codecell>
+nb0_py = """# <nbformat>2</nbformat>
+
+# <codecell>
import numpy
+# </codecell>
# <codecell>
a = numpy.random.rand(100)
+# </codecell>
# <codecell>
print a
+
+# </codecell>
"""
View
15 IPython/nbformat/v2/tests/test_json.py
@@ -0,0 +1,15 @@
+
+from unittest import TestCase
+
+from ..nbjson import reads, writes
+from .nbexamples import nb0
+
+
+class TestJSON(TestCase):
+
+ def test_roundtrip(self):
+ s = writes(nb0)
+ self.assertEquals(reads(s),nb0)
+
+
+
View
6 IPython/nbformat/tests/test_nbbase.py → IPython/nbformat/v2/tests/test_nbbase.py
@@ -1,6 +1,6 @@
from unittest import TestCase
-from IPython.nbformat.nbbase import (
+from ..nbbase import (
NotebookNode,
new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
)
@@ -53,10 +53,12 @@ def test_empty_notebook(self):
self.assertEquals('id' in nb, True)
self.assertEquals(nb.worksheets, [])
self.assertEquals('name' not in nb, True)
+ self.assertEquals(nb.nbformat,2)
- def test_notebooke(self):
+ def test_notebook(self):
worksheets = [new_worksheet(),new_worksheet()]
nb = new_notebook(name='foo',worksheets=worksheets)
self.assertEquals(nb.name,u'foo')
self.assertEquals(nb.worksheets,worksheets)
+ self.assertEquals(nb.nbformat,2)
View
6 IPython/nbformat/tests/test_nbpy.py → IPython/nbformat/v2/tests/test_nbpy.py
@@ -1,12 +1,12 @@
from unittest import TestCase
-from IPython.nbformat.nbbase import (
+from ..nbbase import (
NotebookNode,
new_code_cell, new_text_cell, new_worksheet, new_notebook
)
-from IPython.nbformat.nbpy import reads, writes
-from IPython.nbformat.tests.nbexamples import nb0, nb0_py
+from ..nbpy import reads, writes
+from .nbexamples import nb0, nb0_py
class TestPy(TestCase):
View
4 IPython/nbformat/tests/test_xml.py → IPython/nbformat/v2/tests/test_xml.py
@@ -1,7 +1,7 @@
from unittest import TestCase
-from IPython.nbformat.nbxml import reads, writes
-from IPython.nbformat.tests.nbexamples import nb0
+from ..nbxml import reads, writes
+from .nbexamples import nb0
import pprint
class TestXML(TestCase):
Please sign in to comment.
Something went wrong with that request. Please try again.