@@ -755,27 +755,21 @@ def resolving(self, ref):
755
755
finally :
756
756
self .pop_scope ()
757
757
758
- @lru_cache ()
759
758
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 ]
771
760
772
761
@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
775
769
776
770
@lru_cache ()
777
771
def _find_in_subschemas (self , url ):
778
- subschemas = self ._find_subschemas ()
772
+ subschemas = self ._get_subschemas_cache ()[ "$id" ]
779
773
if not subschemas :
780
774
return None
781
775
uri , fragment = urldefrag (url )
@@ -841,7 +835,7 @@ def resolve_fragment(self, document, fragment):
841
835
else :
842
836
843
837
def find (key ):
844
- return self . _finditem (document , key )
838
+ yield from _search_schema (document , _match_keyword ( key ) )
845
839
846
840
for keyword in ["$anchor" , "$dynamicAnchor" ]:
847
841
for subschema in find (keyword ):
@@ -924,6 +918,35 @@ def resolve_remote(self, uri):
924
918
return result
925
919
926
920
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
+
927
950
def validate (instance , schema , cls = None , * args , ** kwargs ):
928
951
"""
929
952
Validate an instance under the given schema.
0 commit comments