Skip to content

Commit

Permalink
Merge remote-tracking branch 'Stranger6667/dd/more-ref-cache'
Browse files Browse the repository at this point in the history
* Stranger6667/dd/more-ref-cache:
  perf: Cache subschemas
  • Loading branch information
Julian committed Dec 19, 2021
2 parents 02ca9fa + 68b0454 commit 359b33d
Showing 1 changed file with 39 additions and 16 deletions.
55 changes: 39 additions & 16 deletions jsonschema/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,27 +755,21 @@ def resolving(self, ref):
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:
each = values.pop()
if not isinstance(each, dict):
continue
if key in each:
yield each
values.extendleft(each.values())
return self._get_subschemas_cache()[key]

@lru_cache()
def _find_subschemas(self):
return list(self._finditem(self.referrer, "$id"))
def _get_subschemas_cache(self):
cache = {key: [] for key in SUBSCHEMAS_KEYWORDS}
for keyword, subschema in _search_schema(
self.referrer, _match_subschema_keywords,
):
cache[keyword].append(subschema)
return cache

@lru_cache()
def _find_in_subschemas(self, url):
subschemas = self._find_subschemas()
subschemas = self._get_subschemas_cache()["$id"]
if not subschemas:
return None
uri, fragment = urldefrag(url)
Expand Down Expand Up @@ -841,7 +835,7 @@ def resolve_fragment(self, document, fragment):
else:

def find(key):
return self._finditem(document, key)
yield from _search_schema(document, _match_keyword(key))

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


SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor")


def _match_keyword(keyword):

def matcher(value):
if keyword in value:
yield value

return matcher


def _match_subschema_keywords(value):
for keyword in SUBSCHEMAS_KEYWORDS:
if keyword in value:
yield keyword, value


def _search_schema(schema, matcher):
"""Breadth-first search routine."""
values = deque([schema])
while values:
value = values.pop()
if not isinstance(value, dict):
continue
yield from matcher(value)
values.extendleft(value.values())


def validate(instance, schema, cls=None, *args, **kwargs):
"""
Validate an instance under the given schema.
Expand Down

0 comments on commit 359b33d

Please sign in to comment.