Skip to content

Commit

Permalink
Merge pull request #249 from kevin-bates/capture-validation-error
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Mar 2, 2022
2 parents 59e927d + 96c75a3 commit 66443e6
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ __pycache__
.coverage
.cache
.python-version
.idea
34 changes: 27 additions & 7 deletions nbformat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class NBFormatError(ValueError):
""")


def reads(s, as_version, **kwargs):
def reads(s, as_version, capture_validation_error=None, **kwargs):
"""Read a notebook from a string and return the NotebookNode object as the given version.
The string can contain a notebook of any version.
Expand All @@ -64,6 +64,10 @@ def reads(s, as_version, **kwargs):
The version of the notebook format to return.
The notebook will be converted, if necessary.
Pass nbformat.NO_CONVERT to prevent conversion.
capture_validation_error : dict, optional
If provided, a key of "ValidationError" with a
value of the ValidationError instance will be added
to the dictionary.
Returns
-------
Expand All @@ -77,10 +81,12 @@ def reads(s, as_version, **kwargs):
validate(nb)
except ValidationError as e:
get_logger().error("Notebook JSON is invalid: %s", e)
if isinstance(capture_validation_error, dict):
capture_validation_error['ValidationError'] = e
return nb


def writes(nb, version=NO_CONVERT, **kwargs):
def writes(nb, version=NO_CONVERT, capture_validation_error=None, **kwargs):
"""Write a notebook to a string in a given format in the given nbformat version.
Any notebook format errors will be logged.
Expand All @@ -93,6 +99,10 @@ def writes(nb, version=NO_CONVERT, **kwargs):
The nbformat version to write.
If unspecified, or specified as nbformat.NO_CONVERT,
the notebook's own version will be used and no conversion performed.
capture_validation_error : dict, optional
If provided, a key of "ValidationError" with a
value of the ValidationError instance will be added
to the dictionary.
Returns
-------
Expand All @@ -107,10 +117,12 @@ def writes(nb, version=NO_CONVERT, **kwargs):
validate(nb)
except ValidationError as e:
get_logger().error("Notebook JSON is invalid: %s", e)
if isinstance(capture_validation_error, dict):
capture_validation_error['ValidationError'] = e
return versions[version].writes_json(nb, **kwargs)


def read(fp, as_version, **kwargs):
def read(fp, as_version, capture_validation_error=None, **kwargs):
"""Read a notebook from a file as a NotebookNode of the given version.
The string can contain a notebook of any version.
Expand All @@ -127,6 +139,10 @@ def read(fp, as_version, **kwargs):
The version of the notebook format to return.
The notebook will be converted, if necessary.
Pass nbformat.NO_CONVERT to prevent conversion.
capture_validation_error : dict, optional
If provided, a key of "ValidationError" with a
value of the ValidationError instance will be added
to the dictionary.
Returns
-------
Expand All @@ -138,12 +154,12 @@ def read(fp, as_version, **kwargs):
buf = fp.read()
except AttributeError:
with io.open(fp, encoding='utf-8') as f:
return reads(f.read(), as_version, **kwargs)
return reads(f.read(), as_version, capture_validation_error, **kwargs)

return reads(buf, as_version, **kwargs)
return reads(buf, as_version, capture_validation_error, **kwargs)


def write(nb, fp, version=NO_CONVERT, **kwargs):
def write(nb, fp, version=NO_CONVERT, capture_validation_error=None, **kwargs):
"""Write a notebook to a file in a given nbformat version.
The file-like object must accept unicode input.
Expand All @@ -160,8 +176,12 @@ def write(nb, fp, version=NO_CONVERT, **kwargs):
If nb is not this version, it will be converted.
If unspecified, or specified as nbformat.NO_CONVERT,
the notebook's own version will be used and no conversion performed.
capture_validation_error : dict, optional
If provided, a key of "ValidationError" with a
value of the ValidationError instance will be added
to the dictionary.
"""
s = writes(nb, version, **kwargs)
s = writes(nb, version, capture_validation_error, **kwargs)
if isinstance(s, bytes):
s = s.decode('utf8')

Expand Down
33 changes: 33 additions & 0 deletions nbformat/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import unittest

from .base import TestsBase
from jsonschema import ValidationError

from tempfile import TemporaryDirectory
from ..reader import get_version
from ..validator import isvalid
from nbformat import read, current_nbformat, writes, write


Expand Down Expand Up @@ -64,3 +66,34 @@ def test_read_write_pathlib_object(self):
dest = pathlib.Path(td) / 'echidna.ipynb'
write(nb, dest)
assert os.path.isfile(dest)

def test_capture_validation_error(self):
"""Test that validation error can be captured on read() and write()"""
validation_error = {}
path = os.path.join(self._get_files_path(), u'invalid.ipynb')
nb = read(path, as_version=4, capture_validation_error=validation_error)
assert not isvalid(nb)
assert 'ValidationError' in validation_error
assert isinstance(validation_error['ValidationError'], ValidationError)

validation_error = {}
with TemporaryDirectory() as td:
dest = os.path.join(td, 'invalid.ipynb')
write(nb, dest, capture_validation_error=validation_error)
assert os.path.isfile(dest)
assert 'ValidationError' in validation_error
assert isinstance(validation_error['ValidationError'], ValidationError)

# Repeat with a valid notebook file
validation_error = {}
path = os.path.join(self._get_files_path(), u'test4.ipynb')
nb = read(path, as_version=4, capture_validation_error=validation_error)
assert isvalid(nb)
assert 'ValidationError' not in validation_error

validation_error = {}
with TemporaryDirectory() as td:
dest = os.path.join(td, 'test4.ipynb')
write(nb, dest, capture_validation_error=validation_error)
assert os.path.isfile(dest)
assert 'ValidationError' not in validation_error

0 comments on commit 66443e6

Please sign in to comment.