-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Version conversion, support for X to Y even if Y < X (nbformat) #4198
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
0851100
Added top level convert class, updated init.py to load v#s
jdfreder 4590631
Notebook version conversions done right?
jdfreder 5dd1264
Fixes (iptest), simplified v1, v2, .. , vX import code
jdfreder b23f5b1
Relative import (suggested by @MinRK)
jdfreder 4a52bac
Revert b23f5b1525e76d5a
jdfreder 8df1d7e
Relative import
jdfreder d872173
Some small fixes and changes of nb version conversion
jdfreder 759d5e2
Small fix
jdfreder 1411cb6
Added nbformat ver conv tests, fixed some bugs
jdfreder 5bcf70b
Added test nbs to setup and removed todo
jdfreder 5e5e132
Spelling/typos fixes
jdfreder 7a5b0ad
Updated JSON error message to limit full error to 80 characters.
jdfreder File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
"""API for converting notebooks between versions. | ||
|
||
Authors: | ||
|
||
* Jonathan Frederic | ||
""" | ||
|
||
#----------------------------------------------------------------------------- | ||
# Copyright (C) 2013 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 .reader import get_version, versions | ||
|
||
#----------------------------------------------------------------------------- | ||
# Functions | ||
#----------------------------------------------------------------------------- | ||
|
||
def convert(nb, to_version): | ||
"""Convert a notebook node object to a specific version. Assumes that | ||
all the versions starting from 1 to the latest major X are implemented. | ||
In other words, there should never be a case where v1 v2 v3 v5 exist without | ||
a v4. Also assumes that all conversions can be made in one step increments | ||
between major versions and ignores minor revisions. | ||
|
||
PARAMETERS: | ||
----------- | ||
nb : NotebookNode | ||
to_version : int | ||
Major revision to convert the notebook to. Can either be an upgrade or | ||
a downgrade. | ||
""" | ||
|
||
# Get input notebook version. | ||
(version, version_minor) = get_version(nb) | ||
|
||
# Check if destination is current version, if so return contents | ||
if version == to_version: | ||
return nb | ||
|
||
# If the version exist, try to convert to it one step at a time. | ||
elif to_version in versions: | ||
|
||
# Get the the version that this recursion will convert to as a step | ||
# closer to the final revision. Make sure the newer of the conversion | ||
# functions is used to perform the conversion. | ||
if to_version > version: | ||
step_version = version + 1 | ||
convert_function = versions[step_version].upgrade | ||
else: | ||
step_version = version - 1 | ||
convert_function = versions[version].downgrade | ||
|
||
# Convert and make sure version changed during conversion. | ||
converted = convert_function(nb) | ||
if converted.get('nbformat', 1) == version: | ||
raise Exception("Cannot convert notebook from v%d to v%d. Operation" \ | ||
"failed silently." % (major, step_version)) | ||
|
||
# Recursively convert until target version is reached. | ||
return convert(converted, to_version) | ||
else: | ||
raise Exception("Cannot convert notebook to v%d because that " \ | ||
"version doesn't exist" % (to_version)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
"""API for reading notebooks. | ||
|
||
Authors: | ||
|
||
* Jonathan Frederic | ||
""" | ||
|
||
#----------------------------------------------------------------------------- | ||
# Copyright (C) 2013 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 json | ||
|
||
import v1 | ||
import v2 | ||
import v3 | ||
|
||
versions = { | ||
1: v1, | ||
2: v2, | ||
3: v3, | ||
} | ||
|
||
#----------------------------------------------------------------------------- | ||
# Code | ||
#----------------------------------------------------------------------------- | ||
|
||
class NotJSONError(ValueError): | ||
pass | ||
|
||
def parse_json(s, **kwargs): | ||
"""Parse a JSON string into a dict.""" | ||
try: | ||
nb_dict = json.loads(s, **kwargs) | ||
except ValueError: | ||
# Limit the error message to 80 characters. Display whatever JSON will fit. | ||
raise NotJSONError(("Notebook does not appear to be JSON: %r" % s)[:77] + "...") | ||
return nb_dict | ||
|
||
# High level API | ||
|
||
def get_version(nb): | ||
"""Get the version of a notebook. | ||
|
||
Parameters | ||
---------- | ||
nb : dict | ||
NotebookNode or dict containing notebook data. | ||
|
||
Returns | ||
------- | ||
Tuple containing major (int) and minor (int) version numbers | ||
""" | ||
major = nb.get('nbformat', 1) | ||
minor = nb.get('nbformat_minor', 0) | ||
return (major, minor) | ||
|
||
|
||
def reads(s, **kwargs): | ||
"""Read a notebook from a json string and return the | ||
NotebookNode object. | ||
|
||
This function properly reads notebooks of any version. No version | ||
conversion is performed. | ||
|
||
Parameters | ||
---------- | ||
s : unicode | ||
The raw unicode string to read the notebook from. | ||
|
||
Returns | ||
------- | ||
nb : NotebookNode | ||
The notebook that was read. | ||
""" | ||
nb_dict = parse_json(s, **kwargs) | ||
(major, minor) = get_version(nb_dict) | ||
if major in versions: | ||
return versions[major].to_notebook_json(nb_dict, minor=minor) | ||
else: | ||
raise NBFormatError('Unsupported nbformat version %s' % major) | ||
|
||
|
||
def read(fp, **kwargs): | ||
"""Read a notebook from a file and return the NotebookNode object. | ||
|
||
This function properly reads notebooks of any version. No version | ||
conversion is performed. | ||
|
||
Parameters | ||
---------- | ||
fp : file | ||
Any file-like object with a read method. | ||
|
||
Returns | ||
------- | ||
nb : NotebookNode | ||
The notebook that was read. | ||
""" | ||
return reads(fp.read(), **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
Contains base test class for nbformat | ||
""" | ||
#----------------------------------------------------------------------------- | ||
#Copyright (c) 2013, the IPython Development Team. | ||
# | ||
#Distributed under the terms of the Modified BSD License. | ||
# | ||
#The full license is in the file COPYING.txt, distributed with this software. | ||
#----------------------------------------------------------------------------- | ||
|
||
#----------------------------------------------------------------------------- | ||
# Imports | ||
#----------------------------------------------------------------------------- | ||
|
||
import os | ||
import unittest | ||
|
||
import IPython | ||
|
||
#----------------------------------------------------------------------------- | ||
# Classes and functions | ||
#----------------------------------------------------------------------------- | ||
|
||
class TestsBase(unittest.TestCase): | ||
"""Base tests class.""" | ||
|
||
def fopen(self, f, mode=u'r'): | ||
return open(os.path.join(self._get_files_path(), f), mode) | ||
|
||
|
||
def _get_files_path(self): | ||
|
||
#Get the relative path to this module in the IPython directory. | ||
names = self.__module__.split(u'.')[1:-1] | ||
|
||
#Build a path using the IPython directory and the relative path we just | ||
#found. | ||
path = IPython.__path__[0] | ||
for name in names: | ||
path = os.path.join(path, name) | ||
return path |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you parse JSON, then re-pack it as JSON again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not, it's poor name for that function (see current.py in master, it does the same thing).
to_notebook_json
is actually theto_notebook
function in the vX\nbjson.py file (where vX = v2, 3 etc..) See snippet from code in master:It really is just rejoining the code lines. Should I rename this in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it is confusing, but I'm in favor of renaming things later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
works for me, thanks.