@@ -1576,15 +1576,16 @@ def _build_anchor_indexes(self, obj: JsonSchemaObject, path: list[str]) -> None:
15761576 root_len = len (root_key )
15771577 if root_len < len (path ):
15781578 suffix_parts = path [root_len :]
1579- # Strip leading '#' from fragment markers (e.g. '#/$defs' -> '$defs')
15801579 first = suffix_parts [0 ]
15811580 if first .startswith ("#" ):
15821581 suffix_parts = [first [1 :].lstrip ("/" ), * suffix_parts [1 :]]
15831582 ref_path = "#/" + "/" .join (suffix_parts )
15841583 else :
15851584 ref_path = "#"
15861585 if obj .recursiveAnchor :
1587- self ._recursive_anchor_index .setdefault (root_key , []).append (ref_path )
1586+ anchors = self ._recursive_anchor_index .setdefault (root_key , [])
1587+ if ref_path not in anchors :
1588+ anchors .append (ref_path )
15881589 if obj .dynamicAnchor :
15891590 self ._dynamic_anchor_index .setdefault (root_key , {}).setdefault (obj .dynamicAnchor , ref_path )
15901591
@@ -1602,7 +1603,6 @@ def _resolve_recursive_ref(self, item: JsonSchemaObject, path: list[str]) -> str
16021603 anchors = self ._recursive_anchor_index .get (root_key , [])
16031604 if not anchors :
16041605 return "#"
1605- # Build root-relative path for comparison
16061606 root_len = len (root_key )
16071607 if root_len < len (path ):
16081608 suffix_parts = path [root_len :]
@@ -1612,8 +1612,6 @@ def _resolve_recursive_ref(self, item: JsonSchemaObject, path: list[str]) -> str
16121612 current_ref = "#/" + "/" .join (suffix_parts )
16131613 else :
16141614 current_ref = "#" # pragma: no cover
1615- # Find the best matching anchor: path prefix with longest match
1616- # best defaults to "#" (root anchor fallback)
16171615 best = "#"
16181616 best_len = 0
16191617 for anchor_ref in anchors :
@@ -3022,10 +3020,8 @@ def parse_item( # noqa: PLR0911, PLR0912, PLR0914, PLR0915
30223020 item ,
30233021 root_type_path ,
30243022 )
3025- # Resolve $recursiveRef to $ref (JSON Schema 2019-09)
30263023 if item .recursiveRef and not item .ref :
30273024 return self .get_ref_data_type (self ._resolve_recursive_ref (item , path ) or "#" )
3028- # Resolve $dynamicRef to $ref (JSON Schema 2020-12)
30293025 if item .dynamicRef and not item .ref :
30303026 return self .get_ref_data_type (self ._resolve_dynamic_ref (item ) or item .dynamicRef )
30313027 if item .is_ref_with_nullable_only and item .ref :
@@ -4151,11 +4147,9 @@ def _parse_file( # noqa: PLR0912, PLR0915
41514147 # parse $id before parsing $ref
41524148 root_obj = self ._validate_schema_object (raw , path_parts or ["#" ])
41534149 self .parse_id (root_obj , path_parts )
4154- # Build $recursiveAnchor index for root object
41554150 if root_obj .recursiveAnchor :
41564151 root_key = tuple (path_parts )
41574152 self ._recursive_anchor_index .setdefault (root_key , []).append ("#" )
4158- # Build $dynamicAnchor index for root object
41594153 if root_obj .dynamicAnchor :
41604154 root_key = tuple (path_parts )
41614155 self ._dynamic_anchor_index .setdefault (root_key , {}).setdefault (root_obj .dynamicAnchor , "#" )
@@ -4173,12 +4167,10 @@ def _parse_file( # noqa: PLR0912, PLR0915
41734167 definition_path = [* path_parts , schema_path , key ]
41744168 obj = self ._validate_schema_object (model , definition_path )
41754169 self .parse_id (obj , definition_path )
4176- # Build $recursiveAnchor index for definitions
41774170 if obj .recursiveAnchor :
41784171 root_key = tuple (path_parts )
41794172 ref_path = "#/" + schema_path .lstrip ("#/" ) + "/" + key
41804173 self ._recursive_anchor_index .setdefault (root_key , []).append (ref_path )
4181- # Build $dynamicAnchor index for definitions
41824174 if obj .dynamicAnchor :
41834175 root_key = tuple (path_parts )
41844176 ref_path = "#/" + schema_path .lstrip ("#/" ) + "/" + key
0 commit comments