diff options
author | Julian Berman <Julian@GrayVines.com> | 2021-12-16 19:54:07 -0500 |
---|---|---|
committer | Julian Berman <Julian@GrayVines.com> | 2021-12-16 19:54:07 -0500 |
commit | d5c4d07ddddf34ad5c9f96982c192b5f71bb85a8 (patch) | |
tree | 24c056ab1fe14993608aaa63bdf2edc4bb269a01 | |
parent | 0878727e6dc39f1f9268a626a6b6390b2dde2f4a (diff) | |
parent | 251763a6006e2c270629af199d350452ef7a6ba6 (diff) | |
download | jsonschema-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.rst | 1 | ||||
-rw-r--r-- | jsonschema/validators.py | 46 |
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 |