Skip to content

Commit b368dca

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

File tree

4 files changed

+35
-48
lines changed

4 files changed

+35
-48
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-38
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@
1616
import jsonref
1717
import jsonschema.validators
1818
import requests
19-
20-
try:
21-
from functools import cached_property
22-
except ImportError:
23-
from cached_property import cached_property
24-
2519
from flattentool import unflatten
26-
from jsonschema import FormatChecker, RefResolver
20+
from jsonschema import FormatChecker
2721
from jsonschema._utils import extras_msg, find_additional_properties, uniq
2822
from jsonschema.exceptions import UndefinedTypeCheck, ValidationError
23+
from referencing import Registry, Resource
2924

3025
from .exceptions import cove_spreadsheet_conversion_error
3126
from .tools import decimal_default, get_request
3227

28+
try:
29+
from functools import cached_property
30+
except ImportError:
31+
from cached_property import cached_property
32+
33+
3334
REQUIRED_RE = re.compile(r"^'([^']+)'")
3435

3536

@@ -813,14 +814,8 @@ def get_schema_validation_errors(
813814
if extra_checkers:
814815
format_checker.checkers.update(extra_checkers)
815816

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)
817+
if hasattr(schema_obj, "registry"):
818+
registry = schema_obj.registry
824819
else:
825820
if getattr(schema_obj, "extended", None):
826821
resolver = CustomRefResolver(
@@ -839,9 +834,17 @@ def get_schema_validation_errors(
839834
schema_url=schema_obj.schema_host,
840835
)
841836

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

846849
for e in our_validator.iter_errors(json_data):
847850
message = e.message
@@ -1165,7 +1168,7 @@ def get_fields_present(*args, **kwargs):
11651168
}
11661169

11671170

1168-
class CustomRefResolver(RefResolver):
1171+
class CustomRefResolver:
11691172
"""This RefResolver is only for use with the jsonschema library"""
11701173

11711174
def __init__(self, *args, **kw):
@@ -1178,44 +1181,29 @@ def __init__(self, *args, **kw):
11781181
# this is ignored when you supply a file
11791182
self.schema_url = kw.pop("schema_url", "")
11801183
self.config = kw.pop("config", "")
1181-
super().__init__(*args, **kw)
11821184

1183-
def resolve_remote(self, uri):
1185+
def retrieve(self, uri):
11841186
schema_name = uri.split("/")[-1]
11851187
if self.schema_file and self.file_schema_name == schema_name:
11861188
uri = self.schema_file
11871189
else:
11881190
uri = urljoin(self.schema_url, schema_name)
11891191

1190-
document = self.store.get(uri)
1191-
1192-
if document:
1193-
return document
11941192
if uri.startswith("http"):
11951193
# This branch of the if-statement in-lines `RefResolver.resolve_remote()`, but using `get_request()`.
1194+
# https://github.com/python-jsonschema/jsonschema/blob/dbc398245a583cb2366795dc529ae042d10c1577/jsonschema/validators.py#L1008-L1023
11961195
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
1196+
if scheme in ("http", "https"):
12031197
result = get_request(uri, config=self.config).json()
12041198
else:
1205-
# Otherwise, pass off to urllib and assume utf-8
12061199
with urlopen(uri) as url:
12071200
result = json.loads(url.read().decode("utf-8"))
1208-
1209-
if self.cache_remote:
1210-
self.store[uri] = result
1211-
return result
12121201
else:
12131202
with open(uri) as schema_file:
12141203
result = json.load(schema_file)
12151204

12161205
add_is_codelist(result)
1217-
self.store[uri] = result
1218-
return result
1206+
return Resource.from_contents(result)
12191207

12201208

12211209
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)