From bf02094f4798af8099b3f006d92982b43ad00525 2014-11-01 23:40:46 From: MinRK Date: 2014-11-01 23:40:46 Subject: [PATCH] copy nbformat.v3 to v4 --- diff --git a/IPython/nbformat/v4/__init__.py b/IPython/nbformat/v4/__init__.py new file mode 100644 index 0000000..59269cc --- /dev/null +++ b/IPython/nbformat/v4/__init__.py @@ -0,0 +1,74 @@ +"""The main API for the v3 notebook format. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from .nbbase import ( + NotebookNode, + new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, + new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor, + nbformat_schema +) + +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 .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 downgrade, upgrade + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def parse_filename(fname): + """Parse a notebook filename. + + This function takes a notebook filename and returns the notebook + format (json/py) and the notebook name. This logic can be + summarized as follows: + + * notebook.ipynb -> (notebook.ipynb, notebook, json) + * notebook.json -> (notebook.json, notebook, json) + * notebook.py -> (notebook.py, notebook, py) + * notebook -> (notebook.ipynb, notebook, json) + + Parameters + ---------- + fname : unicode + The notebook filename. The filename can use a specific filename + extention (.ipynb, .json, .py) or none, in which case .ipynb will + be assumed. + + Returns + ------- + (fname, name, format) : (unicode, unicode, unicode) + The filename, notebook name and format. + """ + if fname.endswith(u'.ipynb'): + format = u'json' + elif fname.endswith(u'.json'): + format = u'json' + elif fname.endswith(u'.py'): + format = u'py' + else: + fname = fname + u'.ipynb' + format = u'json' + name = fname.split('.')[0] + return fname, name, format diff --git a/IPython/nbformat/v4/convert.py b/IPython/nbformat/v4/convert.py new file mode 100644 index 0000000..9f10bfa --- /dev/null +++ b/IPython/nbformat/v4/convert.py @@ -0,0 +1,90 @@ +"""Code for converting notebooks to and from the v2 format. + +Authors: + +* Brian Granger +* Min RK +* Jonathan Frederic +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from .nbbase import ( + new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, + nbformat, nbformat_minor +) + +from IPython.nbformat import v2 + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def upgrade(nb, from_version=2, from_minor=0): + """Convert a notebook to v3. + + Parameters + ---------- + nb : NotebookNode + The Python representation of the notebook to convert. + from_version : int + The original version of the notebook to convert. + from_minor : int + The original minor version of the notebook to convert (only relevant for v >= 3). + """ + if from_version == 2: + # Mark the original nbformat so consumers know it has been converted. + nb.nbformat = nbformat + nb.nbformat_minor = nbformat_minor + + nb.orig_nbformat = 2 + return nb + elif from_version == 3: + if from_minor != nbformat_minor: + nb.orig_nbformat_minor = from_minor + nb.nbformat_minor = nbformat_minor + return nb + else: + raise ValueError('Cannot convert a notebook directly from v%s to v3. ' \ + 'Try using the IPython.nbformat.convert module.' % from_version) + + +def heading_to_md(cell): + """turn heading cell into corresponding markdown""" + cell.cell_type = "markdown" + level = cell.pop('level', 1) + cell.source = '#'*level + ' ' + cell.source + + +def raw_to_md(cell): + """let raw passthrough as markdown""" + cell.cell_type = "markdown" + + +def downgrade(nb): + """Convert a v3 notebook to v2. + + Parameters + ---------- + nb : NotebookNode + The Python representation of the notebook to convert. + """ + if nb.nbformat != 3: + return nb + nb.nbformat = 2 + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'heading': + heading_to_md(cell) + elif cell.cell_type == 'raw': + raw_to_md(cell) + return nb \ No newline at end of file diff --git a/IPython/nbformat/v4/nbbase.py b/IPython/nbformat/v4/nbbase.py new file mode 100644 index 0000000..45b0f52 --- /dev/null +++ b/IPython/nbformat/v4/nbbase.py @@ -0,0 +1,204 @@ +"""The basic dict based notebook format. + +The Python representation of a notebook is a nested structure of +dictionary subclasses that support attribute access +(IPython.utils.ipstruct.Struct). The functions in this module are merely +helpers to build the structs in the right form. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import pprint +import uuid + +from IPython.utils.ipstruct import Struct +from IPython.utils.py3compat import cast_unicode, unicode_type + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +# Change this when incrementing the nbformat version +nbformat = 3 +nbformat_minor = 0 +nbformat_schema = 'v3.withref.json' + +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, output_text=None, output_png=None, + output_html=None, output_svg=None, output_latex=None, output_json=None, + output_javascript=None, output_jpeg=None, prompt_number=None, + ename=None, evalue=None, traceback=None, stream=None, metadata=None): + """Create a new output, to go in the ``cell.outputs`` list of a code cell. + """ + output = NotebookNode() + output.output_type = unicode_type(output_type) + + if metadata is None: + metadata = {} + if not isinstance(metadata, dict): + raise TypeError("metadata must be dict") + output.metadata = metadata + + if output_type != 'pyerr': + if output_text is not None: + output.text = cast_unicode(output_text) + if output_png is not None: + output.png = cast_unicode(output_png) + if output_jpeg is not None: + output.jpeg = cast_unicode(output_jpeg) + if output_html is not None: + output.html = cast_unicode(output_html) + if output_svg is not None: + output.svg = cast_unicode(output_svg) + if output_latex is not None: + output.latex = cast_unicode(output_latex) + if output_json is not None: + output.json = cast_unicode(output_json) + if output_javascript is not None: + output.javascript = cast_unicode(output_javascript) + + if output_type == u'pyout': + if prompt_number is not None: + output.prompt_number = int(prompt_number) + + if output_type == u'pyerr': + if ename is not None: + output.ename = cast_unicode(ename) + if evalue is not None: + output.evalue = cast_unicode(evalue) + if traceback is not None: + output.traceback = [cast_unicode(frame) for frame in list(traceback)] + + if output_type == u'stream': + output.stream = 'stdout' if stream is None else cast_unicode(stream) + + return output + + +def new_code_cell(input=None, prompt_number=None, outputs=None, + language=u'python', collapsed=False, metadata=None): + """Create a new code cell with input and output""" + cell = NotebookNode() + cell.cell_type = u'code' + if language is not None: + cell.language = cast_unicode(language) + if input is not None: + cell.input = cast_unicode(input) + if prompt_number is not None: + cell.prompt_number = int(prompt_number) + if outputs is None: + cell.outputs = [] + else: + cell.outputs = outputs + if collapsed is not None: + cell.collapsed = bool(collapsed) + cell.metadata = NotebookNode(metadata or {}) + + return cell + +def new_text_cell(cell_type, source=None, rendered=None, metadata=None): + """Create a new text cell.""" + cell = NotebookNode() + # VERSIONHACK: plaintext -> raw + # handle never-released plaintext name for raw cells + if cell_type == 'plaintext': + cell_type = 'raw' + if source is not None: + cell.source = cast_unicode(source) + if rendered is not None: + cell.rendered = cast_unicode(rendered) + cell.metadata = NotebookNode(metadata or {}) + cell.cell_type = cell_type + return cell + + +def new_heading_cell(source=None, rendered=None, level=1, metadata=None): + """Create a new section cell with a given integer level.""" + cell = NotebookNode() + cell.cell_type = u'heading' + if source is not None: + cell.source = cast_unicode(source) + if rendered is not None: + cell.rendered = cast_unicode(rendered) + cell.level = int(level) + cell.metadata = NotebookNode(metadata or {}) + return cell + + +def new_worksheet(name=None, cells=None, metadata=None): + """Create a worksheet by name with with a list of cells.""" + ws = NotebookNode() + if name is not None: + ws.name = cast_unicode(name) + if cells is None: + ws.cells = [] + else: + ws.cells = list(cells) + ws.metadata = NotebookNode(metadata or {}) + return ws + + +def new_notebook(name=None, metadata=None, worksheets=None): + """Create a notebook by name, id and a list of worksheets.""" + nb = NotebookNode() + nb.nbformat = nbformat + nb.nbformat_minor = nbformat_minor + if worksheets is None: + nb.worksheets = [] + else: + nb.worksheets = list(worksheets) + if metadata is None: + nb.metadata = new_metadata() + else: + nb.metadata = NotebookNode(metadata) + if name is not None: + nb.metadata.name = cast_unicode(name) + return nb + + +def new_metadata(name=None, authors=None, license=None, created=None, + modified=None, gistid=None): + """Create a new metadata node.""" + metadata = NotebookNode() + if name is not None: + metadata.name = cast_unicode(name) + if authors is not None: + metadata.authors = list(authors) + if created is not None: + metadata.created = cast_unicode(created) + if modified is not None: + metadata.modified = cast_unicode(modified) + if license is not None: + metadata.license = cast_unicode(license) + if gistid is not None: + metadata.gistid = cast_unicode(gistid) + return metadata + +def new_author(name=None, email=None, affiliation=None, url=None): + """Create a new author.""" + author = NotebookNode() + if name is not None: + author.name = cast_unicode(name) + if email is not None: + author.email = cast_unicode(email) + if affiliation is not None: + author.affiliation = cast_unicode(affiliation) + if url is not None: + author.url = cast_unicode(url) + return author diff --git a/IPython/nbformat/v4/nbjson.py b/IPython/nbformat/v4/nbjson.py new file mode 100644 index 0000000..ccae4e8 --- /dev/null +++ b/IPython/nbformat/v4/nbjson.py @@ -0,0 +1,71 @@ +"""Read and write notebooks in JSON format. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import copy +import json + +from .nbbase import from_dict +from .rwbase import ( + NotebookReader, NotebookWriter, restore_bytes, rejoin_lines, split_lines +) + +from IPython.utils import py3compat + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +class BytesEncoder(json.JSONEncoder): + """A JSON encoder that accepts b64 (and other *ascii*) bytestrings.""" + def default(self, obj): + if isinstance(obj, bytes): + return obj.decode('ascii') + return json.JSONEncoder.default(self, obj) + + +class JSONReader(NotebookReader): + + def reads(self, s, **kwargs): + nb = json.loads(s, **kwargs) + nb = self.to_notebook(nb, **kwargs) + return nb + + def to_notebook(self, d, **kwargs): + return rejoin_lines(from_dict(d)) + + +class JSONWriter(NotebookWriter): + + def writes(self, nb, **kwargs): + kwargs['cls'] = BytesEncoder + kwargs['indent'] = 1 + kwargs['sort_keys'] = True + kwargs['separators'] = (',',': ') + if kwargs.pop('split_lines', True): + nb = split_lines(copy.deepcopy(nb)) + return py3compat.str_to_unicode(json.dumps(nb, **kwargs), 'utf-8') + + +_reader = JSONReader() +_writer = JSONWriter() + +reads = _reader.reads +read = _reader.read +to_notebook = _reader.to_notebook +write = _writer.write +writes = _writer.writes diff --git a/IPython/nbformat/v4/rwbase.py b/IPython/nbformat/v4/rwbase.py new file mode 100644 index 0000000..eefb8e0 --- /dev/null +++ b/IPython/nbformat/v4/rwbase.py @@ -0,0 +1,188 @@ +"""Base classes and utilities for readers and writers. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from base64 import encodestring, decodestring +import pprint + +from IPython.utils import py3compat +from IPython.utils.py3compat import str_to_bytes, unicode_type, string_types + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def restore_bytes(nb): + """Restore bytes of image data from unicode-only formats. + + Base64 encoding is handled elsewhere. Bytes objects in the notebook are + always b64-encoded. We DO NOT encode/decode around file formats. + + Note: this is never used + """ + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'code': + for output in cell.outputs: + if 'png' in output: + output.png = str_to_bytes(output.png, 'ascii') + if 'jpeg' in output: + output.jpeg = str_to_bytes(output.jpeg, 'ascii') + return nb + +# output keys that are likely to have multiline values +_multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json'] + + +# FIXME: workaround for old splitlines() +def _join_lines(lines): + """join lines that have been written by splitlines() + + Has logic to protect against `splitlines()`, which + should have been `splitlines(True)` + """ + if lines and lines[0].endswith(('\n', '\r')): + # created by splitlines(True) + return u''.join(lines) + else: + # created by splitlines() + return u'\n'.join(lines) + + +def rejoin_lines(nb): + """rejoin multiline text into strings + + For reversing effects of ``split_lines(nb)``. + + This only rejoins lines that have been split, so if text objects were not split + they will pass through unchanged. + + Used when reading JSON files that may have been passed through split_lines. + """ + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'code': + if 'input' in cell and isinstance(cell.input, list): + cell.input = _join_lines(cell.input) + for output in cell.outputs: + for key in _multiline_outputs: + item = output.get(key, None) + if isinstance(item, list): + output[key] = _join_lines(item) + else: # text, heading cell + for key in ['source', 'rendered']: + item = cell.get(key, None) + if isinstance(item, list): + cell[key] = _join_lines(item) + return nb + + +def split_lines(nb): + """split likely multiline text into lists of strings + + For file output more friendly to line-based VCS. ``rejoin_lines(nb)`` will + reverse the effects of ``split_lines(nb)``. + + Used when writing JSON files. + """ + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'code': + if 'input' in cell and isinstance(cell.input, string_types): + cell.input = cell.input.splitlines(True) + for output in cell.outputs: + for key in _multiline_outputs: + item = output.get(key, None) + if isinstance(item, string_types): + output[key] = item.splitlines(True) + else: # text, heading cell + for key in ['source', 'rendered']: + item = cell.get(key, None) + if isinstance(item, string_types): + cell[key] = item.splitlines(True) + return nb + +# b64 encode/decode are never actually used, because all bytes objects in +# the notebook are already b64-encoded, and we don't need/want to double-encode + +def base64_decode(nb): + """Restore all bytes objects in the notebook from base64-encoded strings. + + Note: This is never used + """ + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'code': + for output in cell.outputs: + if 'png' in output: + if isinstance(output.png, unicode_type): + output.png = output.png.encode('ascii') + output.png = decodestring(output.png) + if 'jpeg' in output: + if isinstance(output.jpeg, unicode_type): + output.jpeg = output.jpeg.encode('ascii') + output.jpeg = decodestring(output.jpeg) + return nb + + +def base64_encode(nb): + """Base64 encode all bytes objects in the notebook. + + These will be b64-encoded unicode strings + + Note: This is never used + """ + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == 'code': + for output in cell.outputs: + if 'png' in output: + output.png = encodestring(output.png).decode('ascii') + if 'jpeg' in output: + output.jpeg = encodestring(output.jpeg).decode('ascii') + return nb + + +class NotebookReader(object): + """A class for reading notebooks.""" + + 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""" + nbs = fp.read() + if not py3compat.PY3 and not isinstance(nbs, unicode_type): + nbs = py3compat.str_to_unicode(nbs) + return self.reads(nbs, **kwargs) + + +class NotebookWriter(object): + """A class for writing notebooks.""" + + 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""" + nbs = self.writes(nb,**kwargs) + if not py3compat.PY3 and not isinstance(nbs, unicode_type): + # this branch is likely only taken for JSON on Python 2 + nbs = py3compat.str_to_unicode(nbs) + return fp.write(nbs) diff --git a/IPython/nbformat/v4/tests/__init__.py b/IPython/nbformat/v4/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/nbformat/v4/tests/__init__.py diff --git a/IPython/nbformat/v4/tests/formattest.py b/IPython/nbformat/v4/tests/formattest.py new file mode 100644 index 0000000..69cdba3 --- /dev/null +++ b/IPython/nbformat/v4/tests/formattest.py @@ -0,0 +1,60 @@ +# -*- coding: utf8 -*- +import io +import os +import shutil +import tempfile + +pjoin = os.path.join + +from ..nbbase import ( + NotebookNode, + new_code_cell, new_text_cell, new_worksheet, new_notebook +) + +from ..nbpy import reads, writes, read, write +from .nbexamples import nb0, nb0_py + + +def open_utf8(fname, mode): + return io.open(fname, mode=mode, encoding='utf-8') + +class NBFormatTest: + """Mixin for writing notebook format tests""" + + # override with appropriate values in subclasses + nb0_ref = None + ext = None + mod = None + + def setUp(self): + self.wd = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.wd) + + def assertNBEquals(self, nba, nbb): + self.assertEqual(nba, nbb) + + def test_writes(self): + s = self.mod.writes(nb0) + if self.nb0_ref: + self.assertEqual(s, self.nb0_ref) + + def test_reads(self): + s = self.mod.writes(nb0) + nb = self.mod.reads(s) + + def test_roundtrip(self): + s = self.mod.writes(nb0) + self.assertNBEquals(self.mod.reads(s),nb0) + + def test_write_file(self): + with open_utf8(pjoin(self.wd, "nb0.%s" % self.ext), 'w') as f: + self.mod.write(nb0, f) + + def test_read_file(self): + with open_utf8(pjoin(self.wd, "nb0.%s" % self.ext), 'w') as f: + self.mod.write(nb0, f) + + with open_utf8(pjoin(self.wd, "nb0.%s" % self.ext), 'r') as f: + nb = self.mod.read(f) diff --git a/IPython/nbformat/v4/tests/nbexamples.py b/IPython/nbformat/v4/tests/nbexamples.py new file mode 100644 index 0000000..36539ec --- /dev/null +++ b/IPython/nbformat/v4/tests/nbexamples.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +import os +from base64 import encodestring + +from ..nbbase import ( + NotebookNode, + new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, + new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor +) + +# some random base64-encoded *text* +png = encodestring(os.urandom(5)).decode('ascii') +jpeg = encodestring(os.urandom(6)).decode('ascii') + +ws = new_worksheet(name='worksheet1') + +ws.cells.append(new_text_cell( + u'html', + source='Some NumPy Examples', + rendered='Some NumPy Examples' +)) + + +ws.cells.append(new_code_cell( + input='import numpy', + prompt_number=1, + collapsed=False +)) + +ws.cells.append(new_text_cell( + u'markdown', + source='A random array', + rendered='A random array' +)) + +ws.cells.append(new_text_cell( + u'raw', + source='A random array', +)) + +ws.cells.append(new_heading_cell( + u'My Heading', + level=2 +)) + +ws.cells.append(new_code_cell( + input='a = numpy.random.rand(100)', + prompt_number=2, + collapsed=True +)) +ws.cells.append(new_code_cell( + input='a = 10\nb = 5\n', + prompt_number=3, +)) +ws.cells.append(new_code_cell( + input='a = 10\nb = 5', + prompt_number=4, +)) + +ws.cells.append(new_code_cell( + input=u'print "ünîcødé"', + prompt_number=3, + collapsed=False, + outputs=[new_output( + output_type=u'pyout', + output_text=u'', + output_html=u'The HTML rep', + output_latex=u'$a$', + output_png=png, + output_jpeg=jpeg, + output_svg=u'', + output_json=u'json data', + output_javascript=u'var i=0;', + prompt_number=3 + ),new_output( + output_type=u'display_data', + output_text=u'', + output_html=u'The HTML rep', + output_latex=u'$a$', + output_png=png, + output_jpeg=jpeg, + output_svg=u'', + output_json=u'json data', + output_javascript=u'var i=0;' + ),new_output( + output_type=u'pyerr', + ename=u'NameError', + evalue=u'NameError was here', + traceback=[u'frame 0', u'frame 1', u'frame 2'] + ),new_output( + output_type=u'stream', + output_text='foo\rbar\r\n' + ),new_output( + output_type=u'stream', + stream='stderr', + output_text='\rfoo\rbar\n' + )] +)) + +authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com', + affiliation=u'Fox',url=u'http://www.fox.com')] +md = new_metadata(name=u'My Notebook',license=u'BSD',created=u'8601_goes_here', + modified=u'8601_goes_here',gistid=u'21341231',authors=authors) + +nb0 = new_notebook( + worksheets=[ws, new_worksheet(name='worksheet2')], + metadata=md +) + +nb0_py = u"""# -*- coding: utf-8 -*- +# %i.%i + +# + +# Some NumPy Examples + +# + +import numpy + +# + +# A random array + +# + +# A random array + +# + +# My Heading + +# + +a = numpy.random.rand(100) + +# + +a = 10 +b = 5 + +# + +a = 10 +b = 5 + +# + +print "ünîcødé" + +""" % (nbformat, nbformat_minor) diff --git a/IPython/nbformat/v4/tests/test_json.py b/IPython/nbformat/v4/tests/test_json.py new file mode 100644 index 0000000..01cf3c4 --- /dev/null +++ b/IPython/nbformat/v4/tests/test_json.py @@ -0,0 +1,68 @@ +import pprint +from base64 import decodestring +from unittest import TestCase + +from IPython.utils.py3compat import unicode_type +from ..nbjson import reads, writes +from .. import nbjson +from .nbexamples import nb0 + +from . import formattest + +from .nbexamples import nb0 + + +class TestJSON(formattest.NBFormatTest, TestCase): + + nb0_ref = None + ext = 'ipynb' + mod = nbjson + + def test_roundtrip_nosplit(self): + """Ensure that multiline blobs are still readable""" + # ensures that notebooks written prior to splitlines change + # are still readable. + s = writes(nb0, split_lines=False) + self.assertEqual(nbjson.reads(s),nb0) + + def test_roundtrip_split(self): + """Ensure that splitting multiline blocks is safe""" + # This won't differ from test_roundtrip unless the default changes + s = writes(nb0, split_lines=True) + self.assertEqual(nbjson.reads(s),nb0) + + def test_read_png(self): + """PNG output data is b64 unicode""" + s = writes(nb0) + nb1 = nbjson.reads(s) + found_png = False + for cell in nb1.worksheets[0].cells: + if not 'outputs' in cell: + continue + for output in cell.outputs: + if 'png' in output: + found_png = True + pngdata = output['png'] + self.assertEqual(type(pngdata), unicode_type) + # test that it is valid b64 data + b64bytes = pngdata.encode('ascii') + raw_bytes = decodestring(b64bytes) + assert found_png, "never found png output" + + def test_read_jpeg(self): + """JPEG output data is b64 unicode""" + s = writes(nb0) + nb1 = nbjson.reads(s) + found_jpeg = False + for cell in nb1.worksheets[0].cells: + if not 'outputs' in cell: + continue + for output in cell.outputs: + if 'jpeg' in output: + found_jpeg = True + jpegdata = output['jpeg'] + self.assertEqual(type(jpegdata), unicode_type) + # test that it is valid b64 data + b64bytes = jpegdata.encode('ascii') + raw_bytes = decodestring(b64bytes) + assert found_jpeg, "never found jpeg output" diff --git a/IPython/nbformat/v4/tests/test_nbbase.py b/IPython/nbformat/v4/tests/test_nbbase.py new file mode 100644 index 0000000..2625fab --- /dev/null +++ b/IPython/nbformat/v4/tests/test_nbbase.py @@ -0,0 +1,155 @@ +from unittest import TestCase + +from ..nbbase import ( + NotebookNode, + new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, + new_author, new_metadata, new_heading_cell, nbformat +) + +class TestCell(TestCase): + + def test_empty_code_cell(self): + cc = new_code_cell() + self.assertEqual(cc.cell_type,u'code') + self.assertEqual(u'input' not in cc, True) + self.assertEqual(u'prompt_number' not in cc, True) + self.assertEqual(cc.outputs, []) + self.assertEqual(cc.collapsed, False) + + def test_code_cell(self): + cc = new_code_cell(input='a=10', prompt_number=0, collapsed=True) + cc.outputs = [new_output(output_type=u'pyout', + output_svg=u'foo',output_text=u'10',prompt_number=0)] + self.assertEqual(cc.input, u'a=10') + self.assertEqual(cc.prompt_number, 0) + self.assertEqual(cc.language, u'python') + self.assertEqual(cc.outputs[0].svg, u'foo') + self.assertEqual(cc.outputs[0].text, u'10') + self.assertEqual(cc.outputs[0].prompt_number, 0) + self.assertEqual(cc.collapsed, True) + + def test_pyerr(self): + o = new_output(output_type=u'pyerr', ename=u'NameError', + evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2'] + ) + self.assertEqual(o.output_type, u'pyerr') + self.assertEqual(o.ename, u'NameError') + self.assertEqual(o.evalue, u'Name not found') + self.assertEqual(o.traceback, [u'frame 0', u'frame 1', u'frame 2']) + + def test_empty_html_cell(self): + tc = new_text_cell(u'html') + self.assertEqual(tc.cell_type, u'html') + self.assertEqual(u'source' not in tc, True) + self.assertEqual(u'rendered' not in tc, True) + + def test_html_cell(self): + tc = new_text_cell(u'html', 'hi', 'hi') + self.assertEqual(tc.source, u'hi') + self.assertEqual(tc.rendered, u'hi') + + def test_empty_markdown_cell(self): + tc = new_text_cell(u'markdown') + self.assertEqual(tc.cell_type, u'markdown') + self.assertEqual(u'source' not in tc, True) + self.assertEqual(u'rendered' not in tc, True) + + def test_markdown_cell(self): + tc = new_text_cell(u'markdown', 'hi', 'hi') + self.assertEqual(tc.source, u'hi') + self.assertEqual(tc.rendered, u'hi') + + def test_empty_raw_cell(self): + tc = new_text_cell(u'raw') + self.assertEqual(tc.cell_type, u'raw') + self.assertEqual(u'source' not in tc, True) + self.assertEqual(u'rendered' not in tc, True) + + def test_raw_cell(self): + tc = new_text_cell(u'raw', 'hi', 'hi') + self.assertEqual(tc.source, u'hi') + self.assertEqual(tc.rendered, u'hi') + + def test_empty_heading_cell(self): + tc = new_heading_cell() + self.assertEqual(tc.cell_type, u'heading') + self.assertEqual(u'source' not in tc, True) + self.assertEqual(u'rendered' not in tc, True) + + def test_heading_cell(self): + tc = new_heading_cell(u'hi', u'hi', level=2) + self.assertEqual(tc.source, u'hi') + self.assertEqual(tc.rendered, u'hi') + self.assertEqual(tc.level, 2) + + +class TestWorksheet(TestCase): + + def test_empty_worksheet(self): + ws = new_worksheet() + self.assertEqual(ws.cells,[]) + self.assertEqual(u'name' not in ws, True) + + def test_worksheet(self): + cells = [new_code_cell(), new_text_cell(u'html')] + ws = new_worksheet(cells=cells,name=u'foo') + self.assertEqual(ws.cells,cells) + self.assertEqual(ws.name,u'foo') + +class TestNotebook(TestCase): + + def test_empty_notebook(self): + nb = new_notebook() + self.assertEqual(nb.worksheets, []) + self.assertEqual(nb.metadata, NotebookNode()) + self.assertEqual(nb.nbformat,nbformat) + + def test_notebook(self): + worksheets = [new_worksheet(),new_worksheet()] + metadata = new_metadata(name=u'foo') + nb = new_notebook(metadata=metadata,worksheets=worksheets) + self.assertEqual(nb.metadata.name,u'foo') + self.assertEqual(nb.worksheets,worksheets) + self.assertEqual(nb.nbformat,nbformat) + + def test_notebook_name(self): + worksheets = [new_worksheet(),new_worksheet()] + nb = new_notebook(name='foo',worksheets=worksheets) + self.assertEqual(nb.metadata.name,u'foo') + self.assertEqual(nb.worksheets,worksheets) + self.assertEqual(nb.nbformat,nbformat) + +class TestMetadata(TestCase): + + def test_empty_metadata(self): + md = new_metadata() + self.assertEqual(u'name' not in md, True) + self.assertEqual(u'authors' not in md, True) + self.assertEqual(u'license' not in md, True) + self.assertEqual(u'saved' not in md, True) + self.assertEqual(u'modified' not in md, True) + self.assertEqual(u'gistid' not in md, True) + + def test_metadata(self): + authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com')] + md = new_metadata(name=u'foo',license=u'BSD',created=u'today', + modified=u'now',gistid=u'21341231',authors=authors) + self.assertEqual(md.name, u'foo') + self.assertEqual(md.license, u'BSD') + self.assertEqual(md.created, u'today') + self.assertEqual(md.modified, u'now') + self.assertEqual(md.gistid, u'21341231') + self.assertEqual(md.authors, authors) + +class TestOutputs(TestCase): + def test_binary_png(self): + out = new_output(output_png=b'\x89PNG\r\n\x1a\n', output_type='display_data') + + def test_b64b6tes_png(self): + out = new_output(output_png=b'iVBORw0KG', output_type='display_data') + + def test_binary_jpeg(self): + out = new_output(output_jpeg=b'\xff\xd8', output_type='display_data') + + def test_b64b6tes_jpeg(self): + out = new_output(output_jpeg=b'/9', output_type='display_data')