From 069f64e8b66a36ae87130d30bccdd2f1f06a21e7 2012-01-30 23:01:08 From: Brian E. Granger Date: 2012-01-30 23:01:08 Subject: [PATCH] Merge pull request #1331 from ellisonbg/celltypes Added plaintext and heading cells to the notebook UI and nbformat. In the process we have updated the nbformat to v3 and integrated these new cell types into the new toolbar. --- diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js index 71d0670..e02a618 100644 --- a/IPython/frontend/html/notebook/static/js/codecell.js +++ b/IPython/frontend/html/notebook/static/js/codecell.js @@ -824,6 +824,8 @@ var IPython = (function (IPython) { if (data.collapsed !== undefined) { if (data.collapsed) { this.collapse(); + } else { + this.expand(); }; }; }; diff --git a/IPython/frontend/html/notebook/static/js/menubar.js b/IPython/frontend/html/notebook/static/js/menubar.js index 5ddf5f0..b513ecd 100644 --- a/IPython/frontend/html/notebook/static/js/menubar.js +++ b/IPython/frontend/html/notebook/static/js/menubar.js @@ -129,6 +129,27 @@ var IPython = (function (IPython) { this.element.find('#to_markdown').click(function () { IPython.notebook.to_markdown(); }); + this.element.find('#to_plaintext').click(function () { + IPython.notebook.to_plaintext(); + }); + this.element.find('#to_heading1').click(function () { + IPython.notebook.to_heading(undefined, 1); + }); + this.element.find('#to_heading2').click(function () { + IPython.notebook.to_heading(undefined, 2); + }); + this.element.find('#to_heading3').click(function () { + IPython.notebook.to_heading(undefined, 3); + }); + this.element.find('#to_heading4').click(function () { + IPython.notebook.to_heading(undefined, 4); + }); + this.element.find('#to_heading5').click(function () { + IPython.notebook.to_heading(undefined, 5); + }); + this.element.find('#to_heading6').click(function () { + IPython.notebook.to_heading(undefined, 6); + }); this.element.find('#toggle_output').click(function () { IPython.notebook.toggle_output(); }); diff --git a/IPython/frontend/html/notebook/static/js/notebook.js b/IPython/frontend/html/notebook/static/js/notebook.js index c69a758..df20219 100644 --- a/IPython/frontend/html/notebook/static/js/notebook.js +++ b/IPython/frontend/html/notebook/static/js/notebook.js @@ -136,7 +136,42 @@ var IPython = (function (IPython) { that.control_key_active = false; return false; } else if (event.which === 84 && that.control_key_active) { - // Toggle output = t + // To Plaintext = t + that.to_plaintext(); + that.control_key_active = false; + return false; + } else if (event.which === 49 && that.control_key_active) { + // To Heading 1 = 1 + that.to_heading(undefined, 1); + that.control_key_active = false; + return false; + } else if (event.which === 50 && that.control_key_active) { + // To Heading 2 = 2 + that.to_heading(undefined, 2); + that.control_key_active = false; + return false; + } else if (event.which === 51 && that.control_key_active) { + // To Heading 3 = 3 + that.to_heading(undefined, 3); + that.control_key_active = false; + return false; + } else if (event.which === 52 && that.control_key_active) { + // To Heading 4 = 4 + that.to_heading(undefined, 4); + that.control_key_active = false; + return false; + } else if (event.which === 53 && that.control_key_active) { + // To Heading 5 = 5 + that.to_heading(undefined, 5); + that.control_key_active = false; + return false; + } else if (event.which === 54 && that.control_key_active) { + // To Heading 6 = 6 + that.to_heading(undefined, 6); + that.control_key_active = false; + return false; + } else if (event.which === 79 && that.control_key_active) { + // Toggle output = o that.toggle_output(); that.control_key_active = false; return false; @@ -366,7 +401,11 @@ var IPython = (function (IPython) { }; var cell = this.get_cell(index) cell.select(); - IPython.toolbar.set_cell_type(cell.cell_type); + if (cell.cell_type === 'heading') { + IPython.toolbar.set_cell_type(cell.cell_type+cell.level); + } else { + IPython.toolbar.set_cell_type(cell.cell_type) + } }; return this; }; @@ -467,15 +506,19 @@ var IPython = (function (IPython) { // type = ('code','html','markdown') // index = cell index or undefined to insert below selected index = this.index_or_selected(index); + var cell = null; if (this.ncells() === 0 || this.is_valid_cell_index(index)) { - var cell = null; if (type === 'code') { - var cell = new IPython.CodeCell(this); + cell = new IPython.CodeCell(this); cell.set_input_prompt(); } else if (type === 'markdown') { - var cell = new IPython.MarkdownCell(this); + cell = new IPython.MarkdownCell(this); } else if (type === 'html') { - var cell = new IPython.HTMLCell(this); + cell = new IPython.HTMLCell(this); + } else if (type === 'plaintext') { + cell = new IPython.PlaintextCell(this); + } else if (type === 'heading') { + cell = new IPython.HeadingCell(this); }; if (cell !== null) { if (this.ncells() === 0) { @@ -489,6 +532,7 @@ var IPython = (function (IPython) { return cell; }; }; + return cell; }; @@ -496,15 +540,19 @@ var IPython = (function (IPython) { // type = ('code','html','markdown') // index = cell index or undefined to insert above selected index = this.index_or_selected(index); + var cell = null; if (this.ncells() === 0 || this.is_valid_cell_index(index)) { - var cell = null; if (type === 'code') { - var cell = new IPython.CodeCell(this); + cell = new IPython.CodeCell(this); cell.set_input_prompt(); } else if (type === 'markdown') { - var cell = new IPython.MarkdownCell(this); + cell = new IPython.MarkdownCell(this); } else if (type === 'html') { - var cell = new IPython.HTMLCell(this); + cell = new IPython.HTMLCell(this); + } else if (type === 'plaintext') { + cell = new IPython.PlaintextCell(this); + } else if (type === 'heading') { + cell = new IPython.HeadingCell(this); }; if (cell !== null) { if (this.ncells() === 0) { @@ -518,6 +566,7 @@ var IPython = (function (IPython) { return cell; }; }; + return cell; }; @@ -534,8 +583,8 @@ var IPython = (function (IPython) { } target_cell.set_text(text); source_element.remove(); + this.dirty = true; }; - this.dirty = true; }; }; @@ -545,19 +594,16 @@ var IPython = (function (IPython) { if (this.is_valid_cell_index(i)) { var source_element = this.get_cell_element(i); var source_cell = source_element.data("cell"); - var target_cell = null; if (!(source_cell instanceof IPython.MarkdownCell)) { target_cell = this.insert_cell_below('markdown',i); var text = source_cell.get_text(); if (text === source_cell.placeholder) { text = ''; }; - if (target_cell !== null) { - // The edit must come before the set_text. - target_cell.edit(); - target_cell.set_text(text); - source_element.remove(); - } + // The edit must come before the set_text. + target_cell.edit(); + target_cell.set_text(text); + source_element.remove(); this.dirty = true; }; }; @@ -576,14 +622,61 @@ var IPython = (function (IPython) { if (text === source_cell.placeholder) { text = ''; }; - if (target_cell !== null) { - // The edit must come before the set_text. - target_cell.edit(); - target_cell.set_text(text); - source_element.remove(); - } + // The edit must come before the set_text. + target_cell.edit(); + target_cell.set_text(text); + source_element.remove(); + this.dirty = true; + }; + }; + }; + + + Notebook.prototype.to_plaintext = function (index) { + var i = this.index_or_selected(index); + if (this.is_valid_cell_index(i)) { + var source_element = this.get_cell_element(i); + var source_cell = source_element.data("cell"); + var target_cell = null; + if (!(source_cell instanceof IPython.PlaintextCell)) { + target_cell = this.insert_cell_below('plaintext',i); + var text = source_cell.get_text(); + if (text === source_cell.placeholder) { + text = ''; + }; + // The edit must come before the set_text. + target_cell.edit(); + target_cell.set_text(text); + source_element.remove(); + this.dirty = true; + }; + }; + }; + + + Notebook.prototype.to_heading = function (index, level) { + level = level || 1; + var i = this.index_or_selected(index); + if (this.is_valid_cell_index(i)) { + var source_element = this.get_cell_element(i); + var source_cell = source_element.data("cell"); + var target_cell = null; + if (source_cell instanceof IPython.HeadingCell) { + source_cell.set_level(level); + } else { + target_cell = this.insert_cell_below('heading',i); + var text = source_cell.get_text(); + if (text === source_cell.placeholder) { + text = ''; + }; + // The edit must come before the set_text. + target_cell.set_level(level); + target_cell.edit(); + target_cell.set_text(text); + source_element.remove(); this.dirty = true; }; + IPython.toolbar.set_cell_type("heading"+level); }; }; @@ -1098,7 +1191,7 @@ var IPython = (function (IPython) { // We may want to move the name/id/nbformat logic inside toJSON? var data = this.toJSON(); data.metadata.name = nbname; - data.nbformat = 2; + data.nbformat = 3; // We do the call with settings so we can set cache to false. var settings = { processData : false, diff --git a/IPython/frontend/html/notebook/static/js/quickhelp.js b/IPython/frontend/html/notebook/static/js/quickhelp.js index 8e29541..4edd117 100644 --- a/IPython/frontend/html/notebook/static/js/quickhelp.js +++ b/IPython/frontend/html/notebook/static/js/quickhelp.js @@ -34,13 +34,15 @@ var IPython = (function (IPython) { {key: 'Ctrl-m d', help: 'delete cell'}, {key: 'Ctrl-m a', help: 'insert cell above'}, {key: 'Ctrl-m b', help: 'insert cell below'}, - {key: 'Ctrl-m t', help: 'toggle output'}, + {key: 'Ctrl-m o', help: 'toggle output'}, {key: 'Ctrl-m l', help: 'toggle line numbers'}, {key: 'Ctrl-m s', help: 'save notebook'}, {key: 'Ctrl-m j', help: 'move cell down'}, {key: 'Ctrl-m k', help: 'move cell up'}, {key: 'Ctrl-m y', help: 'code cell'}, {key: 'Ctrl-m m', help: 'markdown cell'}, + {key: 'Ctrl-m t', help: 'plaintext cell'}, + {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'}, {key: 'Ctrl-m p', help: 'select previous'}, {key: 'Ctrl-m n', help: 'select next'}, {key: 'Ctrl-m i', help: 'interrupt kernel'}, diff --git a/IPython/frontend/html/notebook/static/js/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js index aacfddf..1c98dea 100644 --- a/IPython/frontend/html/notebook/static/js/textcell.js +++ b/IPython/frontend/html/notebook/static/js/textcell.js @@ -237,49 +237,109 @@ var IPython = (function (IPython) { }; - // RSTCell + // PlaintextCell - var RSTCell = function (notebook) { - this.placeholder = "Type *ReStructured Text* and LaTeX: $\\alpha^2$"; + var PlaintextCell = function (notebook) { + this.placeholder = "Type plain text and LaTeX: $\\alpha^2$"; + this.code_mirror_mode = 'rst'; IPython.TextCell.apply(this, arguments); - this.cell_type = 'rst'; + this.cell_type = 'plaintext'; }; - RSTCell.prototype = new TextCell(); + PlaintextCell.prototype = new TextCell(); - RSTCell.prototype.render = function () { - if (this.rendered === false) { - var text = this.get_text(); - if (text === "") { text = this.placeholder; } - var settings = { - processData : false, - cache : false, - type : "POST", - data : text, - headers : {'Content-Type': 'application/x-rst'}, - success : $.proxy(this.handle_render,this) - }; - $.ajax("/rstservice/render", settings); - this.element.find('div.text_cell_input').hide(); - this.element.find("div.text_cell_render").show(); - this.set_rendered("Rendering..."); + PlaintextCell.prototype.render = function () { + this.rendered = true; + this.edit(); + }; + + + PlaintextCell.prototype.select = function () { + IPython.Cell.prototype.select.apply(this); + this.code_mirror.refresh(); + this.code_mirror.focus(); + }; + + + PlaintextCell.prototype.at_top = function () { + var cursor = this.code_mirror.getCursor(); + if (cursor.line === 0) { + return true; + } else { + return false; } }; - RSTCell.prototype.handle_render = function (data, status, xhr) { - this.set_rendered(data); - this.typeset(); - this.rendered = true; + PlaintextCell.prototype.at_bottom = function () { + var cursor = this.code_mirror.getCursor(); + if (cursor.line === (this.code_mirror.lineCount()-1)) { + return true; + } else { + return false; + } }; + // HTMLCell + + var HeadingCell = function (notebook) { + this.placeholder = "Type Heading Here"; + IPython.TextCell.apply(this, arguments); + this.cell_type = 'heading'; + this.level = 1; + }; + + + HeadingCell.prototype = new TextCell(); + + + HeadingCell.prototype.set_level = function (level) { + this.level = level; + if (this.rendered) { + this.rendered = false; + this.render(); + }; + }; + + + HeadingCell.prototype.get_level = function () { + return this.level; + }; + + + HeadingCell.prototype.set_rendered = function (text) { + var r = this.element.find("div.text_cell_render"); + r.empty(); + r.append($('').html(text)); + }; + + + HeadingCell.prototype.get_rendered = function () { + var r = this.element.find("div.text_cell_render"); + return r.children().first().html(); + }; + + + HeadingCell.prototype.render = function () { + if (this.rendered === false) { + var text = this.get_text(); + if (text === "") { text = this.placeholder; } + this.set_rendered(text); + this.typeset(); + this.element.find('div.text_cell_input').hide(); + this.element.find("div.text_cell_render").show(); + this.rendered = true; + }; + }; + IPython.TextCell = TextCell; IPython.HTMLCell = HTMLCell; IPython.MarkdownCell = MarkdownCell; - IPython.RSTCell = RSTCell; + IPython.PlaintextCell = PlaintextCell; + IPython.HeadingCell = HeadingCell; return IPython; diff --git a/IPython/frontend/html/notebook/static/js/toolbar.js b/IPython/frontend/html/notebook/static/js/toolbar.js index 501f00a..df0de25 100644 --- a/IPython/frontend/html/notebook/static/js/toolbar.js +++ b/IPython/frontend/html/notebook/static/js/toolbar.js @@ -108,6 +108,20 @@ var IPython = (function (IPython) { IPython.notebook.to_code(); } else if (cell_type === 'markdown') { IPython.notebook.to_markdown(); + } else if (cell_type === 'plaintext') { + IPython.notebook.to_plaintext(); + } else if (cell_type === 'heading1') { + IPython.notebook.to_heading(undefined, 1); + } else if (cell_type === 'heading2') { + IPython.notebook.to_heading(undefined, 2); + } else if (cell_type === 'heading3') { + IPython.notebook.to_heading(undefined, 3); + } else if (cell_type === 'heading4') { + IPython.notebook.to_heading(undefined, 4); + } else if (cell_type === 'heading5') { + IPython.notebook.to_heading(undefined, 5); + } else if (cell_type === 'heading6') { + IPython.notebook.to_heading(undefined, 6); }; }); diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html index 7e547f1..242a5d0 100644 --- a/IPython/frontend/html/notebook/templates/notebook.html +++ b/IPython/frontend/html/notebook/templates/notebook.html @@ -117,8 +117,15 @@
  • Run in Place
  • Run All

  • -
  • Code Cell
  • -
  • Markdown Cell
  • +
  • Code
  • +
  • Markdown
  • +
  • Plaintext
  • +
  • Heading 1
  • +
  • Heading 2
  • +
  • Heading 3
  • +
  • Heading 4
  • +
  • Heading 5
  • +
  • Heading 6

  • Toggle Output
  • Clear All Output
  • @@ -173,6 +180,13 @@ diff --git a/IPython/nbformat/current.py b/IPython/nbformat/current.py index ab4fa3b..a1e29dc 100644 --- a/IPython/nbformat/current.py +++ b/IPython/nbformat/current.py @@ -21,20 +21,21 @@ import json from xml.etree import ElementTree as ET import re +from IPython.nbformat import v3 from IPython.nbformat import v2 from IPython.nbformat import v1 -from IPython.nbformat.v2 import ( +from IPython.nbformat.v3 import ( NotebookNode, new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, - parse_filename, new_metadata, new_author + parse_filename, new_metadata, new_author, new_heading_cell ) #----------------------------------------------------------------------------- # Code #----------------------------------------------------------------------------- -current_nbformat = 2 +current_nbformat = 3 class NBFormatError(Exception): @@ -48,17 +49,6 @@ def parse_json(s, **kwargs): 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'# (?P\d+)' @@ -66,7 +56,7 @@ def parse_py(s, **kwargs): if m is not None: nbformat = int(m.group('nbformat')) else: - nbformat = 2 + nbformat = 3 return nbformat, s @@ -75,26 +65,19 @@ def reads_json(s, **kwargs): 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) + nb = v3.convert_to_this_nbformat(nb, orig_version=1) elif nbformat == 2: nb = v2.to_notebook_json(d, **kwargs) + nb = v3.convert_to_this_nbformat(nb, orig_version=2) + elif nbformat == 3: + nb = v3.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 + return v3.writes_json(nb, **kwargs) def reads_py(s, **kwargs): @@ -102,13 +85,15 @@ def reads_py(s, **kwargs): nbformat, s = parse_py(s, **kwargs) if nbformat == 2: nb = v2.to_notebook_py(s, **kwargs) + elif nbformat == 3: + nb = v3.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) + return v3.writes_py(nb, **kwargs) # High level API @@ -133,9 +118,7 @@ def reads(s, format, **kwargs): The notebook that was read. """ format = unicode(format) - if format == u'xml': - return reads_xml(s, **kwargs) - elif format == u'json' or format == u'ipynb': + if format == u'json' or format == u'ipynb': return reads_json(s, **kwargs) elif format == u'py': return reads_py(s, **kwargs) @@ -161,9 +144,7 @@ def writes(nb, format, **kwargs): The notebook string. """ format = unicode(format) - if format == u'xml': - raise NotImplementedError('Write to XML files is not implemented.') - elif format == u'json' or format == u'ipynb': + if format == u'json' or format == u'ipynb': return writes_json(nb, **kwargs) elif format == u'py': return writes_py(nb, **kwargs) diff --git a/IPython/nbformat/v3/__init__.py b/IPython/nbformat/v3/__init__.py new file mode 100644 index 0000000..2c86f51 --- /dev/null +++ b/IPython/nbformat/v3/__init__.py @@ -0,0 +1,74 @@ +"""The main API for the v2 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 +) + +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 convert_to_this_nbformat + +#----------------------------------------------------------------------------- +# 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/v3/convert.py b/IPython/nbformat/v3/convert.py new file mode 100644 index 0000000..8480b9d --- /dev/null +++ b/IPython/nbformat/v3/convert.py @@ -0,0 +1,48 @@ +"""Code for converting notebooks to and from the v2 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 ( + new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output +) + +from IPython.nbformat import v2 + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def convert_to_this_nbformat(nb, orig_version=2): + """Convert a notebook to the v2 format. + + Parameters + ---------- + nb : NotebookNode + The Python representation of the notebook to convert. + orig_version : int + The original version of the notebook to convert. + """ + if orig_version == 1: + nb = v2.convert_to_this_nbformat(nb) + orig_version = 2 + if orig_version == 2: + return nb + elif orig_version == 3: + return nb + else: + raise ValueError('Cannot convert a notebook from v%s to v3' % orig_version) + diff --git a/IPython/nbformat/v3/nbbase.py b/IPython/nbformat/v3/nbbase.py new file mode 100644 index 0000000..9d218eb --- /dev/null +++ b/IPython/nbformat/v3/nbbase.py @@ -0,0 +1,191 @@ +"""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. + +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 pprint +import uuid + +from IPython.utils.ipstruct import Struct + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +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, output_jpeg=None, prompt_number=None, + etype=None, evalue=None, traceback=None): + """Create a new code cell with input and output""" + output = NotebookNode() + if output_type is not None: + output.output_type = unicode(output_type) + + if output_type != 'pyerr': + if output_text is not None: + output.text = unicode(output_text) + if output_png is not None: + output.png = bytes(output_png) + if output_jpeg is not None: + output.jpeg = bytes(output_jpeg) + if output_html is not None: + output.html = unicode(output_html) + if output_svg is not None: + output.svg = unicode(output_svg) + if output_latex is not None: + output.latex = unicode(output_latex) + if output_json is not None: + output.json = unicode(output_json) + if output_javascript is not None: + output.javascript = 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 etype is not None: + output.etype = unicode(etype) + if evalue is not None: + output.evalue = unicode(evalue) + if traceback is not None: + output.traceback = [unicode(frame) for frame in list(traceback)] + + return output + + +def new_code_cell(input=None, prompt_number=None, outputs=None, + language=u'python', collapsed=False): + """Create a new code cell with input and output""" + cell = NotebookNode() + cell.cell_type = u'code' + if language is not None: + cell.language = unicode(language) + if input is not None: + cell.input = 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) + + return cell + +def new_text_cell(cell_type, source=None, rendered=None): + """Create a new text cell.""" + cell = NotebookNode() + if source is not None: + cell.source = unicode(source) + if rendered is not None: + cell.rendered = unicode(rendered) + cell.cell_type = cell_type + return cell + + +def new_heading_cell(source=None, rendered=None, level=1): + """Create a new section cell with a given integer level.""" + cell = NotebookNode() + cell.cell_type = u'heading' + if source is not None: + cell.source = unicode(source) + if rendered is not None: + cell.rendered = unicode(rendered) + cell.level = int(level) + return cell + + +def new_worksheet(name=None, cells=None): + """Create a worksheet by name with with a list of cells.""" + ws = NotebookNode() + if name is not None: + ws.name = unicode(name) + if cells is None: + ws.cells = [] + else: + ws.cells = list(cells) + return ws + + +def new_notebook(metadata=None, worksheets=None): + """Create a notebook by name, id and a list of worksheets.""" + nb = NotebookNode() + nb.nbformat = 3 + if worksheets is None: + nb.worksheets = [] + else: + nb.worksheets = list(worksheets) + if metadata is None: + nb.metadata = new_metadata() + else: + nb.metadata = NotebookNode(metadata) + 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 = unicode(name) + if authors is not None: + metadata.authors = list(authors) + if created is not None: + metadata.created = unicode(created) + if modified is not None: + metadata.modified = unicode(modified) + if license is not None: + metadata.license = unicode(license) + if gistid is not None: + metadata.gistid = 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 = unicode(name) + if email is not None: + author.email = unicode(email) + if affiliation is not None: + author.affiliation = unicode(affiliation) + if url is not None: + author.url = unicode(url) + return author + diff --git a/IPython/nbformat/v3/nbjson.py b/IPython/nbformat/v3/nbjson.py new file mode 100644 index 0000000..558a613 --- /dev/null +++ b/IPython/nbformat/v3/nbjson.py @@ -0,0 +1,70 @@ +"""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 +) + +#----------------------------------------------------------------------------- +# 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 restore_bytes(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 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 + diff --git a/IPython/nbformat/v3/nbpy.py b/IPython/nbformat/v3/nbpy.py new file mode 100644 index 0000000..ec7daf1 --- /dev/null +++ b/IPython/nbformat/v3/nbpy.py @@ -0,0 +1,200 @@ +"""Read and write notebooks as regular .py files. + +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 re +from .rwbase import NotebookReader, NotebookWriter +from .nbbase import ( + new_code_cell, new_text_cell, new_worksheet, + new_notebook, new_heading_cell +) + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +_encoding_declaration_re = re.compile(r"^#.*coding[:=]\s*([-\w.]+)") + +class PyReaderError(Exception): + pass + + +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 = [] + kwargs = {} + state = u'codecell' + for line in lines: + if line.startswith(u'# ') or _encoding_declaration_re.match(line): + pass + elif line.startswith(u'# '): + cell = self.new_cell(state, cell_lines, **kwargs) + if cell is not None: + cells.append(cell) + state = u'codecell' + cell_lines = [] + kwargs = {} + elif line.startswith(u'# '): + cell = self.new_cell(state, cell_lines, **kwargs) + if cell is not None: + cells.append(cell) + state = u'htmlcell' + cell_lines = [] + kwargs = {} + elif line.startswith(u'# '): + cell = self.new_cell(state, cell_lines, **kwargs) + if cell is not None: + cells.append(cell) + state = u'markdowncell' + cell_lines = [] + kwargs = {} + elif line.startswith(u'# '): + cell = self.new_cell(state, cell_lines, **kwargs) + if cell is not None: + cells.append(cell) + state = u'plaintextcell' + cell_lines = [] + kwargs = {} + elif line.startswith(u'# \d)>',line) + if m is not None: + state = u'headingcell' + kwargs = {} + kwargs['level'] = int(m.group('level')) + else: + state = u'codecell' + kwargs = {} + cell_lines = [] + else: + cell_lines.append(line) + if cell_lines and state == u'codecell': + cell = self.new_cell(state, cell_lines) + if cell is not None: + cells.append(cell) + ws = new_worksheet(cells=cells) + nb = new_notebook(worksheets=[ws]) + return nb + + def new_cell(self, state, lines, **kwargs): + if state == u'codecell': + input = u'\n'.join(lines) + input = input.strip(u'\n') + if input: + return new_code_cell(input=input) + elif state == u'htmlcell': + text = self._remove_comments(lines) + if text: + return new_text_cell(u'html',source=text) + elif state == u'markdowncell': + text = self._remove_comments(lines) + if text: + return new_text_cell(u'markdown',source=text) + elif state == u'plaintextcell': + text = self._remove_comments(lines) + if text: + return new_text_cell(u'plaintext',source=text) + elif state == u'headingcell': + text = self._remove_comments(lines) + level = kwargs.get('level',1) + if text: + return new_heading_cell(source=text,level=level) + + def _remove_comments(self, lines): + new_lines = [] + for line in lines: + if line.startswith(u'#'): + new_lines.append(line[2:]) + else: + new_lines.append(line) + text = u'\n'.join(new_lines) + text = text.strip(u'\n') + return text + + def split_lines_into_blocks(self, lines): + if len(lines) == 1: + yield lines[0] + raise StopIteration() + import ast + source = '\n'.join(lines) + code = ast.parse(source) + starts = [x.lineno-1 for x in code.body] + for i in range(len(starts)-1): + yield '\n'.join(lines[starts[i]:starts[i+1]]).strip('\n') + yield '\n'.join(lines[starts[-1]:]).strip('\n') + + +class PyWriter(NotebookWriter): + + def writes(self, nb, **kwargs): + lines = [u'# -*- coding: utf-8 -*-'] + lines.extend([u'# 2','']) + for ws in nb.worksheets: + for cell in ws.cells: + if cell.cell_type == u'code': + input = cell.get(u'input') + if input is not None: + lines.extend([u'# ',u'']) + lines.extend(input.splitlines()) + lines.append(u'') + elif cell.cell_type == u'html': + input = cell.get(u'source') + if input is not None: + lines.extend([u'# ',u'']) + lines.extend([u'# ' + line for line in input.splitlines()]) + lines.append(u'') + elif cell.cell_type == u'markdown': + input = cell.get(u'source') + if input is not None: + lines.extend([u'# ',u'']) + lines.extend([u'# ' + line for line in input.splitlines()]) + lines.append(u'') + elif cell.cell_type == u'plaintext': + input = cell.get(u'source') + if input is not None: + lines.extend([u'# ',u'']) + lines.extend([u'# ' + line for line in input.splitlines()]) + lines.append(u'') + elif cell.cell_type == u'heading': + input = cell.get(u'source') + level = cell.get(u'level',1) + if input is not None: + lines.extend([u'# ' % level,u'']) + lines.extend([u'# ' + line for line in input.splitlines()]) + lines.append(u'') + lines.append('') + return unicode('\n'.join(lines)) + + +_reader = PyReader() +_writer = PyWriter() + +reads = _reader.reads +read = _reader.read +to_notebook = _reader.to_notebook +write = _writer.write +writes = _writer.writes + diff --git a/IPython/nbformat/v3/rwbase.py b/IPython/nbformat/v3/rwbase.py new file mode 100644 index 0000000..7566b33 --- /dev/null +++ b/IPython/nbformat/v3/rwbase.py @@ -0,0 +1,165 @@ +"""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.py3compat import str_to_bytes + +#----------------------------------------------------------------------------- +# 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. + """ + 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'] + +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 = u'\n'.join(cell.input) + for output in cell.outputs: + for key in _multiline_outputs: + item = output.get(key, None) + if isinstance(item, list): + output[key] = u'\n'.join(item) + else: # text, heading cell + for key in ['source', 'rendered']: + item = cell.get(key, None) + if isinstance(item, list): + cell[key] = u'\n'.join(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, basestring): + cell.input = cell.input.splitlines() + for output in cell.outputs: + for key in _multiline_outputs: + item = output.get(key, None) + if isinstance(item, basestring): + output[key] = item.splitlines() + else: # text, heading cell + for key in ['source', 'rendered']: + item = cell.get(key, None) + if isinstance(item, basestring): + cell[key] = item.splitlines() + 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): + output.png = output.png.encode('ascii') + output.png = decodestring(output.png) + if 'jpeg' in output: + if isinstance(output.jpeg, unicode): + 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""" + return self.read(fp.read(), **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""" + return fp.write(self.writes(nb,**kwargs)) + + + diff --git a/IPython/nbformat/v3/tests/__init__.py b/IPython/nbformat/v3/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/nbformat/v3/tests/__init__.py diff --git a/IPython/nbformat/v3/tests/nbexamples.py b/IPython/nbformat/v3/tests/nbexamples.py new file mode 100644 index 0000000..6482fa7 --- /dev/null +++ b/IPython/nbformat/v3/tests/nbexamples.py @@ -0,0 +1,127 @@ +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 +) + +# some random base64-encoded *bytes* +png = encodestring(os.urandom(5)) +jpeg = encodestring(os.urandom(6)) + +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'plaintext', + 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='print a', + 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', + etype=u'NameError', + evalue=u'NameError was here', + traceback=[u'frame 0', u'frame 1', u'frame 2'] + )] +)) + +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 = """# -*- coding: utf-8 -*- +# 2 + +# + +# Some NumPy Examples + +# + +import numpy + +# + +# A random array + +# + +# A random array + +# + +# My Heading + +# + +a = numpy.random.rand(100) + +# + +print a + +""" + + diff --git a/IPython/nbformat/v3/tests/test_json.py b/IPython/nbformat/v3/tests/test_json.py new file mode 100644 index 0000000..ecbb2d1 --- /dev/null +++ b/IPython/nbformat/v3/tests/test_json.py @@ -0,0 +1,34 @@ +import pprint +from unittest import TestCase + +from ..nbjson import reads, writes +from .nbexamples import nb0 + + +class TestJSON(TestCase): + + def test_roundtrip(self): + s = writes(nb0) + # print + # print pprint.pformat(nb0,indent=2) + # print + # print pprint.pformat(reads(s),indent=2) + # print + # print s + self.assertEquals(reads(s),nb0) + + 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.assertEquals(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.assertEquals(reads(s),nb0) + + + diff --git a/IPython/nbformat/v3/tests/test_nbbase.py b/IPython/nbformat/v3/tests/test_nbbase.py new file mode 100644 index 0000000..3dc4cda --- /dev/null +++ b/IPython/nbformat/v3/tests/test_nbbase.py @@ -0,0 +1,136 @@ +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 +) + +class TestCell(TestCase): + + def test_empty_code_cell(self): + cc = new_code_cell() + self.assertEquals(cc.cell_type,u'code') + self.assertEquals(u'input' not in cc, True) + self.assertEquals(u'prompt_number' not in cc, True) + self.assertEquals(cc.outputs, []) + self.assertEquals(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.assertEquals(cc.input, u'a=10') + self.assertEquals(cc.prompt_number, 0) + self.assertEquals(cc.language, u'python') + self.assertEquals(cc.outputs[0].svg, u'foo') + self.assertEquals(cc.outputs[0].text, u'10') + self.assertEquals(cc.outputs[0].prompt_number, 0) + self.assertEquals(cc.collapsed, True) + + def test_pyerr(self): + o = new_output(output_type=u'pyerr', etype=u'NameError', + evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2'] + ) + self.assertEquals(o.output_type, u'pyerr') + self.assertEquals(o.etype, u'NameError') + self.assertEquals(o.evalue, u'Name not found') + self.assertEquals(o.traceback, [u'frame 0', u'frame 1', u'frame 2']) + + def test_empty_html_cell(self): + tc = new_text_cell(u'html') + self.assertEquals(tc.cell_type, u'html') + self.assertEquals(u'source' not in tc, True) + self.assertEquals(u'rendered' not in tc, True) + + def test_html_cell(self): + tc = new_text_cell(u'html', 'hi', 'hi') + self.assertEquals(tc.source, u'hi') + self.assertEquals(tc.rendered, u'hi') + + def test_empty_markdown_cell(self): + tc = new_text_cell(u'markdown') + self.assertEquals(tc.cell_type, u'markdown') + self.assertEquals(u'source' not in tc, True) + self.assertEquals(u'rendered' not in tc, True) + + def test_markdown_cell(self): + tc = new_text_cell(u'markdown', 'hi', 'hi') + self.assertEquals(tc.source, u'hi') + self.assertEquals(tc.rendered, u'hi') + + def test_empty_plaintext_cell(self): + tc = new_text_cell(u'plaintext') + self.assertEquals(tc.cell_type, u'plaintext') + self.assertEquals(u'source' not in tc, True) + self.assertEquals(u'rendered' not in tc, True) + + def test_plaintext_cell(self): + tc = new_text_cell(u'plaintext', 'hi', 'hi') + self.assertEquals(tc.source, u'hi') + self.assertEquals(tc.rendered, u'hi') + + def test_empty_heading_cell(self): + tc = new_heading_cell() + self.assertEquals(tc.cell_type, u'heading') + self.assertEquals(u'source' not in tc, True) + self.assertEquals(u'rendered' not in tc, True) + + def test_heading_cell(self): + tc = new_heading_cell(u'hi', u'hi', level=2) + self.assertEquals(tc.source, u'hi') + self.assertEquals(tc.rendered, u'hi') + self.assertEquals(tc.level, 2) + + +class TestWorksheet(TestCase): + + def test_empty_worksheet(self): + ws = new_worksheet() + self.assertEquals(ws.cells,[]) + self.assertEquals(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.assertEquals(ws.cells,cells) + self.assertEquals(ws.name,u'foo') + +class TestNotebook(TestCase): + + def test_empty_notebook(self): + nb = new_notebook() + self.assertEquals(nb.worksheets, []) + self.assertEquals(nb.metadata, NotebookNode()) + self.assertEquals(nb.nbformat,2) + + def test_notebook(self): + worksheets = [new_worksheet(),new_worksheet()] + metadata = new_metadata(name=u'foo') + nb = new_notebook(metadata=metadata,worksheets=worksheets) + self.assertEquals(nb.metadata.name,u'foo') + self.assertEquals(nb.worksheets,worksheets) + self.assertEquals(nb.nbformat,2) + +class TestMetadata(TestCase): + + def test_empty_metadata(self): + md = new_metadata() + self.assertEquals(u'name' not in md, True) + self.assertEquals(u'authors' not in md, True) + self.assertEquals(u'license' not in md, True) + self.assertEquals(u'saved' not in md, True) + self.assertEquals(u'modified' not in md, True) + self.assertEquals(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.assertEquals(md.name, u'foo') + self.assertEquals(md.license, u'BSD') + self.assertEquals(md.created, u'today') + self.assertEquals(md.modified, u'now') + self.assertEquals(md.gistid, u'21341231') + self.assertEquals(md.authors, authors) + diff --git a/IPython/nbformat/v3/tests/test_nbpy.py b/IPython/nbformat/v3/tests/test_nbpy.py new file mode 100644 index 0000000..4c12c4e --- /dev/null +++ b/IPython/nbformat/v3/tests/test_nbpy.py @@ -0,0 +1,17 @@ +from unittest import TestCase + +from ..nbbase import ( + NotebookNode, + new_code_cell, new_text_cell, new_worksheet, new_notebook +) + +from ..nbpy import reads, writes +from .nbexamples import nb0, nb0_py + + +class TestPy(TestCase): + + def test_write(self): + s = writes(nb0) + self.assertEquals(s,nb0_py) + diff --git a/docs/examples/notebooks/00_notebook_tour.ipynb b/docs/examples/notebooks/00_notebook_tour.ipynb index 53ddfaa..c1db0df 100644 --- a/docs/examples/notebooks/00_notebook_tour.ipynb +++ b/docs/examples/notebooks/00_notebook_tour.ipynb @@ -1,959 +1,959 @@ { "metadata": { "name": "00_notebook_tour" - }, - "nbformat": 2, + }, + "nbformat": 3, "worksheets": [ { "cells": [ { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "# A brief tour of the IPython notebook", - "", - "This document will give you a brief tour of the capabilities of the IPython notebook. ", - "You can view its contents by scrolling around, or execute each cell by typing `Shift-Enter`.", - "After you conclude this brief high-level tour, you should read the accompanying notebook ", - "titled `01_notebook_introduction`, which takes a more step-by-step approach to the features of the", - "system. ", - "", - "The rest of the notebooks in this directory illustrate various other aspects and ", - "capabilities of the IPython notebook; some of them may require additional libraries to be executed.", - "", - "**NOTE:** This notebook *must* be run from its own directory, so you must ``cd``", - "to this directory and then start the notebook, but do *not* use the ``--notebook-dir``", - "option to run it from another location.", - "", - "The first thing you need to know is that you are still controlling the same old IPython you're used to,", + "# A brief tour of the IPython notebook", + "", + "This document will give you a brief tour of the capabilities of the IPython notebook. ", + "You can view its contents by scrolling around, or execute each cell by typing `Shift-Enter`.", + "After you conclude this brief high-level tour, you should read the accompanying notebook ", + "titled `01_notebook_introduction`, which takes a more step-by-step approach to the features of the", + "system. ", + "", + "The rest of the notebooks in this directory illustrate various other aspects and ", + "capabilities of the IPython notebook; some of them may require additional libraries to be executed.", + "", + "**NOTE:** This notebook *must* be run from its own directory, so you must ``cd``", + "to this directory and then start the notebook, but do *not* use the ``--notebook-dir``", + "option to run it from another location.", + "", + "The first thing you need to know is that you are still controlling the same old IPython you're used to,", "so things like shell aliases and magic commands still work:" ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ "pwd" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "pyout", - "prompt_number": 1, + "output_type": "pyout", + "prompt_number": 1, "text": [ "u'/home/fperez/ipython/ipython/docs/examples/notebooks'" ] } - ], + ], "prompt_number": 1 - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ "ls" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ - "00_notebook_tour.ipynb python-logo.svg", - "01_notebook_introduction.ipynb sympy.ipynb", - "animation.m4v sympy_quantum_computing.ipynb", - "display_protocol.ipynb trapezoid_rule.ipynb", + "00_notebook_tour.ipynb python-logo.svg", + "01_notebook_introduction.ipynb sympy.ipynb", + "animation.m4v sympy_quantum_computing.ipynb", + "display_protocol.ipynb trapezoid_rule.ipynb", "formatting.ipynb" ] } - ], + ], "prompt_number": 2 - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "message = 'The IPython notebook is great!'", - "# note: the echo command does not run on Windows, it's a unix command.", + "message = 'The IPython notebook is great!'", + "# note: the echo command does not run on Windows, it's a unix command.", "!echo $message" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "The IPython notebook is great!" ] } - ], + ], "prompt_number": 3 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "Plots with matplotlib: do *not* execute the next below if you do not have matplotlib installed or didn't start up ", + "Plots with matplotlib: do *not* execute the next below if you do not have matplotlib installed or didn't start up ", "this notebook with the `--pylab` option, as the code will not work." ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "x = linspace(0, 3*pi, 500)", - "plot(x, sin(x**2))", + "x = linspace(0, 3*pi, 500)", + "plot(x, sin(x**2))", "title('A simple chirp');" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "display_data", + "output_type": "display_data", "png": "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\n" } - ], + ], "prompt_number": 4 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "You can paste blocks of input with prompt markers, such as those from", + "You can paste blocks of input with prompt markers, such as those from", "[the official Python tutorial](http://docs.python.org/tutorial/interpreter.html#interactive-mode)" ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - ">>> the_world_is_flat = 1", - ">>> if the_world_is_flat:", + ">>> the_world_is_flat = 1", + ">>> if the_world_is_flat:", "... print \"Be careful not to fall off!\"" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "Be careful not to fall off!" ] } - ], + ], "prompt_number": 5 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ "Errors are shown in informative ways:" ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ "%run non_existent_file" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "stream", - "stream": "stderr", + "output_type": "stream", + "stream": "stderr", "text": [ "ERROR: File `non_existent_file.py` not found." ] } - ], + ], "prompt_number": 6 - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "x = 1", - "y = 4", + "x = 1", + "y = 4", "z = y/(1-x)" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "ename": "ZeroDivisionError", - "evalue": "integer division or modulo by zero", - "output_type": "pyerr", + "ename": "ZeroDivisionError", + "evalue": "integer division or modulo by zero", + "output_type": "pyerr", "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/home/fperez/ipython/ipython/docs/examples/notebooks/\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/home/fperez/ipython/ipython/docs/examples/notebooks/\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: integer division or modulo by zero" ] } - ], + ], "prompt_number": 7 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "When IPython needs to display additional information (such as providing details on an object via `x?`", + "When IPython needs to display additional information (such as providing details on an object via `x?`", "it will automatically invoke a pager at the bottom of the screen:" ] - }, + }, { - "cell_type": "code", - "collapsed": true, + "cell_type": "code", + "collapsed": true, "input": [ "magic" - ], - "language": "python", - "outputs": [], + ], + "language": "python", + "outputs": [], "prompt_number": 8 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "## Non-blocking output of kernel", - "", + "## Non-blocking output of kernel", + "", "If you execute the next cell, you will see the output arriving as it is generated, not all at the end." ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "import time, sys", - "for i in range(8):", - " print i,", + "import time, sys", + "for i in range(8):", + " print i,", " time.sleep(0.5)" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "0 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "1 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "2 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "3 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "4 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "5 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "6 " ] - }, + }, { - "output_type": "stream", - "stream": "stdout", + "output_type": "stream", + "stream": "stdout", "text": [ "7" ] } - ], + ], "prompt_number": 9 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "## Clean crash and restart", - "", - "We call the low-level system libc.time routine with the wrong argument via", + "## Clean crash and restart", + "", + "We call the low-level system libc.time routine with the wrong argument via", "ctypes to segfault the Python interpreter:" ] - }, + }, { - "cell_type": "code", - "collapsed": true, + "cell_type": "code", + "collapsed": true, "input": [ - "from ctypes import CDLL", - "# This will crash a linux system; equivalent calls can be made on Windows or Mac", - "libc = CDLL(\"libc.so.6\") ", + "from ctypes import CDLL", + "# This will crash a linux system; equivalent calls can be made on Windows or Mac", + "libc = CDLL(\"libc.so.6\") ", "libc.time(-1) # BOOM!!" - ], - "language": "python", - "outputs": [], + ], + "language": "python", + "outputs": [], "prompt_number": "*" - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "## Markdown cells can contain formatted text and code", - "", - "You can *italicize*, **boldface**", - "", - "* build", - "* lists", - "", - "and embed code meant for illustration instead of execution in Python:", - "", - " def f(x):", - " \"\"\"a docstring\"\"\"", - " return x**2", - "", - "or other languages:", - "", - " if (i=0; i" - ], - "output_type": "pyout", - "prompt_number": 2, + ], + "output_type": "pyout", + "prompt_number": 2, "text": [ "<IPython.core.display.Image at 0x41d4550>" ] } - ], + ], "prompt_number": 2 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ "SVG images are also supported out of the box (since modern browsers do a good job of rendering them):" ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "from IPython.core.display import SVG", + "from IPython.core.display import SVG", "SVG(filename='python-logo.svg')" - ], - "language": "python", + ], + "language": "python", "outputs": [ { - "output_type": "pyout", - "prompt_number": 3, + "output_type": "pyout", + "prompt_number": 3, "svg": [ - "", - " ", - " ", - " ", - " image/svg+xml", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", + "", + " ", + " ", + " ", + " image/svg+xml", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", "" - ], + ], "text": [ "<IPython.core.display.SVG at 0x41d4910>" ] } - ], + ], "prompt_number": 3 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ "### Video" ] - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "And more exotic objects can also be displayed, as long as their representation supports ", - "the IPython display protocol.", - "", - "For example, videos hosted externally on YouTube are easy to load (and writing a similar wrapper for other", + "And more exotic objects can also be displayed, as long as their representation supports ", + "the IPython display protocol.", + "", + "For example, videos hosted externally on YouTube are easy to load (and writing a similar wrapper for other", "hosted content is trivial):" ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "from IPython.lib.display import YouTubeVideo", - "# a talk about IPython at Sage Days at U. Washington, Seattle.", - "# Video credit: William Stein.", + "from IPython.lib.display import YouTubeVideo", + "# a talk about IPython at Sage Days at U. Washington, Seattle.", + "# Video credit: William Stein.", "YouTubeVideo('1j_HxD4iLn8')" - ], - "language": "python", + ], + "language": "python", "outputs": [ { "html": [ - "", - " ", + "", + " ", " " - ], - "output_type": "pyout", - "prompt_number": 4, + ], + "output_type": "pyout", + "prompt_number": 4, "text": [ "<IPython.lib.display.YouTubeVideo at 0x41d4310>" ] } - ], + ], "prompt_number": 4 - }, + }, { - "cell_type": "markdown", + "cell_type": "markdown", "source": [ - "Using the nascent video capabilities of modern browsers, you may also be able to display local", - "videos. At the moment this doesn't work very well in all browsers, so it may or may not work for you;", - "we will continue testing this and looking for ways to make it more robust. ", - "", - "The following cell loads a local file called `animation.m4v`, encodes the raw video as base64 for http", - "transport, and uses the HTML5 video tag to load it. On Chrome 15 it works correctly, displaying a control", + "Using the nascent video capabilities of modern browsers, you may also be able to display local", + "videos. At the moment this doesn't work very well in all browsers, so it may or may not work for you;", + "we will continue testing this and looking for ways to make it more robust. ", + "", + "The following cell loads a local file called `animation.m4v`, encodes the raw video as base64 for http", + "transport, and uses the HTML5 video tag to load it. On Chrome 15 it works correctly, displaying a control", "bar at the bottom with a play/pause button and a location slider." ] - }, + }, { - "cell_type": "code", - "collapsed": false, + "cell_type": "code", + "collapsed": false, "input": [ - "from IPython.core.display import HTML", - "video = open(\"animation.m4v\", \"rb\").read()", - "video_encoded = video.encode(\"base64\")", - "video_tag = '", - "", - "", - "", - "and a video with the HTML5 video tag:", - "", - "