Skip to content

Commit

Permalink
Created new notebook magic that can export/convert notebooks.
Browse files Browse the repository at this point in the history
* %notebook --export foo will export the current IPython history
  to a file foo.ipynb.
* %notebook --format=json foo.ipynb will convert foo.ipynb to
  foo.json.
  • Loading branch information
ellisonbg committed Aug 10, 2011
1 parent 53df0ac commit 46fc646
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 9 deletions.
65 changes: 65 additions & 0 deletions IPython/core/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from IPython.core.fakemodule import FakeModule
from IPython.core.profiledir import ProfileDir
from IPython.core.macro import Macro
from IPython.core import magic_arguments
from IPython.core import page
from IPython.core.prefilter import ESC_MAGIC
from IPython.lib.pylabtools import mpl_runner
Expand Down Expand Up @@ -3495,4 +3496,68 @@ def magic_precision(self, s=''):
ptformatter.float_precision = s
return ptformatter.float_format


@magic_arguments.magic_arguments()
@magic_arguments.argument(
'-e', '--export', action='store_true', default=False,
help='Export IPython history as a notebook. The filename argument '
'is used to specify the notebook name and format. For example '
'a filename of notebook.ipynb will result in a notebook name '
'of "notebook" and a format of "xml". Likewise using a ".json" '
'or ".py" file extension will write the notebook in the json '
'or py formats.'
)
@magic_arguments.argument(
'-f', '--format',
help='Convert an existing IPython notebook to a new format. This option '
'specifies the new format and can have the values: xml, json, py. '
'The target filename is choosen automatically based on the new '
'format. The filename argument gives the name of the source file.'
)
@magic_arguments.argument(
'filename', type=unicode,
help='Notebook name or filename'
)
def magic_notebook(self, s):
"""Export and convert IPython notebooks.
This function can export the current IPython history to a notebook file
or can convert an existing notebook file into a different format. For
example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
To export the history to "foo.py" do "%notebook -e foo.py". To convert
"foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
formats include (xml/ipynb, json, py).
"""
args = magic_arguments.parse_argstring(self.magic_notebook, s)
print args

from IPython.nbformat import current
if args.export:
fname, name, format = current.parse_filename(args.filename)
cells = []
hist = list(self.history_manager.get_range())
for session, prompt_number, input in hist[:-1]:
cells.append(current.new_code_cell(prompt_number=prompt_number, input=input))
worksheet = current.new_worksheet(cells=cells)
nb = current.new_notebook(name=name,worksheets=[worksheet])
with open(fname, 'w') as f:
current.write(nb, f, format);
elif args.format is not None:
old_fname, old_name, old_format = current.parse_filename(args.filename)
new_format = args.format
if new_format == u'xml' or new_format == u'ipynb':
new_fname = old_name + u'.ipynb'
new_format = u'xml'
elif new_format == u'py':
new_fname = old_name + u'.py'
elif new_format == u'json':
new_fname = old_name + u'.json'
else:
raise ValueError('Invalid notebook format: %s' % newformat)
with open(old_fname, 'r') as f:
nb = current.read(f, old_format)
with open(new_fname, 'w') as f:
current.write(nb, f, new_format)


# end Magic
3 changes: 2 additions & 1 deletion IPython/nbformat/current.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

from IPython.nbformat.v2 import (
NotebookNode,
new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet
new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet,
parse_filename
)


Expand Down
36 changes: 36 additions & 0 deletions IPython/nbformat/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,39 @@
from .convert import convert_to_this_nbformat


def parse_filename(fname):
"""Parse a notebook filename.
This function takes a notebook filename and returns the notebook
format (xml/json/py) and the notebook name. This logic can be
summarized as follows:
* notebook.ipynb -> (notebook.ipynb, notebook, xml)
* notebook.json -> (notebook.json, notebook, json)
* notebook.py -> (notebook.py, notebook, py)
* notebook -> (notebook.ipynb, notebook, xml)
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'xml'
elif fname.endswith(u'.json'):
format = u'json'
elif fname.endswith(u'.py'):
format = u'py'
else:
fname = fname + u'.ipynb'
format = u'xml'
name = fname.split('.')[0]
return fname, name, format

25 changes: 17 additions & 8 deletions IPython/nbformat/v2/nbpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@ def to_notebook(self, s, **kwargs):
cell_lines = []
code_cell = False
for line in lines:
if line.startswith(u'# <codecell>'):
if line.startswith(u'# <nbformat>'):
pass
elif line.startswith(u'# <codecell>'):
if code_cell:
raise PyReaderError('Unexpected <codecell>')
if cell_lines:
for block in self.split_lines_into_blocks(cell_lines):
cells.append(new_code_cell(input=block))
# We can't use the ast to split blocks because there can be
# IPython syntax in the files.
# if cell_lines:
# for block in self.split_lines_into_blocks(cell_lines):
# cells.append(new_code_cell(input=block))
cell_lines = []
code_cell = True
if line.startswith(u'# </codecell>'):
elif line.startswith(u'# </codecell>'):
if not code_cell:
raise PyReaderError('Unexpected </codecell>')
code = u'\n'.join(cell_lines)
Expand All @@ -37,14 +41,19 @@ def to_notebook(self, s, **kwargs):
code_cell = False
else:
cell_lines.append(line)
# For lines we were not able to process,
for block in self.split_lines_into_blocks(cell_lines):
cells.append(new_code_cell(input=block))
# We can't use the ast to split blocks because there can be
# IPython syntax in the files.
# if cell_lines:
# for block in self.split_lines_into_blocks(cell_lines):
# cells.append(new_code_cell(input=block))
ws = new_worksheet(cells=cells)
nb = new_notebook(worksheets=[ws])
return nb

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)
Expand Down

0 comments on commit 46fc646

Please sign in to comment.