Skip to content

Commit d09c290

Browse files
jpmckinneyjarofgreen
authored andcommitted
Jsonschema 4.18+ is now required.
python-jsonschema/jsonschema#994
1 parent b508c45 commit d09c290

File tree

4 files changed

+35
-47
lines changed

4 files changed

+35
-47
lines changed

CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
### Removed
1111

1212
- Dropped support for Python 3.6 & 3.7, as these are now end of life.
13-
- Drop jsonschema 3 support
13+
- Jsonschema 4.18+ is now required. Support for 3 and older versions of 4 is removed.
1414

1515
### Changed
1616

1717
- Restore jsonschema's type validator, as its performance has improved in recent Python versions https://github.com./OpenDataServices/lib-cove/pull/127
18-
- Allow `SchemaJsonMixin` classes to define a `validator` method, that accepts lib-cove's JSON Schema draft 4 validator class and its format checker, and returns a validator instance. https://github.com./OpenDataServices/lib-cove/pull/128
18+
- Allow `SchemaJsonMixin` classes to define a `registry` value, TODO
1919

2020
### Fixed
2121

2222
- Calculate additional codelist values for schema using `anyOf` or `oneOf`, like OCDS record packages https://github.com./open-contracting/lib-cove-ocds/issues/106
2323
- Descend into nullable objects and arrays. (For example, OCDS `parties/details` is nullable, and additional codes for `parties/details/scale` were unreported.) https://github.com./OpenDataServices/lib-cove/pull/131
24+
- Process subschemas with our custom validator. Fixes an issue in later versions of Jsonschema.
2425

2526
## [0.31.0] - 2023-07-06
2627

libcove/lib/common.py

+26-37
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,21 @@
1616
import jsonref
1717
import jsonschema.validators
1818
import requests
19+
from flattentool import unflatten
20+
from jsonschema import FormatChecker
21+
from jsonschema._utils import extras_msg, find_additional_properties, uniq
22+
from jsonschema.exceptions import UndefinedTypeCheck, ValidationError
23+
from referencing import Registry, Resource
24+
25+
from .exceptions import cove_spreadsheet_conversion_error
26+
from .tools import decimal_default, get_request
1927

2028
try:
2129
from functools import cached_property
2230
except ImportError:
2331
from cached_property import cached_property
2432

25-
from flattentool import unflatten
26-
from jsonschema import FormatChecker, RefResolver
27-
from jsonschema._utils import extras_msg, find_additional_properties, uniq
28-
from jsonschema.exceptions import UndefinedTypeCheck, ValidationError
2933

30-
from .exceptions import cove_spreadsheet_conversion_error
31-
from .tools import decimal_default, get_request
3234

3335
REQUIRED_RE = re.compile(r"^'([^']+)'")
3436

