From 7b1ec0ae9603adab926b2da01e00e16646b03aff Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 16 Apr 2014 14:58:40 +0200 Subject: [PATCH 1/5] Rename _remove_consecutive_duplicates to _squash_repeats --- doc/ext/notebook.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/doc/ext/notebook.py b/doc/ext/notebook.py index 36c6fa1f..a306f11d 100644 --- a/doc/ext/notebook.py +++ b/doc/ext/notebook.py @@ -30,8 +30,8 @@ sample = """{ }""" -def _remove_consecutive_duplicates(x): - """Remove duplicates of elements appearing consecutively. +def _squash_repeats(x): + """Reduce repeating elements to a single occurrance. Parameters ---------- @@ -40,19 +40,17 @@ def _remove_consecutive_duplicates(x): Returns ------- - modified_x : list - Output list, with no consecutive duplicates. + list + A copy of `x` with repeating elements squashed. Examples -------- >>> input = [1, 2, 3, 3, 4, 5, 6, 6] - >>> output = remove_consecutive_duplicates(input) - >>> output + >>> print _squash_repeats(input) [1, 2, 3, 4, 5, 6] """ - modified_x = [x[0]] + [x[i] for i in range(1, len(x)) if x[i] != x[i-1]] - return modified_x + return [x[0]] + [x[i] for i in range(1, len(x)) if x[i] != x[i-1]] class Notebook(): @@ -146,7 +144,7 @@ def python_to_notebook(example_file, notebook_path): docstring = False source = [] - code = _remove_consecutive_duplicates(nb.code) + code = _squash_repeats(nb.code) for line in code: # A linebreak indicates a segment has ended. From a9107bbd5c70e23b18e58bf00417da5495f3be77 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 16 Apr 2014 15:01:41 +0200 Subject: [PATCH 2/5] Update Notebook docstring --- doc/ext/notebook.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/doc/ext/notebook.py b/doc/ext/notebook.py index a306f11d..3827eecf 100644 --- a/doc/ext/notebook.py +++ b/doc/ext/notebook.py @@ -53,11 +53,9 @@ def _squash_repeats(x): return [x[0]] + [x[i] for i in range(1, len(x)) if x[i] != x[i-1]] -class Notebook(): +class Notebook(object): """ - Notebook object for generating an IPython notebook from an example Python - file. - + Notebook object for building an IPython notebook cell-by-cell. """ def __init__(self): @@ -91,10 +89,9 @@ class Notebook(): Parameters ---------- value : str - The actual content to be saved in the cell. + Cell content. cell_type : {'code', 'markdown'} - The type of content. - The default value will add a cell of type 'code'. + Type of content (default is 'code'). """ if cell_type in ['markdown', 'code']: @@ -104,15 +101,15 @@ class Notebook(): # assign value to the last cell cells[-1][key] = value else: - warnings.warn('Unsupported cell type %s, data ignored' % cell_type) + warnings.warn('Ignoring unsupported cell type (%s)' % cell_type) def json(self): - """Dump the template JSON to string. + """Return a JSON representation of the notebook. Returns ------- str - The template JSON converted to a string with a two char indent. + JSON notebook. """ return json.dumps(self.template, indent=2) From bbd7817eadc51b5151f26415026a9ac34b001ed0 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 16 Apr 2014 15:03:19 +0200 Subject: [PATCH 3/5] Add unit tests --- doc/ext/notebook.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/ext/notebook.py b/doc/ext/notebook.py index 3827eecf..e2f7d62b 100644 --- a/doc/ext/notebook.py +++ b/doc/ext/notebook.py @@ -4,6 +4,7 @@ import json import copy import warnings + sample = """{ "metadata": { "name":"" @@ -176,3 +177,12 @@ def python_to_notebook(example_file, notebook_path): with open(notebook_path, 'w') as output: output.write(nb.json()) + + +def test_foo(): + assert 1==1 + + +if __name__ == "__main__": + import numpy.testing as npt + npt.run_module_suite() From c597fa47d46508fa135a24374f70721b21f17229 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 16 Apr 2014 16:45:06 +0200 Subject: [PATCH 4/5] Add tests for notebook. Remove python_to_notebook utility function. --- doc/ext/notebook.py | 110 ++++++++++---------------------------------- 1 file changed, 24 insertions(+), 86 deletions(-) diff --git a/doc/ext/notebook.py b/doc/ext/notebook.py index e2f7d62b..054a9c44 100644 --- a/doc/ext/notebook.py +++ b/doc/ext/notebook.py @@ -5,7 +5,8 @@ import copy import warnings -sample = """{ +# Skeleton notebook in JSON format +skeleton_nb = """{ "metadata": { "name":"" }, @@ -31,29 +32,6 @@ sample = """{ }""" -def _squash_repeats(x): - """Reduce repeating elements to a single occurrance. - - Parameters - ---------- - x : list - Input list. - - Returns - ------- - list - A copy of `x` with repeating elements squashed. - - Examples - -------- - >>> input = [1, 2, 3, 3, 4, 5, 6, 6] - >>> print _squash_repeats(input) - [1, 2, 3, 4, 5, 6] - - """ - return [x[0]] + [x[i] for i in range(1, len(x)) if x[i] != x[i-1]] - - class Notebook(object): """ Notebook object for building an IPython notebook cell-by-cell. @@ -81,6 +59,7 @@ class Notebook(object): ] } + self.template = json.loads(skeleton_nb) self.cell_type = {'input': self.cell_code, 'source': self.cell_md} self.valuetype_to_celltype = {'code': 'input', 'markdown': 'source'} @@ -116,71 +95,30 @@ class Notebook(object): return json.dumps(self.template, indent=2) -def python_to_notebook(example_file, notebook_path): - """Convert a Python file to an IPython notebook. - - Parameters - ---------- - example_file : str - Path for source Python file. - notebook_path : str - Path for saving the notebook file (includes the filename). - - """ +def test_notebook_basic(): nb = Notebook() - with open(example_file, 'r') as pythonfile: - nb.template = json.loads(sample) - nb.code = pythonfile.readlines() - # Add an extra newline at the end, - # this aids in extraction of text segments - nb.code.append('\n') - - # Newline separated portions in example file, are sections. - # Code and markdown written together in such a section are further - # treated as different segments. Each cell has content from one - # segment. - docstring = False - source = [] - - code = _squash_repeats(nb.code) - - for line in code: - # A linebreak indicates a segment has ended. - # If the text segment had only comments, ignore the blank source as - # already added in cell type markdown - if line == '\n': - if source: - # we've found text segments within the docstring - if docstring: - nb.add_cell(source, 'markdown') - else: - nb.add_cell(source, 'code') - source = [] - # if it's a comment - elif line.strip().startswith('#'): - line = line.lstrip(' #') - nb.add_cell(line, 'markdown') - elif line == '"""\n': - if not docstring: - docstring = True - # Indicates, completion of docstring - # add whatever in source to markdown (cell type markdown) - elif docstring: - docstring = False - # Write leftover docstring if any left - if source: - nb.add_cell(source, 'markdown') - source = [] - else: - # some text segment is continuing, so add to source - source.append(line) - - with open(notebook_path, 'w') as output: - output.write(nb.json()) + assert(json.loads(nb.json()) == json.loads(skeleton_nb)) -def test_foo(): - assert 1==1 +def test_notebook_add(): + nb = Notebook() + + str1 = 'hello world' + str2 = 'f = lambda x: x * x' + + nb.add_cell(str1, cell_type='markdown') + nb.add_cell(str2, cell_type='code') + + d = json.loads(nb.json()) + cells = d['worksheets'][0]['cells'] + values = [c['input'] if c['cell_type'] == 'code' else c['source'] + for c in cells] + + assert values[1] == str1 + assert values[2] == str2 + + assert cells[1]['cell_type'] == 'markdown' + assert cells[2]['cell_type'] == 'code' if __name__ == "__main__": From 5dc647c860fc3312738e4f53db654dcba90611cb Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 16 Apr 2014 16:45:25 +0200 Subject: [PATCH 5/5] Update plot2rst to generate notebooks --- doc/ext/plot2rst.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/ext/plot2rst.py b/doc/ext/plot2rst.py index 128cd4f9..258a6929 100644 --- a/doc/ext/plot2rst.py +++ b/doc/ext/plot2rst.py @@ -80,7 +80,9 @@ from skimage import io from skimage import transform from skimage.util.dtype import dtype_range -from notebook import python_to_notebook +from notebook import Notebook + +from docutils.core import publish_parts LITERALINCLUDE = """ @@ -326,7 +328,8 @@ def write_example(src_name, src_dir, rst_dir, cfg): rst_path = rst_dir.pjoin(basename + cfg.source_suffix) notebook_path = notebook_dir.pjoin(basename + '.ipynb') - if _plots_are_current(src_path, image_path) and rst_path.exists and notebook_path.exists: + if _plots_are_current(src_path, image_path) and rst_path.exists and \ + notebook_path.exists: return blocks = split_code_and_text_blocks(example_file) @@ -374,7 +377,23 @@ def write_example(src_name, src_dir, rst_dir, cfg): else: shutil.copy(cfg.plot2rst_default_thumb, thumb_path) - python_to_notebook(example_file, notebook_path) + # Export example to IPython notebook + nb = Notebook() + + for (cell_type, _, content) in blocks: + content = content.rstrip('\n') + + if cell_type == 'code': + nb.add_cell(content, cell_type='code') + else: + content = content.replace('"""', '') + content = '\n'.join([line for line in content.split('\n') if + not line.startswith('.. image')]) + html = publish_parts(content, writer_name='html')['html_body'] + nb.add_cell(html, cell_type='markdown') + + with open(notebook_path, 'w') as f: + f.write(nb.json()) def save_thumbnail(image, thumb_path, shape):