@@ -152,12 +152,22 @@ def _analyze_object_instance(instance: object) -> Dict[str, UnifiedParameterInfo
152152 Always returns CLASS signature defaults (not instance values).
153153 ObjectState extracts instance values separately via object.__getattribute__.
154154
155+ For dynamic containers like SimpleNamespace (which use **kwargs in __init__),
156+ falls back to inspecting __dict__ to discover attributes and their types.
157+
155158 Args:
156159 instance: Object instance to analyze
157160 """
161+ from types import SimpleNamespace
162+ import logging
163+ _logger = logging .getLogger (__name__ )
164+
158165 # Use MRO to get all constructor parameters from the inheritance chain
159166 instance_class = type (instance )
160167 all_params = {}
168+ found_kwargs_only = False
169+
170+ _logger .debug (f"🔧 _analyze_object_instance: instance_class={ instance_class .__name__ } , MRO={ [c .__name__ for c in instance_class .__mro__ ]} " )
161171
162172 # Traverse MRO from most specific to most general (like dual-axis resolver)
163173 for cls in instance_class .__mro__ :
@@ -176,10 +186,15 @@ def _analyze_object_instance(instance: object) -> Dict[str, UnifiedParameterInfo
176186 if 'self' in class_params :
177187 del class_params ['self' ]
178188
179- # Special handling for **kwargs - if we see 'kwargs', skip this class
180- # and let parent classes provide the actual parameters
181- if 'kwargs' in class_params and len (class_params ) <= 2 :
182- # This class uses **kwargs, skip it and let parent classes define parameters
189+ _logger .debug (f"🔧 _analyze_object_instance: cls={ cls .__name__ } , class_params after removing self={ list (class_params .keys ())} " )
190+
191+ # Special handling for *args/**kwargs - if params are only args/kwargs, skip this class
192+ # This handles dynamic containers like SimpleNamespace(self, /, *args, **kwargs)
193+ variadic_only = set (class_params .keys ()) <= {'args' , 'kwargs' }
194+ if variadic_only and class_params :
195+ # This class uses only *args/**kwargs, skip it and use __dict__ fallback
196+ found_kwargs_only = True
197+ _logger .debug (f"🔧 _analyze_object_instance: cls={ cls .__name__ } has only variadic params { list (class_params .keys ())} , skipping, found_kwargs_only=True" )
183198 continue
184199
185200 # Add parameters that haven't been seen yet (most specific wins)
@@ -200,6 +215,26 @@ def _analyze_object_instance(instance: object) -> Dict[str, UnifiedParameterInfo
200215 # in MRO might not have analyzable constructors (e.g., ABC, object)
201216 continue
202217
218+ # Fallback for dynamic containers (SimpleNamespace, etc.): inspect __dict__
219+ # This handles objects that store attrs via **kwargs and have no static signature
220+ _logger .debug (f"🔧 _analyze_object_instance: after MRO loop, all_params={ list (all_params .keys ())} , found_kwargs_only={ found_kwargs_only } " )
221+ if not all_params and found_kwargs_only and hasattr (instance , '__dict__' ):
222+ _logger .debug (f"🔧 _analyze_object_instance: FALLBACK triggered, inspecting __dict__={ list (instance .__dict__ .keys ())} " )
223+ for attr_name , attr_value in instance .__dict__ .items ():
224+ if attr_name .startswith ('_' ):
225+ continue
226+ # Infer type from value
227+ attr_type = type (attr_value ) if attr_value is not None else type (None )
228+ all_params [attr_name ] = UnifiedParameterInfo (
229+ name = attr_name ,
230+ param_type = attr_type ,
231+ default_value = attr_value ,
232+ is_required = False ,
233+ description = None ,
234+ source_type = "dynamic_attr"
235+ )
236+ _logger .debug (f"🔧 _analyze_object_instance: after fallback, all_params={ list (all_params .keys ())} " )
237+
203238 return all_params
204239
205240 @staticmethod
0 commit comments