1"""The Jupyter notebook format
2
3Use this module to read or write notebook files as particular nbformat versions.
4"""
5
6# Copyright (c) IPython Development Team.
7# Distributed under the terms of the Modified BSD License.
8from __future__ import annotations
9
10from pathlib import Path
11
12from traitlets.log import get_logger
13
14from . import v1, v2, v3, v4
15from ._version import __version__, version_info
16from .sentinel import Sentinel
17
18__all__ = [
19 "versions",
20 "validate",
21 "ValidationError",
22 "convert",
23 "from_dict",
24 "NotebookNode",
25 "current_nbformat",
26 "current_nbformat_minor",
27 "NBFormatError",
28 "NO_CONVERT",
29 "reads",
30 "read",
31 "writes",
32 "write",
33 "version_info",
34 "__version__",
35 "Sentinel",
36]
37
38versions = {
39 1: v1,
40 2: v2,
41 3: v3,
42 4: v4,
43}
44
45from . import reader # noqa: E402
46from .converter import convert # noqa: E402
47from .notebooknode import NotebookNode, from_dict # noqa: E402
48from .v4 import nbformat as current_nbformat # noqa: E402
49from .v4 import nbformat_minor as current_nbformat_minor # noqa: E402
50from .validator import ValidationError, validate # noqa: E402
51
52
53class NBFormatError(ValueError):
54 pass
55
56
57# no-conversion singleton
58NO_CONVERT = Sentinel(
59 "NO_CONVERT",
60 __name__,
61 """Value to prevent nbformat to convert notebooks to most recent version.
62 """,
63)
64
65
66def reads(s, as_version, capture_validation_error=None, **kwargs):
67 """Read a notebook from a string and return the NotebookNode object as the given version.
68
69 The string can contain a notebook of any version.
70 The notebook will be returned `as_version`, converting, if necessary.
71
72 Notebook format errors will be logged.
73
74 Parameters
75 ----------
76 s : unicode
77 The raw unicode string to read the notebook from.
78 as_version : int
79 The version of the notebook format to return.
80 The notebook will be converted, if necessary.
81 Pass nbformat.NO_CONVERT to prevent conversion.
82 capture_validation_error : dict, optional
83 If provided, a key of "ValidationError" with a
84 value of the ValidationError instance will be added
85 to the dictionary.
86
87 Returns
88 -------
89 nb : NotebookNode
90 The notebook that was read.
91 """
92 nb = reader.reads(s, **kwargs)
93 if as_version is not NO_CONVERT:
94 nb = convert(nb, as_version)
95 try:
96 validate(nb)
97 except ValidationError as e:
98 get_logger().error("Notebook JSON is invalid: %s", e)
99 if isinstance(capture_validation_error, dict):
100 capture_validation_error["ValidationError"] = e
101 return nb
102
103
104def writes(nb, version=NO_CONVERT, capture_validation_error=None, **kwargs):
105 """Write a notebook to a string in a given format in the given nbformat version.
106
107 Any notebook format errors will be logged.
108
109 Parameters
110 ----------
111 nb : NotebookNode
112 The notebook to write.
113 version : int, optional
114 The nbformat version to write.
115 If unspecified, or specified as nbformat.NO_CONVERT,
116 the notebook's own version will be used and no conversion performed.
117 capture_validation_error : dict, optional
118 If provided, a key of "ValidationError" with a
119 value of the ValidationError instance will be added
120 to the dictionary.
121
122 Returns
123 -------
124 s : unicode
125 The notebook as a JSON string.
126 """
127 if version is not NO_CONVERT:
128 nb = convert(nb, version)
129 else:
130 version, _ = reader.get_version(nb)
131 try:
132 validate(nb)
133 except ValidationError as e:
134 get_logger().error("Notebook JSON is invalid: %s", e)
135 if isinstance(capture_validation_error, dict):
136 capture_validation_error["ValidationError"] = e
137 return versions[version].writes_json(nb, **kwargs)
138
139
140def read(fp, as_version, capture_validation_error=None, **kwargs):
141 """Read a notebook from a file as a NotebookNode of the given version.
142
143 The string can contain a notebook of any version.
144 The notebook will be returned `as_version`, converting, if necessary.
145
146 Notebook format errors will be logged.
147
148 Parameters
149 ----------
150 fp : file or str
151 A file-like object with a read method that returns unicode (use
152 ``io.open()`` in Python 2), or a path to a file.
153 as_version : int
154 The version of the notebook format to return.
155 The notebook will be converted, if necessary.
156 Pass nbformat.NO_CONVERT to prevent conversion.
157 capture_validation_error : dict, optional
158 If provided, a key of "ValidationError" with a
159 value of the ValidationError instance will be added
160 to the dictionary.
161
162 Returns
163 -------
164 nb : NotebookNode
165 The notebook that was read.
166 """
167
168 try:
169 buf = fp.read()
170 except AttributeError:
171 with open(fp, encoding="utf8") as f: # noqa: PTH123
172 return reads(f.read(), as_version, capture_validation_error, **kwargs)
173
174 return reads(buf, as_version, capture_validation_error, **kwargs)
175
176
177def write(nb, fp, version=NO_CONVERT, capture_validation_error=None, **kwargs):
178 """Write a notebook to a file in a given nbformat version.
179
180 The file-like object must accept unicode input.
181
182 Parameters
183 ----------
184 nb : NotebookNode
185 The notebook to write.
186 fp : file or str
187 Any file-like object with a write method that accepts unicode, or
188 a path to write a file.
189 version : int, optional
190 The nbformat version to write.
191 If nb is not this version, it will be converted.
192 If unspecified, or specified as nbformat.NO_CONVERT,
193 the notebook's own version will be used and no conversion performed.
194 capture_validation_error : dict, optional
195 If provided, a key of "ValidationError" with a
196 value of the ValidationError instance will be added
197 to the dictionary.
198 """
199 s = writes(nb, version, capture_validation_error, **kwargs)
200 if isinstance(s, bytes):
201 s = s.decode("utf8")
202
203 try:
204 fp.write(s)
205 if not s.endswith("\n"):
206 fp.write("\n")
207 except AttributeError:
208 with Path(fp).open("w", encoding="utf8") as f:
209 f.write(s)
210 if not s.endswith("\n"):
211 f.write("\n")