From 5e9055418f1ca500b1ad9c7a5706dfe31cd61202 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 09:19:56 -0500 Subject: [PATCH 1/8] update ci matrix, add oldest supported jsonchema --- .github/workflows/tests.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c427aeb6..da81cb04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,14 +9,26 @@ jobs: name: ${{ matrix.OS }} - Py${{ matrix.PYTHON_VERSION }} runs-on: ${{ matrix.OS }} strategy: - fail-fast: false + fail-fast: false matrix: - OS: ['ubuntu-latest', 'windows-latest'] - PYTHON_VERSION: ['3.7', '3.8', '3.9', '3.10'] + OS: ['ubuntu-latest', 'windows-latest', 'macos-latest'] + PYTHON_VERSION: ['3.7', '3.10', 'pypy-3.8'] + include: + - PYTHON_VERSION: '3.7' + EXTRA_DEPS: '"jsonschema<2.5"' + - PYTHON_VERSION: '3.10' + EXTRA_DEPS: '' + - PYTHON_VERSION: 'pypy-3.8' + EXTRA_DEPS: '' + exclude: + - PYTHON_VERSION: 'pypy-3.8' + OS: 'windows-latest' + - PYTHON_VERSION: 'pypy-3.8' + OS: 'macos-latest' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.PYTHON_VERSION }} - name: Install test dependencies @@ -26,7 +38,7 @@ jobs: pip install codecov pytest-cov - name: Install nbformat run: | - pip install . + pip install . ${{ matrix.EXTRA_DEPS }} pip freeze - name: List dependencies run: pip list From 1146f5e6eed1ac302e570e6870be53153c54769d Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:08:30 -0500 Subject: [PATCH 2/8] also isntall wheel up-front --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index da81cb04..d49c430e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,7 @@ jobs: python-version: ${{ matrix.PYTHON_VERSION }} - name: Install test dependencies run: | - pip install --upgrade pip setuptools + pip install --upgrade pip setuptools wheel pip install .[test] pip install codecov pytest-cov - name: Install nbformat From 5e24e395943227fb29ea112baf6598aec343692e Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:11:32 -0500 Subject: [PATCH 3/8] use .warning --- nbformat/current.py | 11 +++++------ nbformat/v3/nbbase.py | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/nbformat/current.py b/nbformat/current.py index 00bd464d..00d7f03a 100644 --- a/nbformat/current.py +++ b/nbformat/current.py @@ -12,7 +12,7 @@ import re import warnings -warnings.warn("""nbformat.current is deprecated. +warnings.warning("""nbformat.current is deprecated. - use nbformat for read/write/validate public API - use nbformat.vX directly to composing notebooks of a particular version @@ -50,7 +50,7 @@ class NBFormatError(ValueError): def _warn_format(): - warnings.warn("""Non-JSON file support in nbformat is deprecated. + warnings.warning("""Non-JSON file support in nbformat is deprecated. Use nbconvert to create files of other formats.""") @@ -58,7 +58,7 @@ def parse_py(s, **kwargs): """Parse a string into a (nbformat, string) tuple.""" nbf = current_nbformat nbm = current_nbformat_minor - + pattern = r'# (?P\d+[\.\d+]*)' m = re.search(pattern,s) if m is not None: @@ -72,12 +72,12 @@ def parse_py(s, **kwargs): def reads_json(nbjson, **kwargs): """DEPRECATED, use reads""" - warnings.warn("reads_json is deprecated, use reads") + warnings.warning("reads_json is deprecated, use reads") return reads(nbjson) def writes_json(nb, **kwargs): """DEPRECATED, use writes""" - warnings.warn("writes_json is deprecated, use writes") + warnings.warning("writes_json is deprecated, use writes") return writes(nb, **kwargs) def reads_py(s, **kwargs): @@ -189,4 +189,3 @@ def write(nb, fp, format='DEPRECATED', **kwargs): if isinstance(s, bytes): s = s.decode('utf8') return fp.write(s) - diff --git a/nbformat/v3/nbbase.py b/nbformat/v3/nbbase.py index 4d831f9e..c907dec5 100644 --- a/nbformat/v3/nbbase.py +++ b/nbformat/v3/nbbase.py @@ -54,7 +54,7 @@ def cast_str(obj): if isinstance(obj, bytes): # really this should never happend, it should # have been base64 encoded before. - warnings.warn( + warnings.warning( "A notebook got bytes instead of likely base64 encoded values." "The content will likely be corrupted.", UserWarning, @@ -225,4 +225,3 @@ def new_author(name=None, email=None, affiliation=None, url=None): if url is not None: author.url = str_passthrough(url) return author - From 4ba2d91b0b9cb8a2c5eda44fb70ffebad4c5c70c Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:33:35 -0500 Subject: [PATCH 4/8] bump bottom jsonschema pin to 2.6 --- .github/workflows/tests.yml | 4 ++-- nbformat/corpus/tests/test_words.py | 11 +++++------ nbformat/json_compat.py | 2 +- nbformat/v3/nbbase.py | 2 +- setup.py | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d49c430e..1a74fef0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: PYTHON_VERSION: ['3.7', '3.10', 'pypy-3.8'] include: - PYTHON_VERSION: '3.7' - EXTRA_DEPS: '"jsonschema<2.5"' + EXTRA_DEPS: '"jsonschema<2.7"' - PYTHON_VERSION: '3.10' EXTRA_DEPS: '' - PYTHON_VERSION: 'pypy-3.8' @@ -43,7 +43,7 @@ jobs: - name: List dependencies run: pip list - name: Run tests - run: pytest -v --cov=nbformat + run: pytest -v --cov=nbformat --cov-report term-missing:skip-covered --no-cov-on-fail - name: Check manfest run: check-manifest -v - name: Coverage diff --git a/nbformat/corpus/tests/test_words.py b/nbformat/corpus/tests/test_words.py index 972f28e4..bbbb4655 100644 --- a/nbformat/corpus/tests/test_words.py +++ b/nbformat/corpus/tests/test_words.py @@ -9,9 +9,8 @@ from .. import words -def test_generate_corpus_id(): - with pytest.warns(None) as record: - assert len(words.generate_corpus_id()) > 7 - # 1 in 4294967296 (2^32) times this will fail - assert words.generate_corpus_id() != words.generate_corpus_id() - assert len(record) == 0 +def test_generate_corpus_id(recwarn): + assert len(words.generate_corpus_id()) > 7 + # 1 in 4294967296 (2^32) times this will fail + assert words.generate_corpus_id() != words.generate_corpus_id() + assert len(recwarn) == 0 diff --git a/nbformat/json_compat.py b/nbformat/json_compat.py index 2a18be6c..d19815c1 100644 --- a/nbformat/json_compat.py +++ b/nbformat/json_compat.py @@ -35,7 +35,7 @@ def iter_errors(self, data, schema=None): return self._default_validator.iter_errors(data, schema) def error_tree(self, errors): - return ErrorTree(errors) + return ErrorTree(errors=errors) class FastJsonSchemaValidator(JsonSchemaValidator): diff --git a/nbformat/v3/nbbase.py b/nbformat/v3/nbbase.py index c907dec5..822c62c3 100644 --- a/nbformat/v3/nbbase.py +++ b/nbformat/v3/nbbase.py @@ -54,7 +54,7 @@ def cast_str(obj): if isinstance(obj, bytes): # really this should never happend, it should # have been base64 encoded before. - warnings.warning( + warnings.warn( "A notebook got bytes instead of likely base64 encoded values." "The content will likely be corrupted.", UserWarning, diff --git a/setup.py b/setup.py index f20eca65..44cced2b 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ setuptools_args = {} install_requires = setuptools_args['install_requires'] = [ 'traitlets>=4.1', - 'jsonschema>=2.4,!=2.5.0', + 'jsonschema>=2.6', 'jupyter_core', ] From cedb45a73e7f9142c6ba264be8055d0705cbfd7c Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:42:55 -0500 Subject: [PATCH 5/8] more warnigs --- nbformat/validator.py | 7 ++++++- tests/v3/test_nbbase.py | 18 +++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/nbformat/validator.py b/nbformat/validator.py index 977f3a35..49355ca6 100644 --- a/nbformat/validator.py +++ b/nbformat/validator.py @@ -284,7 +284,12 @@ def validate(nbdict=None, ref=None, version=None, version_minor=None, if repair_duplicate_cell_ids: # Best effort to repair if we find a duplicate id cell['id'] = generate_corpus_id() - get_logger().warn("Non-unique cell id '{}' detected. Corrected to '{}'.".format(cell_id, cell['id'])) + get_logger().warning( + "Non-unique cell id '{}' detected. Corrected to '{}'.".format( + cell_id, + cell['id'] + ) + ) else: raise ValidationError("Non-unique cell id '{}' detected.".format(cell_id)) seen_ids.add(cell_id) diff --git a/tests/v3/test_nbbase.py b/tests/v3/test_nbbase.py index 63f5c8b9..4b96df12 100644 --- a/tests/v3/test_nbbase.py +++ b/tests/v3/test_nbbase.py @@ -1,5 +1,7 @@ from unittest import TestCase +import pytest + from nbformat.v3.nbbase import ( NotebookNode, new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, @@ -134,17 +136,19 @@ def test_metadata(self): class TestOutputs(TestCase): def test_binary_png(self): - out = new_output(output_png=b'\x89PNG\r\n\x1a\n', output_type='display_data') + with pytest.warns(UserWarning, match='bytes instead of likely base64'): + out = new_output(output_png=b'\x89PNG\r\n\x1a\n', output_type='display_data') def test_b64b6tes_png(self): # really those tests are wrong, this is not b64, if prefixed by b - out = new_output(output_png=b'iVBORw0KG', output_type='display_data') - + with pytest.warns(UserWarning, match='bytes instead of likely base64'): + out = new_output(output_png=b'iVBORw0KG', output_type='display_data') + def test_binary_jpeg(self): - out = new_output(output_jpeg=b'\xff\xd8', output_type='display_data') + with pytest.warns(UserWarning, match='bytes instead of likely base64'): + out = new_output(output_jpeg=b'\xff\xd8', output_type='display_data') def test_b64b6tes_jpeg(self): # really those tests are wrong, this is not b64, if prefixed by b - out = new_output(output_jpeg=b'/9', output_type='display_data') - - + with pytest.warns(UserWarning, match='bytes instead of likely base64'): + out = new_output(output_jpeg=b'/9', output_type='display_data') From 432ba1c0b160c84c4d11427320e85865ab1330fd Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:43:15 -0500 Subject: [PATCH 6/8] add jsonschema 3 to the matrix --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a74fef0..68f8484e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: - PYTHON_VERSION: '3.10' EXTRA_DEPS: '' - PYTHON_VERSION: 'pypy-3.8' - EXTRA_DEPS: '' + EXTRA_DEPS: '"jsonschema<4"' exclude: - PYTHON_VERSION: 'pypy-3.8' OS: 'windows-latest' From 46d99e21851633865dc1d2744d312379b16633f6 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 13:53:53 -0500 Subject: [PATCH 7/8] use evolve if available --- nbformat/json_compat.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nbformat/json_compat.py b/nbformat/json_compat.py index d19815c1..631e7182 100644 --- a/nbformat/json_compat.py +++ b/nbformat/json_compat.py @@ -20,6 +20,7 @@ _JsonSchemaException = ValidationError + class JsonSchemaValidator: name = "jsonschema" @@ -32,6 +33,10 @@ def validate(self, data): self._default_validator.validate(data) def iter_errors(self, data, schema=None): + if schema is None: + return self._default_validator.iter_errors(data) + if hasattr(self._default_validator, "evolve"): + return self._default_validator.evolve(schema=schema).iter_errors(data) return self._default_validator.iter_errors(data, schema) def error_tree(self, errors): From ded36a57eeeb964109035f2fd8a4ce4264e49ecb Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Mar 2022 14:03:46 -0500 Subject: [PATCH 8/8] use super.iter_errors for fast, check warnings --- nbformat/json_compat.py | 2 +- tests/test_validator.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nbformat/json_compat.py b/nbformat/json_compat.py index 631e7182..9e472941 100644 --- a/nbformat/json_compat.py +++ b/nbformat/json_compat.py @@ -58,7 +58,7 @@ def validate(self, data): def iter_errors(self, data, schema=None): if schema is not None: - return self._default_validator.iter_errors(data, schema) + return super().iter_errors(data, schema) errors = [] validate_func = self._validator diff --git a/tests/test_validator.py b/tests/test_validator.py index 46804a21..34ad06c9 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -201,7 +201,7 @@ def test_invalid_validator_raises_value_error_after_read(): validate(nb) -def test_fallback_validator_with_iter_errors_using_ref(): +def test_fallback_validator_with_iter_errors_using_ref(recwarn): """ Test that when creating a standalone object (code_cell etc) the default validator is used as fallback. @@ -211,6 +211,8 @@ def test_fallback_validator_with_iter_errors_using_ref(): nbformat.v4.new_code_cell() nbformat.v4.new_markdown_cell() nbformat.v4.new_raw_cell() + assert len(recwarn) == 0 + def test_non_unique_cell_ids():