@@ -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+
927950def validate (instance , schema , cls = None , * args , ** kwargs ):
928951 """
929952 Validate an instance under the given schema.
0 commit comments