##// END OF EJS Templates
Merge pull request #6045 from minrk/nbformat4...
Merge pull request #6045 from minrk/nbformat4 nbformat v4

File last commit:

r18603:cd1bfb05
r18617:482c7bd6 merge
Show More
convert.py
249 lines | 8.2 KiB | text/x-python | PythonLexer
MinRK
first complete pass on v4...
r18573 """Code for converting notebooks to and from v3."""
MinRK
copy nbformat.v3 to v4
r18568
MinRK
first complete pass on v4...
r18573 # Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
MinRK
copy nbformat.v3 to v4
r18568
MinRK
test and update conversion between v3 <-> v4
r18577 import json
MinRK
remove heading cells in v4
r18596 import re
MinRK
test and update conversion between v3 <-> v4
r18577
MinRK
copy nbformat.v3 to v4
r18568 from .nbbase import (
MinRK
first complete pass on v4...
r18573 nbformat, nbformat_minor,
MinRK
test and update conversion between v3 <-> v4
r18577 NotebookNode,
MinRK
copy nbformat.v3 to v4
r18568 )
MinRK
first complete pass on v4...
r18573 from IPython.nbformat import v3
MinRK
log, don’t raise on validation failure in v4.convert
r18578 from IPython.utils.log import get_logger
MinRK
copy nbformat.v3 to v4
r18568
MinRK
warn on invalid notebooks before/after conversion...
r18594 def _warn_if_invalid(nb, version):
"""Log validation errors, if there are any."""
MinRK
Add top-level IPython.nbformat API...
r18603 from IPython.nbformat import validate, ValidationError
MinRK
warn on invalid notebooks before/after conversion...
r18594 try:
validate(nb, version=version)
except ValidationError as e:
get_logger().error("Notebook JSON is not valid v%i: %s", version, e)
MinRK
copy nbformat.v3 to v4
r18568
MinRK
first complete pass on v4...
r18573 def upgrade(nb, from_version=3, from_minor=0):
"""Convert a notebook to v4.
MinRK
copy nbformat.v3 to v4
r18568
Parameters
----------
nb : NotebookNode
The Python representation of the notebook to convert.
from_version : int
The original version of the notebook to convert.
from_minor : int
The original minor version of the notebook to convert (only relevant for v >= 3).
"""
MinRK
first complete pass on v4...
r18573 if from_version == 3:
MinRK
test and update conversion between v3 <-> v4
r18577 # Validate the notebook before conversion
MinRK
warn on invalid notebooks before/after conversion...
r18594 _warn_if_invalid(nb, from_version)
MinRK
test and update conversion between v3 <-> v4
r18577
MinRK
preserve orig_nbformat when converting 3 to 4...
r18583 # Mark the original nbformat so consumers know it has been converted
orig_nbformat = nb.pop('orig_nbformat', None)
nb.metadata.orig_nbformat = orig_nbformat or 3
MinRK
test and update conversion between v3 <-> v4
r18577 # Mark the new format
MinRK
copy nbformat.v3 to v4
r18568 nb.nbformat = nbformat
nb.nbformat_minor = nbformat_minor
MinRK
first complete pass on v4...
r18573 # remove worksheet(s)
nb['cells'] = cells = []
# In the unlikely event of multiple worksheets,
# they will be flattened
for ws in nb.pop('worksheets', []):
# upgrade each cell
for cell in ws['cells']:
cells.append(upgrade_cell(cell))
MinRK
test and update conversion between v3 <-> v4
r18577 # upgrade metadata
MinRK
first complete pass on v4...
r18573 nb.metadata.pop('name', '')
MinRK
test and update conversion between v3 <-> v4
r18577 # Validate the converted notebook before returning it
MinRK
warn on invalid notebooks before/after conversion...
r18594 _warn_if_invalid(nb, nbformat)
MinRK
copy nbformat.v3 to v4
r18568 return nb
MinRK
first complete pass on v4...
r18573 elif from_version == 4:
# nothing to do
MinRK
copy nbformat.v3 to v4
r18568 if from_minor != nbformat_minor:
MinRK
test and update conversion between v3 <-> v4
r18577 nb.metadata.orig_nbformat_minor = from_minor
MinRK
copy nbformat.v3 to v4
r18568 nb.nbformat_minor = nbformat_minor
MinRK
test and update conversion between v3 <-> v4
r18577
MinRK
copy nbformat.v3 to v4
r18568 return nb
else:
MinRK
first complete pass on v4...
r18573 raise ValueError('Cannot convert a notebook directly from v%s to v4. ' \
MinRK
copy nbformat.v3 to v4
r18568 'Try using the IPython.nbformat.convert module.' % from_version)
MinRK
first complete pass on v4...
r18573 def upgrade_cell(cell):
"""upgrade a cell from v3 to v4
MinRK
copy nbformat.v3 to v4
r18568
MinRK
remove heading cells in v4
r18596 heading cell -> markdown heading
MinRK
first complete pass on v4...
r18573 code cell:
- remove language metadata
- cell.input -> cell.source
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 - cell.prompt_number -> cell.execution_count
MinRK
first complete pass on v4...
r18573 - update outputs
"""
MinRK
test and update conversion between v3 <-> v4
r18577 cell.setdefault('metadata', NotebookNode())
MinRK
first complete pass on v4...
r18573 if cell.cell_type == 'code':
MinRK
test and update conversion between v3 <-> v4
r18577 cell.pop('language', '')
MinRK
convert v3->v4: only set collapsed metadata if value is defined
r18600 if 'collapsed' in cell:
cell.metadata['collapsed'] = cell.pop('collapsed')
MinRK
remove heading cells in v4
r18596 cell.source = cell.pop('input', '')
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 cell.execution_count = cell.pop('prompt_number', None)
MinRK
test and update conversion between v3 <-> v4
r18577 cell.outputs = upgrade_outputs(cell.outputs)
MinRK
remove heading cells in v4
r18596 elif cell.cell_type == 'heading':
cell.cell_type = 'markdown'
level = cell.pop('level', 1)
cell.source = '{hashes} {single_line}'.format(
hashes='#' * level,
single_line = ' '.join(cell.get('source', '').splitlines()),
)
MinRK
test and update conversion between v3 <-> v4
r18577 elif cell.cell_type == 'html':
# Technically, this exists. It will never happen in practice.
cell.cell_type = 'markdown'
MinRK
first complete pass on v4...
r18573 return cell
def downgrade_cell(cell):
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 """downgrade a cell from v4 to v3
code cell:
- set cell.language
- cell.input <- cell.source
- cell.prompt_number <- cell.execution_count
- update outputs
MinRK
remove heading cells in v4
r18596 markdown cell:
- single-line heading -> heading cell
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 """
MinRK
first complete pass on v4...
r18573 if cell.cell_type == 'code':
MinRK
test and update conversion between v3 <-> v4
r18577 cell.language = 'python'
cell.input = cell.pop('source', '')
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 cell.prompt_number = cell.pop('execution_count', None)
MinRK
test and update conversion between v3 <-> v4
r18577 cell.collapsed = cell.metadata.pop('collapsed', False)
cell.outputs = downgrade_outputs(cell.outputs)
MinRK
remove heading cells in v4
r18596 elif cell.cell_type == 'markdown':
source = cell.get('source', '')
if '\n' not in source and source.startswith('#'):
prefix, text = re.match(r'(#+)\s*(.*)', source).groups()
cell.cell_type = 'heading'
cell.source = text
cell.level = len(prefix)
MinRK
first complete pass on v4...
r18573 return cell
_mime_map = {
"text" : "text/plain",
"html" : "text/html",
"svg" : "image/svg+xml",
"png" : "image/png",
"jpeg" : "image/jpeg",
"latex" : "text/latex",
"json" : "application/json",
"javascript" : "application/javascript",
};
def to_mime_key(d):
"""convert dict with v3 aliases to plain mime-type keys"""
for alias, mime in _mime_map.items():
if alias in d:
d[mime] = d.pop(alias)
return d
def from_mime_key(d):
"""convert dict with mime-type keys to v3 aliases"""
for alias, mime in _mime_map.items():
if mime in d:
d[alias] = d.pop(mime)
return d
def upgrade_output(output):
"""upgrade a single code cell output from v3 to v4
- pyout -> execute_result
- pyerr -> error
MinRK
move mime-bundle data to rich output.data...
r18589 - output.type -> output.data.mime/type
MinRK
first complete pass on v4...
r18573 - mime-type keys
MinRK
test and update conversion between v3 <-> v4
r18577 - stream.stream -> stream.name
MinRK
first complete pass on v4...
r18573 """
MinRK
test and update conversion between v3 <-> v4
r18577 if output['output_type'] in {'pyout', 'display_data'}:
MinRK
only rich display outputs have metadata...
r18590 output.setdefault('metadata', NotebookNode())
MinRK
test and update conversion between v3 <-> v4
r18577 if output['output_type'] == 'pyout':
output['output_type'] = 'execute_result'
MinRK
s/prompt_number/execution_count in nbformat 4
r18587 output['execution_count'] = output.pop('prompt_number', None)
MinRK
move mime-bundle data to rich output.data...
r18589
# move output data into data sub-dict
data = {}
for key in list(output):
if key in {'output_type', 'execution_count', 'metadata'}:
continue
data[key] = output.pop(key)
to_mime_key(data)
output['data'] = data
MinRK
test and update conversion between v3 <-> v4
r18577 to_mime_key(output.metadata)
MinRK
move mime-bundle data to rich output.data...
r18589 if 'application/json' in data:
data['application/json'] = json.loads(data['application/json'])
MinRK
test and update conversion between v3 <-> v4
r18577 # promote ascii bytes (from v2) to unicode
for key in ('image/png', 'image/jpeg'):
MinRK
move mime-bundle data to rich output.data...
r18589 if key in data and isinstance(data[key], bytes):
data[key] = data[key].decode('ascii')
MinRK
first complete pass on v4...
r18573 elif output['output_type'] == 'pyerr':
output['output_type'] = 'error'
MinRK
test and update conversion between v3 <-> v4
r18577 elif output['output_type'] == 'stream':
output['name'] = output.pop('stream')
MinRK
first complete pass on v4...
r18573 return output
def downgrade_output(output):
"""downgrade a single code cell output to v3 from v4
- pyout <- execute_result
- pyerr <- error
MinRK
move mime-bundle data to rich output.data...
r18589 - output.data.mime/type -> output.type
MinRK
first complete pass on v4...
r18573 - un-mime-type keys
MinRK
test and update conversion between v3 <-> v4
r18577 - stream.stream <- stream.name
MinRK
first complete pass on v4...
r18573 """
MinRK
move mime-bundle data to rich output.data...
r18589 if output['output_type'] in {'execute_result', 'display_data'}:
if output['output_type'] == 'execute_result':
output['output_type'] = 'pyout'
output['prompt_number'] = output.pop('execution_count', None)
# promote data dict to top-level output namespace
data = output.pop('data', {})
if 'application/json' in data:
data['application/json'] = json.dumps(data['application/json'])
from_mime_key(data)
output.update(data)
MinRK
first complete pass on v4...
r18573 from_mime_key(output.get('metadata', {}))
elif output['output_type'] == 'error':
output['output_type'] = 'pyerr'
MinRK
test and update conversion between v3 <-> v4
r18577 elif output['output_type'] == 'stream':
output['stream'] = output.pop('name')
MinRK
first complete pass on v4...
r18573 return output
def upgrade_outputs(outputs):
"""upgrade outputs of a code cell from v3 to v4"""
return [upgrade_output(op) for op in outputs]
def downgrade_outputs(outputs):
"""downgrade outputs of a code cell to v3 from v4"""
return [downgrade_output(op) for op in outputs]
MinRK
copy nbformat.v3 to v4
r18568
def downgrade(nb):
MinRK
first complete pass on v4...
r18573 """Convert a v4 notebook to v3.
MinRK
copy nbformat.v3 to v4
r18568
Parameters
----------
nb : NotebookNode
The Python representation of the notebook to convert.
"""
MinRK
warn on invalid notebooks before/after conversion...
r18594 if nb.nbformat != nbformat:
return nb
MinRK
test and update conversion between v3 <-> v4
r18577 # Validate the notebook before conversion
MinRK
warn on invalid notebooks before/after conversion...
r18594 _warn_if_invalid(nb, nbformat)
MinRK
test and update conversion between v3 <-> v4
r18577
MinRK
first complete pass on v4...
r18573 nb.nbformat = v3.nbformat
nb.nbformat_minor = v3.nbformat_minor
MinRK
test and update conversion between v3 <-> v4
r18577 cells = [ downgrade_cell(cell) for cell in nb.pop('cells') ]
MinRK
first complete pass on v4...
r18573 nb.worksheets = [v3.new_worksheet(cells=cells)]
nb.metadata.setdefault('name', '')
MinRK
test and update conversion between v3 <-> v4
r18577 nb.metadata.pop('orig_nbformat', None)
nb.metadata.pop('orig_nbformat_minor', None)
# Validate the converted notebook before returning it
MinRK
warn on invalid notebooks before/after conversion...
r18594 _warn_if_invalid(nb, v3.nbformat)
MinRK
test and update conversion between v3 <-> v4
r18577 return nb