@@ -901,6 +901,7 @@ def __init__(
901901 self ._dynamic_anchor_index : dict [tuple [str , ...], dict [str , str ]] = {}
902902 self ._recursive_anchor_index : dict [tuple [str , ...], list [str ]] = {}
903903 self ._ref_data_type_facts : dict [str , tuple [Any , bool ]] = {}
904+ self ._ref_schema_object_cache : dict [str , JsonSchemaObject ] = {}
904905 self ._force_base_model_refs : set [str ] = set ()
905906 self ._force_base_model_generation = False
906907 self .field_keys : set [str ] = {
@@ -2462,6 +2463,9 @@ def _deep_merge(self, dict1: dict[Any, Any], dict2: dict[Any, Any]) -> dict[Any,
24622463 def _load_ref_schema_object (self , ref : str ) -> JsonSchemaObject :
24632464 """Load a JsonSchemaObject from a $ref using standard resolve/load pipeline."""
24642465 resolved_ref = self .model_resolver .resolve_ref (ref )
2466+ if (cached := self ._ref_schema_object_cache .get (resolved_ref )) is not None :
2467+ return cached
2468+
24652469 file_part , fragment = ([* resolved_ref .split ("#" , 1 ), "" ])[:2 ]
24662470 raw_doc = self ._get_ref_body (file_part ) if file_part else self .raw_obj
24672471
@@ -2470,7 +2474,9 @@ def _load_ref_schema_object(self, ref: str) -> JsonSchemaObject:
24702474 pointer = split_json_pointer (raw_doc , fragment )
24712475 target_schema = get_model_by_path (raw_doc , pointer )
24722476
2473- return self ._validate_schema_object (target_schema , [resolved_ref ])
2477+ ref_schema = self ._validate_schema_object (target_schema , [resolved_ref ])
2478+ self ._ref_schema_object_cache [resolved_ref ] = ref_schema
2479+ return ref_schema
24742480
24752481 def _anchor_ref_path (self , root_key : tuple [str , ...], path : list [str ]) -> str : # noqa: PLR6301
24762482 """Return the local ref path for an anchor under the current root."""
@@ -5486,14 +5492,24 @@ def parse_raw_obj(
54865492 path : list [str ],
54875493 ) -> None :
54885494 """Parse a raw dictionary into a JsonSchemaObject and process it."""
5495+ self ._parse_raw_or_validated_obj (name , raw , path )
5496+
5497+ def _parse_raw_or_validated_obj (
5498+ self ,
5499+ name : str ,
5500+ raw : dict [str , YamlValue ] | YamlValue ,
5501+ path : list [str ],
5502+ validated_obj : JsonSchemaObject | None = None ,
5503+ ) -> None :
5504+ """Parse a raw schema, reusing a validated object when available."""
54895505 if isinstance (raw , dict ) and "x-python-import" in raw :
54905506 self ._handle_python_import (name , path )
54915507 return
54925508
54935509 # Strict mode: check for version-specific features before validation
54945510 self ._check_version_specific_features (raw , path )
54955511
5496- obj = self ._validate_schema_object (raw , path )
5512+ obj = validated_obj if validated_obj is not None else self ._validate_schema_object (raw , path )
54975513 # Build $recursiveAnchor / $dynamicAnchor indexes for this schema
54985514 self ._build_anchor_indexes (obj , path )
54995515 self .parse_obj (name , obj , path )
@@ -5964,11 +5980,13 @@ def _parse_file( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
59645980 * definition_entries ,
59655981 ]
59665982 seen_definition_metadata_paths : set [tuple [str , ...]] = set ()
5983+ validated_definition_objects : dict [tuple [str , ...], JsonSchemaObject ] = {}
59675984 for _key , model , definition_path in definition_metadata_entries :
59685985 if (definition_path_key := tuple (definition_path )) in seen_definition_metadata_paths :
59695986 continue
59705987 seen_definition_metadata_paths .add (definition_path_key )
59715988 obj = self ._validate_schema_object (model , definition_path )
5989+ validated_definition_objects [definition_path_key ] = obj
59725990 self .parse_id (obj , definition_path )
59735991 if obj .recursiveAnchor :
59745992 ref_path = self ._anchor_ref_path (root_key , definition_path )
@@ -5986,7 +6004,12 @@ def _parse_file( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
59866004 for key , model , path in definition_entries :
59876005 reference = self .model_resolver .get (path )
59886006 if not reference or not reference .loaded :
5989- self .parse_raw_obj (key , model , path )
6007+ self ._parse_raw_or_validated_obj (
6008+ key ,
6009+ model ,
6010+ path ,
6011+ validated_definition_objects .get (tuple (path )),
6012+ )
59906013
59916014 key = tuple (path_parts )
59926015 reserved_refs = set (self .reserved_refs .get (key ) or [])
0 commit comments