Skip to content

Commit 68b0454

Browse files
committed
perf: Cache subschemas
1 parent 02ca9fa commit 68b0454

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

jsonschema/validators.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -755,27 +755,21 @@ def resolving(self, ref):
755755
finally:
756756
self.pop_scope()
757757

758-
@lru_cache()
759758
def _find_in_referrer(self, key):
760-
return list(self._finditem(self.referrer, key))
761-
762-
def _finditem(self, schema, key):
763-
values = deque([schema])
764-
while values:
765-
each = values.pop()
766-
if not isinstance(each, dict):
767-
continue
768-
if key in each:
769-
yield each
770-
values.extendleft(each.values())
759+
return self._get_subschemas_cache()[key]
771760

772761
@lru_cache()
773-
def _find_subschemas(self):
774-
return list(self._finditem(self.referrer, "$id"))
762+
def _get_subschemas_cache(self):
763+
cache = {key: [] for key in SUBSCHEMAS_KEYWORDS}
764+
for keyword, subschema in _search_schema(
765+
self.referrer, _match_subschema_keywords,
766+
):
767+
cache[keyword].append(subschema)
768+
return cache
775769

776770
@lru_cache()
777771
def _find_in_subschemas(self, url):
778-
subschemas = self._find_subschemas()
772+
subschemas = self._get_subschemas_cache()["$id"]
779773
if not subschemas:
780774
return None
781775
uri, fragment = urldefrag(url)
@@ -841,7 +835,7 @@ def resolve_fragment(self, document, fragment):
841835
else:
842836

843837
def find(key):
844-
return self._finditem(document, key)
838+
yield from _search_schema(document, _match_keyword(key))
845839

846840
for keyword in ["$anchor", "$dynamicAnchor"]:
847841
for subschema in find(keyword):
@@ -924,6 +918,35 @@ def resolve_remote(self, uri):
924918
return result
925919

926920

921+
SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor")
922+
923+
924+
def _match_keyword(keyword):
925+
926+
def matcher(value):
927+
if keyword in value:
928+
yield value
929+
930+
return matcher
931+
932+
933+
def _match_subschema_keywords(value):
934+
for keyword in SUBSCHEMAS_KEYWORDS:
935+
if keyword in value:
936+
yield keyword, value
937+
938+
939+
def _search_schema(schema, matcher):
940+
"""Breadth-first search routine."""
941+
values = deque([schema])
942+
while values:
943+
value = values.pop()
944+
if not isinstance(value, dict):
945+
continue
946+
yield from matcher(value)
947+
values.extendleft(value.values())
948+
949+
927950
def validate(instance, schema, cls=None, *args, **kwargs):
928951
"""
929952
Validate an instance under the given schema.

0 commit comments

Comments
 (0)