summaryrefslogtreecommitdiff
path: root/jsonschema
diff options
context:
space:
mode:
authorJulian Berman <Julian@GrayVines.com>2023-02-15 12:25:58 +0200
committerJulian Berman <Julian@GrayVines.com>2023-02-21 09:58:40 +0200
commit9da55dfa02c54b8e638a35362fb99755e5d46ce5 (patch)
treedf1bf6ac00beb2423b98d20df614794e73f3daaa /jsonschema
parent4ec24ab4627adac1bdfb65028642b56ebf7c4c26 (diff)
downloadjsonschema-9da55dfa02c54b8e638a35362fb99755e5d46ce5.tar.gz
Move reference resolution to a (private) Validator method.
This will make it easier to swap over to referencing's Resolver (while preserving backwards compat for anyone who passes a RefResolver to a Validator). At some point in the future this method may become public (which will make it easier for external dialects to resolve references) but let's keep it private for a bit until it's clear that the interface is stable -- a future draft might do crazy things like have adjacent properties to $ref affect the resolution behavior, which would make this method need to take more than just the $ref value.
Diffstat (limited to 'jsonschema')
-rw-r--r--jsonschema/_validators.py27
-rw-r--r--jsonschema/tests/test_jsonschema_test_suite.py52
-rw-r--r--jsonschema/validators.py34
3 files changed, 63 insertions, 50 deletions
diff --git a/jsonschema/_validators.py b/jsonschema/_validators.py
index 8542a87..7351c48 100644
--- a/jsonschema/_validators.py
+++ b/jsonschema/_validators.py
@@ -1,5 +1,4 @@
from fractions import Fraction
-from urllib.parse import urldefrag, urljoin
import re
from jsonschema._utils import (
@@ -286,33 +285,11 @@ def enum(validator, enums, instance, schema):
def ref(validator, ref, instance, schema):
- resolve = getattr(validator.resolver, "resolve", None)
- if resolve is None:
- with validator.resolver.resolving(ref) as resolved:
- yield from validator.descend(instance, resolved)
- else:
- scope, resolved = validator.resolver.resolve(ref)
- validator.resolver.push_scope(scope)
-
- try:
- yield from validator.descend(instance, resolved)
- finally:
- validator.resolver.pop_scope()
+ yield from validator._validate_reference(ref=ref, instance=instance)
def dynamicRef(validator, dynamicRef, instance, schema):
- _, fragment = urldefrag(dynamicRef)
-
- for url in validator.resolver._scopes_stack:
- lookup_url = urljoin(url, dynamicRef)
- with validator.resolver.resolving(lookup_url) as subschema:
- if ("$dynamicAnchor" in subschema
- and fragment == subschema["$dynamicAnchor"]):
- yield from validator.descend(instance, subschema)
- break
- else:
- with validator.resolver.resolving(dynamicRef) as subschema:
- yield from validator.descend(instance, subschema)
+ yield from validator._validate_reference(ref=dynamicRef, instance=instance)
def type(validator, types, instance, schema):
diff --git a/jsonschema/tests/test_jsonschema_test_suite.py b/jsonschema/tests/test_jsonschema_test_suite.py
index 5ebd7ed..3b602aa 100644
--- a/jsonschema/tests/test_jsonschema_test_suite.py
+++ b/jsonschema/tests/test_jsonschema_test_suite.py
@@ -422,23 +422,6 @@ TestDraft202012 = DRAFT202012.to_unittest_testcase(
or skip(
message="dynamicRef support isn't fully working yet.",
subject="dynamicRef",
- description="correct extended schema",
- case_description=(
- "$ref and $dynamicAnchor are independent of order - "
- "$defs first"
- ),
- )(test)
- or skip(
- message="dynamicRef support isn't fully working yet.",
- subject="dynamicRef",
- description="correct extended schema",
- case_description=(
- "$ref and $dynamicAnchor are independent of order - $ref first"
- ),
- )(test)
- or skip(
- message="dynamicRef support isn't fully working yet.",
- subject="dynamicRef",
description="incorrect extended schema",
case_description=(
"$ref and $dynamicAnchor are independent of order - $ref first"
@@ -483,6 +466,41 @@ TestDraft202012 = DRAFT202012.to_unittest_testcase(
case_description="same $anchor with different base uri",
)(test)
or skip(
+ message="dynamicRef support isn't fully working yet.",
+ subject="dynamicRef",
+ description="instance with misspelled field",
+ case_description=(
+ "strict-tree schema, guards against misspelled properties"
+ ),
+ )(test)
+ or skip(
+ message="dynamicRef support isn't fully working yet.",
+ subject="dynamicRef",
+ description="An array containing non-strings is invalid",
+ case_description=(
+ "A $dynamicRef resolves to the first $dynamicAnchor still "
+ "in scope that is encountered when the schema is evaluated"
+ ),
+ )(test)
+ or skip(
+ message="dynamicRef support isn't fully working yet.",
+ subject="dynamicRef",
+ description="An array containing non-strings is invalid",
+ case_description=(
+ "A $dynamicRef with intermediate scopes that don't include a "
+ "matching $dynamicAnchor does not affect dynamic scope "
+ "resolution"
+ ),
+ )(test)
+ or skip(
+ message="dynamicRef support isn't fully working yet.",
+ subject="dynamicRef",
+ description="incorrect extended schema",
+ case_description=(
+ "tests for implementation dynamic anchor and reference link"
+ ),
+ )(test)
+ or skip(
message="Vocabulary support is still in-progress.",
subject="vocabulary",
description=(
diff --git a/jsonschema/validators.py b/jsonschema/validators.py
index b86f376..be5ff18 100644
--- a/jsonschema/validators.py
+++ b/jsonschema/validators.py
@@ -180,7 +180,7 @@ def create(
ID_OF = staticmethod(id_of)
schema = attr.ib(repr=reprlib.repr)
- resolver = attr.ib(default=None, repr=False)
+ _resolver = attr.ib(default=None, repr=False)
format_checker = attr.ib(default=None)
def __init_subclass__(cls):
@@ -198,13 +198,6 @@ def create(
stacklevel=2,
)
- def __attrs_post_init__(self):
- if self.resolver is None:
- self.resolver = _RefResolver.from_schema(
- self.schema,
- id_of=id_of,
- )
-
@classmethod
def check_schema(cls, schema, format_checker=_UNSET):
Validator = validator_for(cls.META_SCHEMA, default=cls)
@@ -217,6 +210,15 @@ def create(
for error in validator.iter_errors(schema):
raise exceptions.SchemaError.create_from(error)
+ @property
+ def resolver(self):
+ if self._resolver is None:
+ self._resolver = _RefResolver.from_schema(
+ self.schema,
+ id_of=id_of,
+ )
+ return self._resolver
+
def evolve(self, **changes):
# Essentially reproduces attr.evolve, but may involve instantiating
# a different class than this one.
@@ -262,6 +264,8 @@ def create(
)
return
+ # Temporarily needed to eagerly create a resolver...
+ self.resolver
scope = id_of(_schema)
if scope:
self.resolver.push_scope(scope)
@@ -306,6 +310,20 @@ def create(
except exceptions.UndefinedTypeCheck:
raise exceptions.UnknownType(type, instance, self.schema)
+ def _validate_reference(self, ref, instance):
+ resolve = getattr(self.resolver, "resolve", None)
+ if resolve is None:
+ with self.resolver.resolving(ref) as resolved:
+ yield from self.descend(instance, resolved)
+ else:
+ scope, resolved = self.resolver.resolve(ref)
+ self.resolver.push_scope(scope)
+
+ try:
+ yield from self.descend(instance, resolved)
+ finally:
+ self.resolver.pop_scope()
+
def is_valid(self, instance, _schema=None):
if _schema is not None:
warnings.warn(