##// END OF EJS Templates
Make nbformat.(read|write) accept file-like object or path
Thomas Kluyver -
Show More
@@ -1,150 +1,161 b''
1 """The IPython notebook format
1 """The IPython notebook format
2
2
3 Use this module to read or write notebook files as particular nbformat versions.
3 Use this module to read or write notebook files as particular nbformat versions.
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 import io
9 from IPython.utils import py3compat
8
10
9 from IPython.utils.log import get_logger
11 from IPython.utils.log import get_logger
10
12
11 from . import v1
13 from . import v1
12 from . import v2
14 from . import v2
13 from . import v3
15 from . import v3
14 from . import v4
16 from . import v4
15
17
16 __all__ = ['versions', 'validate', 'ValidationError', 'convert', 'from_dict',
18 __all__ = ['versions', 'validate', 'ValidationError', 'convert', 'from_dict',
17 'NotebookNode', 'current_nbformat', 'current_nbformat_minor',
19 'NotebookNode', 'current_nbformat', 'current_nbformat_minor',
18 'NBFormatError', 'NO_CONVERT', 'reads', 'read', 'writes', 'write']
20 'NBFormatError', 'NO_CONVERT', 'reads', 'read', 'writes', 'write']
19
21
20 versions = {
22 versions = {
21 1: v1,
23 1: v1,
22 2: v2,
24 2: v2,
23 3: v3,
25 3: v3,
24 4: v4,
26 4: v4,
25 }
27 }
26
28
27 from .validator import validate, ValidationError
29 from .validator import validate, ValidationError
28 from .converter import convert
30 from .converter import convert
29 from . import reader
31 from . import reader
30 from .notebooknode import from_dict, NotebookNode
32 from .notebooknode import from_dict, NotebookNode
31
33
32 from .v4 import (
34 from .v4 import (
33 nbformat as current_nbformat,
35 nbformat as current_nbformat,
34 nbformat_minor as current_nbformat_minor,
36 nbformat_minor as current_nbformat_minor,
35 )
37 )
36
38
37 class NBFormatError(ValueError):
39 class NBFormatError(ValueError):
38 pass
40 pass
39
41
40 # no-conversion singleton
42 # no-conversion singleton
41 NO_CONVERT = object()
43 NO_CONVERT = object()
42
44
43 def reads(s, as_version, **kwargs):
45 def reads(s, as_version, **kwargs):
44 """Read a notebook from a string and return the NotebookNode object as the given version.
46 """Read a notebook from a string and return the NotebookNode object as the given version.
45
47
46 The string can contain a notebook of any version.
48 The string can contain a notebook of any version.
47 The notebook will be returned `as_version`, converting, if necessary.
49 The notebook will be returned `as_version`, converting, if necessary.
48
50
49 Notebook format errors will be logged.
51 Notebook format errors will be logged.
50
52
51 Parameters
53 Parameters
52 ----------
54 ----------
53 s : unicode
55 s : unicode
54 The raw unicode string to read the notebook from.
56 The raw unicode string to read the notebook from.
55 as_version : int
57 as_version : int
56 The version of the notebook format to return.
58 The version of the notebook format to return.
57 The notebook will be converted, if necessary.
59 The notebook will be converted, if necessary.
58 Pass nbformat.NO_CONVERT to prevent conversion.
60 Pass nbformat.NO_CONVERT to prevent conversion.
59
61
60 Returns
62 Returns
61 -------
63 -------
62 nb : NotebookNode
64 nb : NotebookNode
63 The notebook that was read.
65 The notebook that was read.
64 """
66 """
65 nb = reader.reads(s, **kwargs)
67 nb = reader.reads(s, **kwargs)
66 if as_version is not NO_CONVERT:
68 if as_version is not NO_CONVERT:
67 nb = convert(nb, as_version)
69 nb = convert(nb, as_version)
68 try:
70 try:
69 validate(nb)
71 validate(nb)
70 except ValidationError as e:
72 except ValidationError as e:
71 get_logger().error("Notebook JSON is invalid: %s", e)
73 get_logger().error("Notebook JSON is invalid: %s", e)
72 return nb
74 return nb
73
75
74
76
75 def writes(nb, version=NO_CONVERT, **kwargs):
77 def writes(nb, version=NO_CONVERT, **kwargs):
76 """Write a notebook to a string in a given format in the given nbformat version.
78 """Write a notebook to a string in a given format in the given nbformat version.
77
79
78 Any notebook format errors will be logged.
80 Any notebook format errors will be logged.
79
81
80 Parameters
82 Parameters
81 ----------
83 ----------
82 nb : NotebookNode
84 nb : NotebookNode
83 The notebook to write.
85 The notebook to write.
84 version : int, optional
86 version : int, optional
85 The nbformat version to write.
87 The nbformat version to write.
86 If unspecified, or specified as nbformat.NO_CONVERT,
88 If unspecified, or specified as nbformat.NO_CONVERT,
87 the notebook's own version will be used and no conversion performed.
89 the notebook's own version will be used and no conversion performed.
88
90
89 Returns
91 Returns
90 -------
92 -------
91 s : unicode
93 s : unicode
92 The notebook as a JSON string.
94 The notebook as a JSON string.
93 """
95 """
94 if version is not NO_CONVERT:
96 if version is not NO_CONVERT:
95 nb = convert(nb, version)
97 nb = convert(nb, version)
96 else:
98 else:
97 version, _ = reader.get_version(nb)
99 version, _ = reader.get_version(nb)
98 try:
100 try:
99 validate(nb)
101 validate(nb)
100 except ValidationError as e:
102 except ValidationError as e:
101 get_logger().error("Notebook JSON is invalid: %s", e)
103 get_logger().error("Notebook JSON is invalid: %s", e)
102 return versions[version].writes_json(nb, **kwargs)
104 return versions[version].writes_json(nb, **kwargs)
103
105
104
106
105 def read(fp, as_version, **kwargs):
107 def read(fp, as_version, **kwargs):
106 """Read a notebook from a file as a NotebookNode of the given version.
108 """Read a notebook from a file as a NotebookNode of the given version.
107
109
108 The string can contain a notebook of any version.
110 The string can contain a notebook of any version.
109 The notebook will be returned `as_version`, converting, if necessary.
111 The notebook will be returned `as_version`, converting, if necessary.
110
112
111 Notebook format errors will be logged.
113 Notebook format errors will be logged.
112
114
113 Parameters
115 Parameters
114 ----------
116 ----------
115 fp : file
117 fp : file or str
116 Any file-like object with a read method.
118 Any file-like object with a read method, or a path to a file.
117 as_version: int
119 as_version: int
118 The version of the notebook format to return.
120 The version of the notebook format to return.
119 The notebook will be converted, if necessary.
121 The notebook will be converted, if necessary.
120 Pass nbformat.NO_CONVERT to prevent conversion.
122 Pass nbformat.NO_CONVERT to prevent conversion.
121
123
122 Returns
124 Returns
123 -------
125 -------
124 nb : NotebookNode
126 nb : NotebookNode
125 The notebook that was read.
127 The notebook that was read.
126 """
128 """
129 if isinstance(fp, py3compat.string_types):
130 with io.open(fp, encoding='utf-8') as f:
131 return read(f, as_version, **kwargs)
132
127 return reads(fp.read(), as_version, **kwargs)
133 return reads(fp.read(), as_version, **kwargs)
128
134
129
135
130 def write(nb, fp, version=NO_CONVERT, **kwargs):
136 def write(nb, fp, version=NO_CONVERT, **kwargs):
131 """Write a notebook to a file in a given nbformat version.
137 """Write a notebook to a file in a given nbformat version.
132
138
133 The file-like object must accept unicode input.
139 The file-like object must accept unicode input.
134
140
135 Parameters
141 Parameters
136 ----------
142 ----------
137 nb : NotebookNode
143 nb : NotebookNode
138 The notebook to write.
144 The notebook to write.
139 fp : file
145 fp : file or str
140 Any file-like object with a write method that accepts unicode.
146 Any file-like object with a write method that accepts unicode, or
147 a path to write a file.
141 version : int, optional
148 version : int, optional
142 The nbformat version to write.
149 The nbformat version to write.
143 If nb is not this version, it will be converted.
150 If nb is not this version, it will be converted.
144 If unspecified, or specified as nbformat.NO_CONVERT,
151 If unspecified, or specified as nbformat.NO_CONVERT,
145 the notebook's own version will be used and no conversion performed.
152 the notebook's own version will be used and no conversion performed.
146 """
153 """
154 if isinstance(fp, py3compat.string_types):
155 with open(fp, 'w', encoding='utf-8') as f:
156 return write(nb, f, version=version, **kwargs)
157
147 s = writes(nb, version, **kwargs)
158 s = writes(nb, version, **kwargs)
148 if isinstance(s, bytes):
159 if isinstance(s, bytes):
149 s = s.decode('utf8')
160 s = s.decode('utf8')
150 return fp.write(s)
161 return fp.write(s)
@@ -1,37 +1,49 b''
1 """Test the APIs at the top-level of nbformat"""
1 """Test the APIs at the top-level of nbformat"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
6 import json
7 import os
7
8
8 from .base import TestsBase
9 from .base import TestsBase
9
10
11 from IPython.utils.tempdir import TemporaryDirectory
10 from ..reader import get_version
12 from ..reader import get_version
11 from IPython.nbformat import read, current_nbformat, writes
13 from IPython.nbformat import read, current_nbformat, writes, write
12
14
13
15
14 class TestAPI(TestsBase):
16 class TestAPI(TestsBase):
15
17
16 def test_read(self):
18 def test_read(self):
17 """Can older notebooks be opened and automatically converted to the current
19 """Can older notebooks be opened and automatically converted to the current
18 nbformat?"""
20 nbformat?"""
19
21
20 # Open a version 2 notebook.
22 # Open a version 2 notebook.
21 with self.fopen(u'test2.ipynb', 'r') as f:
23 with self.fopen(u'test2.ipynb', 'r') as f:
22 nb = read(f, as_version=current_nbformat)
24 nb = read(f, as_version=current_nbformat)
23
25
24 # Check that the notebook was upgraded to the latest version automatically.
26 # Check that the notebook was upgraded to the latest version automatically.
25 (major, minor) = get_version(nb)
27 (major, minor) = get_version(nb)
26 self.assertEqual(major, current_nbformat)
28 self.assertEqual(major, current_nbformat)
27
29
28 def test_write_downgrade_2(self):
30 def test_write_downgrade_2(self):
29 """dowgrade a v3 notebook to v2"""
31 """dowgrade a v3 notebook to v2"""
30 # Open a version 3 notebook.
32 # Open a version 3 notebook.
31 with self.fopen(u'test3.ipynb', 'r') as f:
33 with self.fopen(u'test3.ipynb', 'r') as f:
32 nb = read(f, as_version=3)
34 nb = read(f, as_version=3)
33
35
34 jsons = writes(nb, version=2)
36 jsons = writes(nb, version=2)
35 nb2 = json.loads(jsons)
37 nb2 = json.loads(jsons)
36 (major, minor) = get_version(nb2)
38 (major, minor) = get_version(nb2)
37 self.assertEqual(major, 2)
39 self.assertEqual(major, 2)
40
41 def test_read_write_path(self):
42 """read() and write() take filesystem paths"""
43 path = os.path.join(self._get_files_path(), u'test4.ipynb')
44 nb = read(path, as_version=4)
45
46 with TemporaryDirectory() as td:
47 dest = os.path.join(td, 'echidna.ipynb')
48 write(nb, dest)
49 assert os.path.isfile(dest)
General Comments 0
You need to be logged in to leave comments. Login now