summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Berman <Julian@GrayVines.com>2021-12-16 19:54:07 -0500
committerJulian Berman <Julian@GrayVines.com>2021-12-16 19:54:07 -0500
commitd5c4d07ddddf34ad5c9f96982c192b5f71bb85a8 (patch)
tree24c056ab1fe14993608aaa63bdf2edc4bb269a01
parent0878727e6dc39f1f9268a626a6b6390b2dde2f4a (diff)
parent251763a6006e2c270629af199d350452ef7a6ba6 (diff)
downloadjsonschema-d5c4d07ddddf34ad5c9f96982c192b5f71bb85a8.tar.gz
Merge remote-tracking branch 'Stranger6667/dd/cache-ref-resolving'
* Stranger6667/dd/cache-ref-resolving: docs: Update changelog perf: Replace the `Validator.evolve` method with an equivalent class attribute perf: Use cached lookups for resolving fragments if the referent document is known perf: Cache reference lookups for subschemas
-rw-r--r--CHANGELOG.rst1
-rw-r--r--jsonschema/validators.py46
2 files changed, 35 insertions, 12 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d10f50f..595ce1f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,6 +3,7 @@ v4.3.0
* Fix undesired fallback to brute force container uniqueness check on
certain input types (#893)
+* Resolving refs has had performance improvements (#893)
* Implement a PEP544 Protocol for validator classes (#890)
v4.2.1
diff --git a/jsonschema/validators.py b/jsonschema/validators.py
index e037c4b..936d723 100644
--- a/jsonschema/validators.py
+++ b/jsonschema/validators.py
@@ -169,6 +169,7 @@ def create(
schema = attr.ib(repr=reprlib.repr)
resolver = attr.ib(default=None, repr=False)
format_checker = attr.ib(default=None)
+ evolve = attr.evolve
def __attrs_post_init__(self):
if self.resolver is None:
@@ -182,9 +183,6 @@ def create(
for error in cls(cls.META_SCHEMA).iter_errors(schema):
raise exceptions.SchemaError.create_from(error)
- def evolve(self, **kwargs):
- return attr.evolve(self, **kwargs)
-
def iter_errors(self, instance, _schema=None):
if _schema is not None:
warnings.warn(
@@ -757,6 +755,10 @@ class RefResolver(object):
finally:
self.pop_scope()
+ @lru_cache()
+ def _find_in_referrer(self, key):
+ return list(self._finditem(self.referrer, key))
+
def _finditem(self, schema, key):
values = deque([schema])
while values:
@@ -767,15 +769,17 @@ class RefResolver(object):
yield each
values.extendleft(each.values())
- def resolve(self, ref):
- """
- Resolve the given reference.
- """
- url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/")
+ @lru_cache()
+ def _find_subschemas(self):
+ return list(self._finditem(self.referrer, "$id"))
+ @lru_cache()
+ def _find_in_subschemas(self, url):
+ subschemas = self._find_subschemas()
+ if not subschemas:
+ return None
uri, fragment = urldefrag(url)
-
- for subschema in self._finditem(self.referrer, "$id"):
+ for subschema in subschemas:
target_uri = self._urljoin_cache(
self.resolution_scope, subschema["$id"],
)
@@ -783,6 +787,17 @@ class RefResolver(object):
if fragment:
subschema = self.resolve_fragment(subschema, fragment)
return url, subschema
+ return None
+
+ def resolve(self, ref):
+ """
+ Resolve the given reference.
+ """
+ url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/")
+
+ match = self._find_in_subschemas(url)
+ if match is not None:
+ return match
return url, self._remote_cache(url)
@@ -821,12 +836,19 @@ class RefResolver(object):
if not fragment:
return document
+ if document is self.referrer:
+ find = self._find_in_referrer
+ else:
+
+ def find(key):
+ return self._finditem(document, key)
+
for keyword in ["$anchor", "$dynamicAnchor"]:
- for subschema in self._finditem(document, keyword):
+ for subschema in find(keyword):
if fragment == subschema[keyword]:
return subschema
for keyword in ["id", "$id"]:
- for subschema in self._finditem(document, keyword):
+ for subschema in find(keyword):
if "#" + fragment == subschema[keyword]:
return subschema