@@ -570,6 +570,20 @@ def __init__( # noqa: PLR0913, PLR0917
570570 # Only use suffixes when explicitly provided via --duplicate-name-suffix
571571 self .duplicate_name_suffix_map : dict [str , str ] = duplicate_name_suffix_map or {}
572572
573+ # Cache for reference names to avoid O(n) set creation on every _get_unique_name call
574+ self ._reference_names_cache : set [str ] | None = None
575+
576+ def _get_reference_names (self ) -> set [str ]:
577+ """Get cached set of all reference names for uniqueness checking."""
578+ if self ._reference_names_cache is not None :
579+ return self ._reference_names_cache # pragma: no cover
580+ self ._reference_names_cache = {r .name for r in self .references .values ()}
581+ return self ._reference_names_cache
582+
583+ def _invalidate_reference_names_cache (self ) -> None :
584+ """Invalidate the reference names cache when references change."""
585+ self ._reference_names_cache = None
586+
573587 @property
574588 def current_base_path (self ) -> Path | None :
575589 """Return the current base path for file resolution."""
@@ -788,17 +802,27 @@ def add_ref(self, ref: str, resolved: bool = False) -> Reference: # noqa: FBT00
788802 )
789803
790804 self .references [path ] = reference
805+ self ._invalidate_reference_names_cache ()
791806 return reference
792807
808+ def _find_parent_reference (self , path : Sequence [str ]) -> Reference | None :
809+ """Find the closest parent reference for a given path.
810+
811+ Traverses up the path hierarchy to find the first existing parent reference.
812+ Returns None if no parent reference is found.
813+ """
814+ parent_path = list (path [:- 1 ])
815+ while parent_path :
816+ if parent_reference := self .references .get (self .join_path (parent_path )):
817+ return parent_reference
818+ parent_path = parent_path [:- 1 ]
819+ return None
820+
793821 def _check_parent_scope_option (self , name : str , path : Sequence [str ]) -> str :
794822 # Check for parent-prefixed naming via either the legacy flag or the new naming strategy
795823 use_parent_prefix = self .parent_scoped_naming or self .naming_strategy == NamingStrategy .ParentPrefixed
796- if use_parent_prefix :
797- parent_path = path [:- 1 ]
798- while parent_path :
799- if parent_reference := self .references .get (self .join_path (parent_path )):
800- return f"{ parent_reference .name } _{ name } "
801- parent_path = parent_path [:- 1 ]
824+ if use_parent_prefix and (parent_ref := self ._find_parent_reference (path )):
825+ return f"{ parent_ref .name } _{ name } "
802826 return name
803827
804828 def _apply_full_path_naming (self , name : str , path : Sequence [str ]) -> str :
@@ -811,12 +835,9 @@ def _apply_full_path_naming(self, name: str, path: Sequence[str]) -> str:
811835 return name
812836
813837 # Find the immediate parent reference to prefix the name
814- parent_path = path [:- 1 ]
815- while parent_path :
816- if parent_reference := self .references .get (self .join_path (parent_path )):
817- # Use immediate parent's name (CamelCase join without underscore)
818- return f"{ parent_reference .name } { snake_to_upper_camel (name )} "
819- parent_path = parent_path [:- 1 ]
838+ if parent_ref := self ._find_parent_reference (path ):
839+ # Use immediate parent's name (CamelCase join without underscore)
840+ return f"{ parent_ref .name } { snake_to_upper_camel (name )} "
820841
821842 return name
822843
@@ -856,6 +877,7 @@ def _rename_external_ref_with_same_name(self, name: str, current_path: str) -> N
856877 new_name = self ._get_unique_name (name , camel = True )
857878 ref .duplicate_name = ref .name
858879 ref .name = new_name
880+ self ._invalidate_reference_names_cache ()
859881 break
860882
861883 def add ( # noqa: PLR0913
@@ -921,6 +943,7 @@ def add( # noqa: PLR0913
921943 reference .name = name
922944 reference .loaded = loaded
923945 reference .duplicate_name = duplicate_name
946+ self ._invalidate_reference_names_cache ()
924947 else :
925948 reference = Reference (
926949 path = joined_path ,
@@ -930,6 +953,7 @@ def add( # noqa: PLR0913
930953 duplicate_name = duplicate_name ,
931954 )
932955 self .references [joined_path ] = reference
956+ self ._invalidate_reference_names_cache ()
933957 return reference
934958
935959 def get (self , path : Sequence [str ] | str ) -> Reference | None :
@@ -941,6 +965,7 @@ def delete(self, path: Sequence[str] | str) -> None:
941965 resolved = self .resolve_ref (path )
942966 if resolved in self .references :
943967 del self .references [resolved ]
968+ self ._invalidate_reference_names_cache ()
944969
945970 def default_class_name_generator (self , name : str ) -> str :
946971 """Generate a valid class name from a string."""
@@ -989,7 +1014,8 @@ def get_class_name(
9891014 def _get_unique_name (self , name : str , camel : bool = False , model_type : str = "model" ) -> str : # noqa: FBT001, FBT002
9901015 unique_name : str = name
9911016 count : int = 0 if self .remove_suffix_number else 1
992- reference_names = {r .name for r in self .references .values ()} | self .exclude_names
1017+ # Use cached reference names for O(1) lookup instead of O(n) set creation
1018+ reference_names = self ._get_reference_names () | self .exclude_names
9931019
9941020 # Determine the suffix to use
9951021 suffix = self ._get_suffix_for_model_type (model_type )
0 commit comments