Skip to content

Commit d5c4d07

Browse files
committed
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
2 parents 0878727 + 251763a commit d5c4d07

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ v4.3.0
33

44
* Fix undesired fallback to brute force container uniqueness check on
55
certain input types (#893)
6+
* Resolving refs has had performance improvements (#893)
67
* Implement a PEP544 Protocol for validator classes (#890)
78

89
v4.2.1

jsonschema/validators.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class Validator:
169169
schema = attr.ib(repr=reprlib.repr)
170170
resolver = attr.ib(default=None, repr=False)
171171
format_checker = attr.ib(default=None)
172+
evolve = attr.evolve
172173

173174
def __attrs_post_init__(self):
174175
if self.resolver is None:
@@ -182,9 +183,6 @@ def check_schema(cls, schema):
182183
for error in cls(cls.META_SCHEMA).iter_errors(schema):
183184
raise exceptions.SchemaError.create_from(error)
184185

185-
def evolve(self, **kwargs):
186-
return attr.evolve(self, **kwargs)
187-
188186
def iter_errors(self, instance, _schema=None):
189187
if _schema is not None:
190188
warnings.warn(
@@ -757,6 +755,10 @@ def resolving(self, ref):
757755
finally:
758756
self.pop_scope()
759757

758+
@lru_cache()
759+
def _find_in_referrer(self, key):
760+
return list(self._finditem(self.referrer, key))
761+
760762
def _finditem(self, schema, key):
761763
values = deque([schema])
762764
while values:
@@ -767,22 +769,35 @@ def _finditem(self, schema, key):
767769
yield each
768770
values.extendleft(each.values())
769771

770-
def resolve(self, ref):
771-
"""
772-
Resolve the given reference.
773-
"""
774-
url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/")
772+
@lru_cache()
773+
def _find_subschemas(self):
774+
return list(self._finditem(self.referrer, "$id"))
775775

776+
@lru_cache()
777+
def _find_in_subschemas(self, url):
778+
subschemas = self._find_subschemas()
779+
if not subschemas:
780+
return None
776781
uri, fragment = urldefrag(url)
777-
778-
for subschema in self._finditem(self.referrer, "$id"):
782+
for subschema in subschemas:
779783
target_uri = self._urljoin_cache(
780784
self.resolution_scope, subschema["$id"],
781785
)
782786
if target_uri.rstrip("/") == uri.rstrip("/"):
783787
if fragment:
784788
subschema = self.resolve_fragment(subschema, fragment)
785789
return url, subschema
790+
return None
791+
792+
def resolve(self, ref):
793+
"""
794+
Resolve the given reference.
795+
"""
796+
url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/")
797+
798+
match = self._find_in_subschemas(url)
799+
if match is not None:
800+
return match
786801

787802
return url, self._remote_cache(url)
788803

@@ -821,12 +836,19 @@ def resolve_fragment(self, document, fragment):
821836
if not fragment:
822837
return document
823838

839+
if document is self.referrer:
840+
find = self._find_in_referrer
841+
else:
842+
843+
def find(key):
844+
return self._finditem(document, key)
845+
824846
for keyword in ["$anchor", "$dynamicAnchor"]:
825-
for subschema in self._finditem(document, keyword):
847+
for subschema in find(keyword):
826848
if fragment == subschema[keyword]:
827849
return subschema
828850
for keyword in ["id", "$id"]:
829-
for subschema in self._finditem(document, keyword):
851+
for subschema in find(keyword):
830852
if "#" + fragment == subschema[keyword]:
831853
return subschema
832854

0 commit comments

Comments
 (0)