1010#
1111# ═══════════════════════════════════════════════════════════════════════════════
1212
13- from typing import Any , Dict , Set , Type
13+ from typing import Any , Dict , Set , Type , get_type_hints , get_origin
1414from osbot_utils .type_safe .Type_Safe__Primitive import Type_Safe__Primitive
1515from osbot_utils .type_safe .type_safe_core .collections .Type_Safe__Dict import Type_Safe__Dict
1616from osbot_utils .type_safe .type_safe_core .collections .Type_Safe__List import Type_Safe__List
3131
3232class Type_Safe__Fast_Create__Cache : # Schema generation and caching
3333
34- schema_cache : Dict [Type , Schema__Type_Safe__Fast_Create__Class ] # Class -> Schema mapping
35- generating : Set [Type ] # Guards against recursion
34+ schema_cache : Dict [Type , Schema__Type_Safe__Fast_Create__Class ] # Class -> Schema mapping
35+ type_hints_cache : Dict [Type , Dict [str , Type ]] # Class -> type hints mapping
36+ generating : Set [Type ] # Guards against recursion
3637
3738 def __init__ (self ):
38- self .schema_cache = {} # Regular dict - classes persist
39- self .generating = set () # Recursion guard
39+ self .schema_cache = {} # Regular dict - classes persist
40+ self .type_hints_cache = {} # Cache for get_type_hints results
41+ self .generating = set () # Recursion guard
4042
4143 def __enter__ (self ):
4244 return self
@@ -61,6 +63,7 @@ def warm_cache(self, cls: Type) -> None: #
6163
6264 def clear_cache (self ) -> None : # Clear all cached schemas (for testing)
6365 self .schema_cache .clear ()
66+ self .type_hints_cache .clear ()
6467 self .generating .clear ()
6568
6669 def is_generating (self , cls : Type ) -> bool : # Check if schema generation in progress
@@ -76,6 +79,7 @@ def generate_schema(self, cls: Type) -> Schema__Type_Safe__Fast_Create__Class:
7679 try :
7780 template = cls () # Create template to get defaults
7881 template_dict = template .__dict__ .copy ()
82+ type_hints = self .get_type_hints_safe (cls ) # Get type annotations
7983
8084 fields = []
8185 static_dict = {}
@@ -86,7 +90,8 @@ def generate_schema(self, cls: Type) -> Schema__Type_Safe__Fast_Create__Class:
8690 if name .startswith ('_' ): # Skip private attributes
8791 continue
8892
89- field = self .classify_field (name , value )
93+ type_hint = type_hints .get (name ) # Get annotation for this field
94+ field = self .classify_field (name , value , type_hint )
9095 fields .append (field )
9196
9297 if field .mode == FIELD_MODE__STATIC :
@@ -104,8 +109,14 @@ def generate_schema(self, cls: Type) -> Schema__Type_Safe__Fast_Create__Class:
104109 finally :
105110 self .generating .discard (cls ) # Remove from generating set
106111
107- def classify_field (self , name : str , value : Any ) -> Schema__Type_Safe__Fast_Create__Field : # Classify field by its value type
112+ def classify_field (self , name : str , value : Any , type_hint : Type = None ) -> Schema__Type_Safe__Fast_Create__Field : # Classify field by its value type
108113 if self .is_immutable (value ):
114+ # Check if None value has a Type_Safe type hint (On_Demand pattern)
115+ if value is None and type_hint is not None :
116+ if self .is_type_safe_type_hint (type_hint ): # Type hint is a Type_Safe class
117+ return Schema__Type_Safe__Fast_Create__Field (name = name ,
118+ mode = FIELD_MODE__NESTED ,
119+ nested_class = type_hint )
109120 return Schema__Type_Safe__Fast_Create__Field (name = name ,
110121 mode = FIELD_MODE__STATIC ,
111122 static_value = value )
@@ -141,6 +152,32 @@ def is_nested_type_safe(self, value: Any) -> bool: #
141152 return False
142153 return True
143154
155+ def is_type_safe_type_hint (self , type_hint : Type ) -> bool : # Check if type hint is a Type_Safe class
156+ from osbot_utils .type_safe .Type_Safe import Type_Safe # Circular dependency
157+ if type_hint is None :
158+ return False
159+ if get_origin (type_hint ) is not None : # Skip generic types like Optional[X]
160+ return False
161+ if not isinstance (type_hint , type ): # Must be a class
162+ return False
163+ if not issubclass (type_hint , Type_Safe ): # Must be Type_Safe subclass
164+ return False
165+ if issubclass (type_hint , Type_Safe__Primitive ): # Skip primitives
166+ return False
167+ if issubclass (type_hint , (Type_Safe__List , Type_Safe__Dict , Type_Safe__Set )): # Skip collections
168+ return False
169+ return True
170+
171+ def get_type_hints_safe (self , cls : Type ) -> Dict [str , Type ]: # Get type hints with caching
172+ if cls in self .type_hints_cache :
173+ return self .type_hints_cache [cls ]
174+ try :
175+ hints = get_type_hints (cls )
176+ except Exception : # Some classes fail get_type_hints
177+ hints = {}
178+ self .type_hints_cache [cls ] = hints
179+ return hints
180+
144181 # ═══════════════════════════════════════════════════════════════════════════
145182 # Factory Function Generation
146183 # ═══════════════════════════════════════════════════════════════════════════
@@ -168,6 +205,12 @@ def get_factory_func(self, value: Any): #
168205 if isinstance (value , set ):
169206 return set
170207
208+ # Handle when value is a class/type itself
209+ if isinstance (value , type ): # Value IS a class
210+ captured_type = value # Capture for lambda
211+ return lambda : captured_type # Return class itself (not instance)
212+
213+
171214 value_type = type (value ) # Default: use type constructor
172215 return lambda : value_type ()
173216
0 commit comments