@@ -13,8 +13,10 @@ class Type_Safe__Step__Class_Kwargs:
1313 def __init__ (self ):
1414 self .type_safe_cache = type_safe_cache # Initialize with singleton cache
1515
16- def get_cls_kwargs (self , cls : Type )\
17- -> Dict [str , Any ]: # Main entry point for getting class kwargs, returns dict of class kwargs
16+ def get_cls_kwargs (self , # Main entry point for getting class kwargs, returns dict of class kwargs
17+ cls : Type ,
18+ provided_kwargs : Dict [str , Any ] = None # kwargs that were provided on the __init__
19+ )-> Dict [str , Any ]:
1820
1921 if not hasattr (cls , '__mro__' ): # Handle non-class inputs
2022 return {}
@@ -29,9 +31,9 @@ def get_cls_kwargs(self, cls : Type )\
2931 base_classes = type_safe_cache .get_class_mro (cls )
3032 for base_cls in base_classes :
3133 self .process_mro_class (base_cls , kwargs ) # Handle each class in MRO
32- self .process_annotations (cls , base_cls , kwargs ) # Process its annotations
34+ self .process_annotations (cls , base_cls , kwargs , provided_kwargs ) # Process its annotations
3335
34- if self .is_kwargs_cacheable (cls , kwargs ): # if we can cache it (i.e. only IMMUTABLE_TYPES vars)
36+ if not provided_kwargs and self .is_kwargs_cacheable (cls , kwargs ): # Only cache if no provided_kwargs were used and if we can cache it (i.e. only IMMUTABLE_TYPES vars)
3537 type_safe_cache .set_cache__cls_kwargs (cls , kwargs ) # cache it
3638 # else:
3739 # pass # todo:: see how we can cache more the cases when the data is clean (i.e. default values)
@@ -49,30 +51,28 @@ def is_kwargs_cacheable(self, cls, kwargs: Dict[str, Any]) -> bool:
4951 return match
5052
5153
52- def handle_undefined_var (self , cls : Type , # Handle undefined class variables
53- kwargs : Dict [str , Any ] ,
54- var_name : str ,
55- var_type : Type )\
56- -> None :
57- if var_name in kwargs : # Skip if already defined
54+ def handle_undefined_var (self , cls : Type , # Handle undefined class variables
55+ kwargs : Dict [str , Any ] ,
56+ var_name : str ,
57+ var_type : Type ,
58+ provided_kwargs : Dict [str , Any ] = None
59+ )-> None :
60+
61+ if var_name in kwargs :
5862 return
59- var_value = type_safe_step_default_value .default_value (cls , var_type ) # Get default value
60- kwargs [var_name ] = var_value # Store in kwargs
63+ if provided_kwargs and var_name in provided_kwargs : # Skip if caller already provided this value
64+ from osbot_utils .type_safe .Type_Safe import Type_Safe
65+ if isinstance (var_type , type ) and issubclass (var_type , Type_Safe ): # this logic fixes quite a big performance bug with large objects, since without this, we would be calculating the default values for objects that we already have the value since they were provided in the kwargs
66+ kwargs [var_name ] = None # Placeholder - actual value comes from provided_kwargs
67+ return
68+
69+ var_value = type_safe_step_default_value .default_value (cls , var_type )
70+ kwargs [var_name ] = var_value
6171
6272 def handle_defined_var (self , base_cls : Type , # Handle defined class variables
6373 var_name : str ,
6474 var_type : Type )\
6575 -> None :
66- # var_value = getattr(base_cls, var_name) # Get current value
67- # if var_value is None: # Allow None assignments
68- # return
69- #
70- # if type_safe_validation.should_skip_type_check(var_type): # Skip validation if needed
71- # return
72- #
73- # type_safe_validation.validate_variable_type (var_name, var_type, var_value) # Validate type
74- # type_safe_validation.validate_type_immutability(var_name, var_type) # Validate immutability
75-
7676 var_value = getattr (base_cls , var_name )
7777 if var_value is None :
7878 return
@@ -97,35 +97,38 @@ def handle_defined_var(self, base_cls : Type ,
9797 type_safe_validation .validate_variable_type (base_cls , var_name , var_type , var_value )
9898 type_safe_validation .validate_type_immutability (var_name , var_type )
9999
100- def process_annotation (self , cls : Type ,
101- base_cls : Type ,
102- kwargs : Dict [str , Any ] ,
103- var_name : str ,
104- var_type : Type ):
105-
106- class_declares_annotation = var_name in getattr (base_cls , '__annotations__' , {}) # Check if this class has the annotation in its own __annotations__
107- class_has_own_value = var_name in base_cls .__dict__ # Check if this class defines its own value (not inherited)
108-
109- if not hasattr (base_cls , var_name ): # Case 1: No value exists anywhere in hierarchy
110- self .handle_undefined_var (cls , kwargs , var_name , var_type ) # Create fresh default value for this type
111- elif class_declares_annotation and base_cls is cls and not class_has_own_value : # Case 2: Target class redeclares annotation without own value
112- self .handle_undefined_var (cls , kwargs , var_name , var_type ) # Create fresh default, don't inherit parent's explicit None
113- elif class_declares_annotation and base_cls is cls : # Case 3: Target class declares annotation with its own value
114- origin = type_safe_cache .get_origin (var_type ) # Check if it's a Type[T] annotation
115- if origin is type : # Type[T] annotations need special handling
116- self .handle_undefined_var (cls , kwargs , var_name , var_type ) # Recalculate default for Type[T]
117- else : # Normal annotation with explicit value
118- self .handle_defined_var (base_cls , var_name , var_type ) # Validate the defined value
119- else : # Case 4: Inherited value from parent class
120- self .handle_defined_var (base_cls , var_name , var_type ) # Use and validate the inherited value
121-
122- def process_annotations (self , cls : Type , # Process all annotations
123- base_cls : Type ,
124- kwargs : Dict [str , Any ] )\
125- -> None :
126- if hasattr (base_cls , '__annotations__' ): # Process if annotations exist
100+ def process_annotation (self , cls : Type ,
101+ base_cls : Type ,
102+ kwargs : Dict [str , Any ] ,
103+ var_name : str ,
104+ var_type : Type ,
105+ provided_kwargs : Dict [str , Any ] = None
106+ ) -> None :
107+ class_declares_annotation = var_name in getattr (base_cls , '__annotations__' , {}) # Check if this class has the annotation in its own __annotations__
108+ class_has_own_value = var_name in base_cls .__dict__ # Check if this class defines its own value (not inherited)
109+
110+ if not hasattr (base_cls , var_name ): # Case 1: No value exists anywhere in hierarchy
111+ self .handle_undefined_var (cls , kwargs , var_name , var_type , provided_kwargs ) # Create fresh default value for this type
112+ elif class_declares_annotation and base_cls is cls and not class_has_own_value : # Case 2: Target class redeclares annotation without own value
113+ self .handle_undefined_var (cls , kwargs , var_name , var_type , provided_kwargs ) # Create fresh default, don't inherit parent's explicit None
114+ elif class_declares_annotation and base_cls is cls : # Case 3: Target class declares annotation with its own value
115+ origin = type_safe_cache .get_origin (var_type ) # Check if it's a Type[T] annotation
116+ if origin is type : # Type[T] annotations need special handling
117+ self .handle_undefined_var (cls , kwargs , var_name , var_type , provided_kwargs ) # Recalculate default for Type[T] (if value has not been provided)
118+ else : # Normal annotation with explicit value
119+ self .handle_defined_var (base_cls , var_name , var_type ) # Validate the defined value
120+ else : # Case 4: Inherited value from parent class
121+ self .handle_defined_var (base_cls , var_name , var_type ) # Use and validate the inherited value
122+
123+ def process_annotations (self , cls : Type , # Process all annotations
124+ base_cls : Type ,
125+ kwargs : Dict [str , Any ] ,
126+ provided_kwargs : Dict [str , Any ] = None
127+ ) -> None :
128+
129+ if hasattr (base_cls , '__annotations__' ):
127130 for var_name , var_type in type_safe_cache .get_class_annotations (base_cls ):
128- self .process_annotation (cls , base_cls , kwargs , var_name , var_type )
131+ self .process_annotation (cls , base_cls , kwargs , var_name , var_type , provided_kwargs )
129132
130133
131134 def process_mro_class (self , base_cls : Type , # Process class in MRO chain
0 commit comments