@@ -813,14 +815,8 @@ def get_schema_validation_errors(
813815
if extra_checkers:
814816
format_checker.checkers.update(extra_checkers)
815817

816-
# Force jsonschema to use our validator.
817-
# https://github.com./python-jsonschema/jsonschema/issues/994
818-
jsonschema.validators.validates("http://json-schema.org/draft-04/schema#")(
819-
validator
820-
)
821-
822-
if hasattr(schema_obj, "validator"):
823-
our_validator = schema_obj.validator(validator, format_checker)
818+
if hasattr(schema_obj, "registry"):
819+
registry = schema_obj.registry
824820
else:
825821
if getattr(schema_obj, "extended", None):
826822
resolver = CustomRefResolver(
@@ -839,9 +835,17 @@ def get_schema_validation_errors(
839835
schema_url=schema_obj.schema_host,
840836
)
841837

842-
our_validator = validator(
843-
pkg_schema_obj, format_checker=format_checker, resolver=resolver
844-
)
838+
registry = Registry(retrieve=resolver.retrieve)
839+
840+
# Force jsonschema to use our validator.
841+
# https://github.com./python-jsonschema/jsonschema/issues/994
842+
jsonschema.validators.validates("http://json-schema.org/draft-04/schema#")(
843+
validator
844+
)
845+
846+
our_validator = validator(
847+
pkg_schema_obj, format_checker=format_checker, registry=registry
848+
)
845849

846850
for e in our_validator.iter_errors(json_data):
847851
message = e.message
@@ -1165,7 +1169,7 @@ def get_fields_present(*args, **kwargs):
11651169
}
11661170

11671171

1168-
class CustomRefResolver(RefResolver):
1172+
class CustomRefResolver:
11691173
"""This RefResolver is only for use with the jsonschema library"""
11701174

11711175
def __init__(self, *args, **kw):
@@ -1178,44 +1182,29 @@ def __init__(self, *args, **kw):
11781182
# this is ignored when you supply a file
11791183
self.schema_url = kw.pop("schema_url", "")
11801184
self.config = kw.pop("config", "")
1181-
super().__init__(*args, **kw)
11821185

1183-
def resolve_remote(self, uri):
1186+
def retrieve(self, uri):
11841187
schema_name = uri.split("/")[-1]
11851188
if self.schema_file and self.file_schema_name == schema_name:
11861189
uri = self.schema_file
11871190
else:
11881191
uri = urljoin(self.schema_url, schema_name)
11891192

1190-
document = self.store.get(uri)
1191-
1192-
if document:
1193-
return document
11941193
if uri.startswith("http"):
11951194
# This branch of the if-statement in-lines `RefResolver.resolve_remote()`, but using `get_request()`.
1195+
# https://github.com./python-jsonschema/jsonschema/blob/dbc398245a583cb2366795dc529ae042d10c1577/jsonschema/validators.py#L1008-L1023
11961196
scheme = urlsplit(uri).scheme
1197-
1198-
if scheme in self.handlers:
1199-
result = self.handlers[scheme](uri)
1200-
elif scheme in ["http", "https"]:
1201-
# Requests has support for detecting the correct encoding of
1202-
# json over http
1197+
if scheme in ("http", "https"):
12031198
result = get_request(uri, config=self.config).json()
12041199
else:
1205-
# Otherwise, pass off to urllib and assume utf-8
12061200
with urlopen(uri) as url:
12071201
result = json.loads(url.read().decode("utf-8"))
1208-
1209-
if self.cache_remote:
1210-
self.store[uri] = result
1211-
return result
12121202
else:
12131203
with open(uri) as schema_file:
12141204
result = json.load(schema_file)
12151205

12161206
add_is_codelist(result)
1217-
self.store[uri] = result
1218-
return result
1207+
return Resource.from_contents(result)
12191208

12201209

12211210
def _get_schema_deprecated_paths(

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
long_description="A data review library",
1212
install_requires=[
1313
"jsonref",
14-
"jsonschema>=4",
14+
"jsonschema>=4.18",
15+
"referencing",
1516
"requests",
1617
"cached-property;python_version<'3.8'",
1718
"flattentool>=0.11.0",

tests/lib/test_common.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import jsonschema
88
import pytest
99
from freezegun import freeze_time
10+
from referencing.exceptions import CannotDetermineSpecification
1011

1112
from libcove.lib.common import (
1213
SchemaJsonMixin,
@@ -766,7 +767,7 @@ def get_pkg_schema_obj(self):
766767
assert "[Decimal('3.1')] is too short" in validation_error_json
767768

768769

769-
def test_property_that_is_not_json_schema_doesnt_raise_exception(caplog, tmpdir):
770+
def test_property_that_is_not_json_schema_does_raise_exception(tmpdir):
770771
tmpdir.join("test.json").write(
771772
json.dumps({"properties": {"bad_property": "not_a_json_schema"}})
772773
)
@@ -778,12 +779,8 @@ class DummySchemaObj:
778779
def get_pkg_schema_obj(self):
779780
return {"$ref": "test.json"}
780781

781-
validation_errors = get_schema_validation_errors({}, DummySchemaObj(), "", {}, {})
782-
assert validation_errors == {}
783-
assert (
784-
"A 'properties' object contains a 'bad_property' value that is not a JSON Schema: 'not_a_json_schema'"
785-
in caplog.text
786-
)
782+
with pytest.raises(CannotDetermineSpecification):
783+
get_schema_validation_errors({}, DummySchemaObj(), "", {}, {})
787784

788785

789786
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)