##// END OF EJS Templates
Notebook version conversions done right?
Jonathan Frederic -
Show More
@@ -0,0 +1,100 b''
1 """API for reading notebooks.
2
3 Authors:
4
5 * Jonathan Frederic
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import json
20
21 versions = {}
22 for i in range(3):
23 versions[i+1] = __import__('v{0}'.format(i+1))
24
25 #-----------------------------------------------------------------------------
26 # Code
27 #-----------------------------------------------------------------------------
28
29 class NotJSONError(ValueError):
30 pass
31
32 def parse_json(s, **kwargs):
33 """Parse a JSON string into a dict."""
34 try:
35 nb_dict = json.loads(s, **kwargs)
36 except ValueError:
37 raise NotJSONError("Notebook does not appear to be JSON: %r" % s[:16])
38 return nb_dict
39
40 # High level API
41
42 def get_version(nb):
43 """Get the version of a notebook.
44
45 Parameters
46 ----------
47 nb : dict
48 NotebookNode or dict containing notebook data.
49
50 Returns
51 -------
52 Tuple containing major (int) and minor (int) version numbers
53 """
54 major = nb.get('nbformat', 1)
55 minor = nb.get('nbformat_minor', 0)
56 return (major, minor)
57
58
59 def reads(s, format='ipynb', **kwargs):
60 """Read a notebook from a 'ipynb' (json) string and return the
61 NotebookNode object.
62
63 This function properly reads notebooks of any version. No version
64 conversion is performed.
65
66 Parameters
67 ----------
68 s : unicode
69 The raw unicode string to read the notebook from.
70
71 Returns
72 -------
73 nb : NotebookNode
74 The notebook that was read.
75 """
76 nb_dict = parse_json(s, **kwargs)
77 (major, minor) = get_version(nb_dict)
78 if major in versions:
79 return versions[major].to_notebook_json(nb, minor=minor)
80 else:
81 raise NBFormatError('Unsupported nbformat version %s' % major)
82
83
84 def read(fp, **kwargs):
85 """Read a notebook from a file and return the NotebookNode object.
86
87 This function properly reads notebooks of any version. No version
88 conversion is performed.
89
90 Parameters
91 ----------
92 fp : file
93 Any file-like object with a read method.
94
95 Returns
96 -------
97 nb : NotebookNode
98 The notebook that was read.
99 """
100 return reads(fp.read(), format, **kwargs)
@@ -1,3 +0,0 b''
1 import v1
2 import v2
3 import v3
@@ -6,7 +6,7 b' Authors:'
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
@@ -18,51 +18,56 b' Authors:'
18
18
19 import re
19 import re
20
20
21 import IPython.nbformat as nbformat
21 from .reader import get_version, versions
22
23 #-----------------------------------------------------------------------------
24 # Constants
25 #-----------------------------------------------------------------------------
26
27 # Get the convert modules for each major revision.
28 VERSION_REGEX = re.compile("v[0-9]+")
29 version_modules = {}
30 for module in dir(nbformat):
31 if VERSION_REGEX.match(module):
32 version_modules[int(module[1:])] = eval('nbformat.' + module + '.convert')
33
34 # Get the number of minor versions in each major version and create an ordered
35 # list.
36 versions = []
37 for major in version_modules.keys().sort():
38 current_minor = 0
39 if hasattr(version_modules[major], 'nbformat_minor'):
40 current_minor = version_modules[major].nbformat_minor
41
42 for minor in range(0, current_minor + 1):
43 versions.append((major, minor))
44
22
45 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
46 # Functions
24 # Functions
47 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
48
26
49 def convert(nb, to_major, to_minor=0):
27 def convert(nb, to_version):
50 """Convert a notebook node object to a specific version"""
28 """Convert a notebook node object to a specific version. Assumes that
29 all the versions starting from 1 to the latest major X are implemented.
30 In other words, there should never be a case where v1 v2 v3 v5 exist without
31 a v4. Also assumes that all conversions can be made in one step increments
32 between major versions and ignores minor revisions.
33
34 PARAMETERS:
35 -----------
36 nb : NotebookNode
37 to_version : int
38 Major revision to convert the notebook to. Can either be an upgrade or
39 a downgrade.
40 """
51
41
52 # Get input notebook version.
42 # Get input notebook version.
53 major = nb.get('nbformat', 1)
43 (version, version_minor) = get_version(nb)
54 minor = nb.get('nbformat_minor', 0) # v3+
44 version_numbers = versions.keys()
55
45
56 # Check if destination is current version, if so return contents
46 # Check if destination is current version, if so return contents
57 if to_major == major and to_minor == minor:
47 if version == to_version:
58 return nb
48 return nb
59 elif (to_major, to_minor) in versions:
60 index = versions.indexof((major, minor))
61
49
62 if to_major > major or (to_major == major and to_minor > minor):
50 # If the version exist, try to convert to it one step at a time.
63 to_index = index + 1
51 elif to_version in version_numbers:
52
53 # Get the the version that this recursion will convert to as a step
54 # closer to the final revision. Make sure the newer of the conversion
55 # functions is used to perform the conversion.
56 if to_version > version:
57 step_version = version + 1
58 convert_function = versions[step_version].upgrade
64 else:
59 else:
65 to_index = index - 1
60 step_version = version - 1
61 convert_function = versions[version].downgrade
62
63 # Convert and make sure version changed during conversion.
64 converted = convert_function(nb) #todo
65 if converted.get('nbformat', 1) == version:
66 raise Exception("Cannot convert notebook from v%d to v%d. Operation" \
67 "failed silently." % (major, step_version))
66
68
69 # Recuresively convert until target version is reached.
70 return convert(converted, to_version)
67 else:
71 else:
68 raise Exception("Cannot convert notebook to v%d.%d because that version doesn't exist" % (to_major, to_minor)) No newline at end of file
72 raise Exception("Cannot convert notebook to v%d because that " \
73 "version doesn't exist" % (to_version))
@@ -3,6 +3,7 b''
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 * Jonathan Frederic
6 """
7 """
7
8
8 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
@@ -17,14 +18,10 b' Authors:'
17 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
18
19
19 from __future__ import print_function
20 from __future__ import print_function
20 import json
21
21 from xml.etree import ElementTree as ET
22 from xml.etree import ElementTree as ET
22 import re
23 import re
23
24
24 from IPython.nbformat import v3
25 from IPython.nbformat import v2
26 from IPython.nbformat import v1
27
28 from IPython.nbformat.v3 import (
25 from IPython.nbformat.v3 import (
29 NotebookNode,
26 NotebookNode,
30 new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet,
27 new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet,
@@ -32,6 +29,9 b' from IPython.nbformat.v3 import ('
32 nbformat_minor,
29 nbformat_minor,
33 )
30 )
34
31
32 import reader
33 from .convert import convert
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Code
36 # Code
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
@@ -43,20 +43,6 b' current_nbformat_minor = nbformat_minor'
43 class NBFormatError(ValueError):
43 class NBFormatError(ValueError):
44 pass
44 pass
45
45
46 class NotJSONError(ValueError):
47 pass
48
49
50 def parse_json(s, **kwargs):
51 """Parse a string into a (nbformat, dict) tuple."""
52 try:
53 d = json.loads(s, **kwargs)
54 except ValueError:
55 raise NotJSONError("Notebook does not appear to be JSON: %r" % s[:16])
56 nbf = d.get('nbformat', 1)
57 nbm = d.get('nbformat_minor', 0)
58 return nbf, nbm, d
59
60
46
61 def parse_py(s, **kwargs):
47 def parse_py(s, **kwargs):
62 """Parse a string into a (nbformat, string) tuple."""
48 """Parse a string into a (nbformat, string) tuple."""
@@ -76,39 +62,26 b' def parse_py(s, **kwargs):'
76
62
77 def reads_json(s, **kwargs):
63 def reads_json(s, **kwargs):
78 """Read a JSON notebook from a string and return the NotebookNode object."""
64 """Read a JSON notebook from a string and return the NotebookNode object."""
79 nbf, minor, d = parse_json(s, **kwargs)
65 return convert(reader.reads(nb), current_nbformat)
80 if nbf == 1:
81 nb = v1.to_notebook_json(d, **kwargs)
82 nb = v3.convert_to_this_nbformat(nb, orig_version=1)
83 elif nbf == 2:
84 nb = v2.to_notebook_json(d, **kwargs)
85 nb = v3.convert_to_this_nbformat(nb, orig_version=2)
86 elif nbf == 3:
87 nb = v3.to_notebook_json(d, **kwargs)
88 nb = v3.convert_to_this_nbformat(nb, orig_version=3, orig_minor=minor)
89 else:
90 raise NBFormatError('Unsupported JSON nbformat version %s (supported version: %i)' % (nbf, 3))
91 return nb
92
66
93
67
94 def writes_json(nb, **kwargs):
68 def writes_json(nb, **kwargs):
95 return v3.writes_json(nb, **kwargs)
69 return reader.versions[current_nbformat].writes_json(nb, **kwargs)
96
70
97
71
98 def reads_py(s, **kwargs):
72 def reads_py(s, **kwargs):
99 """Read a .py notebook from a string and return the NotebookNode object."""
73 """Read a .py notebook from a string and return the NotebookNode object."""
100 nbf, nbm, s = parse_py(s, **kwargs)
74 nbf, nbm, s = parse_py(s, **kwargs)
101 if nbf == 2:
75 if nbf == 2 or nbf == 3:
102 nb = v2.to_notebook_py(s, **kwargs)
76 nb = reader.versions[nbf].to_notebook_py(s, **kwargs)
103 elif nbf == 3:
104 nb = v3.to_notebook_py(s, **kwargs)
105 else:
77 else:
106 raise NBFormatError('Unsupported PY nbformat version: %i' % nbf)
78 raise NBFormatError('Unsupported PY nbformat version: %i' % nbf)
107 return nb
79 return nb
108
80
109
81
110 def writes_py(nb, **kwargs):
82 def writes_py(nb, **kwargs):
111 return v3.writes_py(nb, **kwargs)
83 # nbformat 3 is the latest format that supports py
84 return reader.versions[3].writes_py(nb, **kwargs)
112
85
113
86
114 # High level API
87 # High level API
@@ -20,5 +20,4 b' from .nbjson import reads as reads_json, writes as writes_json'
20 from .nbjson import reads as read_json, writes as write_json
20 from .nbjson import reads as read_json, writes as write_json
21 from .nbjson import to_notebook as to_notebook_json
21 from .nbjson import to_notebook as to_notebook_json
22
22
23 from .convert import convert_to_this_nbformat
23 from .convert import downgrade, upgrade
24
@@ -11,6 +11,6 b''
11 # Code
11 # Code
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 def convert_to_this_nbformat(nb, orig_version=None):
14 def upgrade(nb, orig_version=None):
15 raise ValueError('Cannot convert to v1 notebook format')
15 raise ValueError('Cannot convert to v1 notebook format')
16
16
@@ -34,7 +34,7 b' from .nbpy import reads as reads_py, writes as writes_py'
34 from .nbpy import reads as read_py, writes as write_py
34 from .nbpy import reads as read_py, writes as write_py
35 from .nbpy import to_notebook as to_notebook_py
35 from .nbpy import to_notebook as to_notebook_py
36
36
37 from .convert import convert_to_this_nbformat
37 from .convert import downgrade, upgrade
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Code
40 # Code
@@ -3,6 +3,7 b''
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 * Jonathan Frederic
6 """
7 """
7
8
8 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
@@ -24,7 +25,7 b' from .nbbase import ('
24 # Code
25 # Code
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
26
27
27 def convert_to_this_nbformat(nb, orig_version=1):
28 def upgrade(nb, from_version=1):
28 """Convert a notebook to the v2 format.
29 """Convert a notebook to the v2 format.
29
30
30 Parameters
31 Parameters
@@ -48,3 +49,13 b' def convert_to_this_nbformat(nb, orig_version=1):'
48 else:
49 else:
49 raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version)
50 raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version)
50
51
52
53 def downgrade(nb):
54 """Convert a v2 notebook to v1.
55
56 Parameters
57 ----------
58 nb : NotebookNode
59 The Python representation of the notebook to convert.
60 """
61 raise Exception("Downgrade from notebook v2 to v1 is not supported.")
@@ -30,7 +30,7 b' from .nbpy import reads as reads_py, writes as writes_py'
30 from .nbpy import reads as read_py, writes as write_py
30 from .nbpy import reads as read_py, writes as write_py
31 from .nbpy import to_notebook as to_notebook_py
31 from .nbpy import to_notebook as to_notebook_py
32
32
33 from .convert import convert_to_this_nbformat
33 from .convert import downgrade, upgrade
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Code
36 # Code
@@ -3,6 +3,8 b''
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 * Min RK
7 * Jonathan Frederic
6 """
8 """
7
9
8 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
@@ -27,33 +29,62 b' from IPython.nbformat import v2'
27 # Code
29 # Code
28 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
29
31
30 def convert_to_this_nbformat(nb, orig_version=2, orig_minor=0):
32 def upgrade(nb, from_version=2, from_minor=0):
31 """Convert a notebook to the v3 format.
33 """Convert a notebook to v3.
32
34
33 Parameters
35 Parameters
34 ----------
36 ----------
35 nb : NotebookNode
37 nb : NotebookNode
36 The Python representation of the notebook to convert.
38 The Python representation of the notebook to convert.
37 orig_version : int
39 from_version : int
38 The original version of the notebook to convert.
40 The original version of the notebook to convert.
39 orig_minor : int
41 from_minor : int
40 The original minor version of the notebook to convert (only relevant for v >= 3).
42 The original minor version of the notebook to convert (only relevant for v >= 3).
41 """
43 """
42 if orig_version == 1:
44 if from_version == 2:
43 nb = v2.convert_to_this_nbformat(nb)
44 orig_version = 2
45 if orig_version == 2:
46 # Mark the original nbformat so consumers know it has been converted.
45 # Mark the original nbformat so consumers know it has been converted.
47 nb.nbformat = nbformat
46 nb.nbformat = nbformat
48 nb.nbformat_minor = nbformat_minor
47 nb.nbformat_minor = nbformat_minor
49
48
50 nb.orig_nbformat = 2
49 nb.orig_nbformat = 2
51 return nb
50 return nb
52 elif orig_version == 3:
51 elif from_version == 3:
53 if orig_minor != nbformat_minor:
52 if from_minor != nbformat_minor:
54 nb.orig_nbformat_minor = orig_minor
53 nb.orig_nbformat_minor = from_minor
55 nb.nbformat_minor = nbformat_minor
54 nb.nbformat_minor = nbformat_minor
56 return nb
55 return nb
57 else:
56 else:
58 raise ValueError('Cannot convert a notebook from v%s to v3' % orig_version)
57 raise ValueError('Cannot convert a notebook directly from v%s to v3. ' \
58 'Try using the IPython.nbformat.convert module.' % from_version)
59
59
60
61 def heading_to_md(cell):
62 """turn heading cell into corresponding markdown"""
63 cell.cell_type = "markdown"
64 level = cell.pop('level', 1)
65 cell.source = '#'*level + ' ' + cell.source
66
67
68 def raw_to_md(cell):
69 """let raw passthrough as markdown"""
70 cell.cell_type = "markdown"
71
72
73 def downgrade(nb):
74 """Convert a v3 notebook to v2.
75
76 Parameters
77 ----------
78 nb : NotebookNode
79 The Python representation of the notebook to convert.
80 """
81 if nb.nbformat != 3:
82 return nb
83 nb.nbformat = 2
84 for ws in nb.worksheets:
85 for cell in ws.cells:
86 if cell.cell_type == 'heading':
87 heading_to_md(cell)
88 elif cell.cell_type == 'raw':
89 raw_to_md(cell)
90 return nb No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now