Skip to content

Commit e826629

Browse files
committed
Resolve $ref using the referencing library.
Passes all the remaining referencing tests across all drafts, hooray! Makes Validators take a referencing.Registry argument which users should use to customize preloaded schemas, or to configure remote reference retrieval. This fully obsoletes jsonschema.RefResolver, which has already been deprecated in a previous commit. Users should move to instead loading schemas into referencing.Registry objects. See the referencing documentation at https://referencing.rtfd.io/ for details (with more jsonschema-specific information to be added shortly). Note that the interface for resolving references on a Validator is not yet public (and hidden behind _resolver and _validate_reference attributes). One or both of these are likely to become public after some period of stabilization. Feedback is of course welcome!
1 parent a39e5c9 commit e826629

File tree

7 files changed

+131
-382
lines changed

7 files changed

+131
-382
lines changed

jsonschema/_legacy_validators.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from referencing.jsonschema import lookup_recursive_ref
2+
13
from jsonschema import _utils
24
from jsonschema.exceptions import ValidationError
35

@@ -210,22 +212,12 @@ def contains_draft6_draft7(validator, contains, instance, schema):
210212

211213

212214
def recursiveRef(validator, recursiveRef, instance, schema):
213-
lookup_url, target = validator.resolver.resolution_scope, validator.schema
214-
215-
for each in reversed(validator.resolver._scopes_stack[1:]):
216-
lookup_url, next_target = validator.resolver.resolve(each)
217-
if next_target.get("$recursiveAnchor"):
218-
target = next_target
219-
else:
220-
break
221-
222-
fragment = recursiveRef.lstrip("#")
223-
subschema = validator.resolver.resolve_fragment(target, fragment)
224-
# FIXME: This is gutted (and not calling .descend) because it can trigger
225-
# recursion errors, so there's a bug here. Re-enable the tests to
226-
# see it.
227-
subschema
228-
return []
215+
resolved = lookup_recursive_ref(validator._resolver)
216+
yield from validator.descend(
217+
instance,
218+
resolved.contents,
219+
resolver=resolved.resolver,
220+
)
229221

230222

231223
def find_evaluated_item_indexes_by_schema(validator, instance, schema):
@@ -243,15 +235,17 @@ def find_evaluated_item_indexes_by_schema(validator, instance, schema):
243235
return list(range(0, len(instance)))
244236

245237
if "$ref" in schema:
246-
scope, resolved = validator.resolver.resolve(schema["$ref"])
247-
validator.resolver.push_scope(scope)
248-
249-
try:
250-
evaluated_indexes += find_evaluated_item_indexes_by_schema(
251-
validator, instance, resolved,
252-
)
253-
finally:
254-
validator.resolver.pop_scope()
238+
resolved = validator._resolver.lookup(schema["$ref"])
239+
evaluated_indexes.extend(
240+
find_evaluated_item_indexes_by_schema(
241+
validator.evolve(
242+
schema=resolved.contents,
243+
_resolver=resolved.resolver,
244+
),
245+
instance,
246+
resolved.contents,
247+
),
248+
)
255249

256250
if "items" in schema:
257251
if validator.is_type(schema["items"], "object"):

jsonschema/_utils.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,17 @@ def find_evaluated_item_indexes_by_schema(validator, instance, schema):
201201
return list(range(0, len(instance)))
202202

203203
if "$ref" in schema:
204-
scope, resolved = validator.resolver.resolve(schema["$ref"])
205-
validator.resolver.push_scope(scope)
206-
207-
try:
208-
evaluated_indexes += find_evaluated_item_indexes_by_schema(
209-
validator, instance, resolved,
210-
)
211-
finally:
212-
validator.resolver.pop_scope()
204+
resolved = validator._resolver.lookup(schema["$ref"])
205+
evaluated_indexes.extend(
206+
find_evaluated_item_indexes_by_schema(
207+
validator.evolve(
208+
schema=resolved.contents,
209+
_resolver=resolved.resolver,
210+
),
211+
instance,
212+
resolved.contents,
213+
),
214+
)
213215

214216
if "prefixItems" in schema:
215217
evaluated_indexes += list(range(0, len(schema["prefixItems"])))
@@ -260,15 +262,17 @@ def find_evaluated_property_keys_by_schema(validator, instance, schema):
260262
evaluated_keys = []
261263

262264
if "$ref" in schema:
263-
scope, resolved = validator.resolver.resolve(schema["$ref"])
264-
validator.resolver.push_scope(scope)
265-
266-
try:
267-
evaluated_keys += find_evaluated_property_keys_by_schema(
268-
validator, instance, resolved,
269-
)
270-
finally:
271-
validator.resolver.pop_scope()
265+
resolved = validator._resolver.lookup(schema["$ref"])
266+
evaluated_keys.extend(
267+
find_evaluated_property_keys_by_schema(
268+
validator.evolve(
269+
schema=resolved.contents,
270+
_resolver=resolved.resolver,
271+
),
272+
instance,
273+
resolved.contents,
274+
),
275+
)
272276

273277
for keyword in [
274278
"properties", "additionalProperties", "unevaluatedProperties",

jsonschema/protocols.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from typing import TYPE_CHECKING, Any, ClassVar, Iterable
1212
import sys
1313

14+
from referencing.jsonschema import SchemaRegistry
15+
1416
# doing these imports with `try ... except ImportError` doesn't pass mypy
1517
# checking because mypy sees `typing._SpecialForm` and
1618
# `typing_extensions._SpecialForm` as incompatible
@@ -60,6 +62,10 @@ class Validator(Protocol):
6062
an invalid schema can lead to undefined behavior. See
6163
`Validator.check_schema` to validate a schema first.
6264
65+
registry:
66+
67+
a schema registry that will be used for looking up JSON references
68+
6369
resolver:
6470
6571
a resolver that will be used to resolve :kw:`$ref`
@@ -113,6 +119,7 @@ class Validator(Protocol):
113119
def __init__(
114120
self,
115121
schema: Mapping | bool,
122+
registry: SchemaRegistry,
116123
format_checker: jsonschema.FormatChecker | None = None,
117124
) -> None:
118125
...

jsonschema/tests/_suite.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
if TYPE_CHECKING:
2222
import pyperf
2323

24-
from jsonschema.validators import _VALIDATORS, _RefResolver
24+
from jsonschema.validators import _VALIDATORS
2525
import jsonschema
2626

2727
_DELIMITERS = re.compile(r"[\W\- ]+")
@@ -245,20 +245,11 @@ def fn(this):
245245

246246
def validate(self, Validator, **kwargs):
247247
Validator.check_schema(self.schema)
248-
resolver = _RefResolver.from_schema(
248+
validator = Validator(
249249
schema=self.schema,
250-
store={k: v.contents for k, v in self._remotes.items()},
251-
id_of=Validator.ID_OF,
250+
registry=self._remotes,
251+
**kwargs,
252252
)
253-
254-
# XXX: #693 asks to improve the public API for this, since yeah, it's
255-
# bad. Figures that since it's hard for end-users, we experience
256-
# the pain internally here too.
257-
def prevent_network_access(uri):
258-
raise RuntimeError(f"Tried to access the network: {uri}")
259-
resolver.resolve_remote = prevent_network_access
260-
261-
validator = Validator(schema=self.schema, resolver=resolver, **kwargs)
262253
if os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0":
263254
breakpoint()
264255
validator.validate(instance=self.data)

0 commit comments

Comments
 (0)