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:", - "", - "