python-introspect 0.1.3__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {python_introspect-0.1.3/src/python_introspect.egg-info → python_introspect-0.1.4}/PKG-INFO +1 -1
  2. {python_introspect-0.1.3 → python_introspect-0.1.4}/pyproject.toml +3 -3
  3. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect/__init__.py +1 -1
  4. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect/unified_parameter_analyzer.py +39 -4
  5. {python_introspect-0.1.3 → python_introspect-0.1.4/src/python_introspect.egg-info}/PKG-INFO +1 -1
  6. {python_introspect-0.1.3 → python_introspect-0.1.4}/LICENSE +0 -0
  7. {python_introspect-0.1.3 → python_introspect-0.1.4}/README.md +0 -0
  8. {python_introspect-0.1.3 → python_introspect-0.1.4}/setup.cfg +0 -0
  9. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect/enableable.py +0 -0
  10. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect/exceptions.py +0 -0
  11. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect/signature_analyzer.py +0 -0
  12. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect.egg-info/SOURCES.txt +0 -0
  13. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect.egg-info/dependency_links.txt +0 -0
  14. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect.egg-info/requires.txt +0 -0
  15. {python_introspect-0.1.3 → python_introspect-0.1.4}/src/python_introspect.egg-info/top_level.txt +0 -0
  16. {python_introspect-0.1.3 → python_introspect-0.1.4}/tests/test_exceptions.py +0 -0
  17. {python_introspect-0.1.3 → python_introspect-0.1.4}/tests/test_init.py +0 -0
  18. {python_introspect-0.1.3 → python_introspect-0.1.4}/tests/test_signature_analyzer.py +0 -0
  19. {python_introspect-0.1.3 → python_introspect-0.1.4}/tests/test_unified_parameter_analyzer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-introspect
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Pure Python introspection toolkit for function signatures, dataclasses, and type hints
5
5
  Author-email: Tristan Simas <tristan.simas@mail.mcgill.ca>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-introspect"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "Pure Python introspection toolkit for function signatures, dataclasses, and type hints"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -58,14 +58,14 @@ python_functions = ["test_*"]
58
58
 
59
59
  [tool.ruff]
60
60
  line-length = 100
61
- target-version = "0.1.3"
61
+ target-version = "0.1.4"
62
62
 
63
63
  [tool.black]
64
64
  line-length = 100
65
65
  target-version = ["py39"]
66
66
 
67
67
  [tool.mypy]
68
- python_version = "0.1.3"
68
+ python_version = "0.1.4"
69
69
  warn_return_any = true
70
70
  warn_unused_configs = true
71
71
  disallow_untyped_defs = true
@@ -9,7 +9,7 @@ Extensibility:
9
9
  type resolution for framework-specific types (lazy configs, proxies, etc.)
10
10
  """
11
11
 
12
- __version__ = "0.1.3"
12
+ __version__ = "0.1.4"
13
13
 
14
14
  from .signature_analyzer import (
15
15
  SignatureAnalyzer,
@@ -152,12 +152,22 @@ class UnifiedParameterAnalyzer:
152
152
  Always returns CLASS signature defaults (not instance values).
153
153
  ObjectState extracts instance values separately via object.__getattribute__.
154
154
 
155
+ For dynamic containers like SimpleNamespace (which use **kwargs in __init__),
156
+ falls back to inspecting __dict__ to discover attributes and their types.
157
+
155
158
  Args:
156
159
  instance: Object instance to analyze
157
160
  """
161
+ from types import SimpleNamespace
162
+ import logging
163
+ _logger = logging.getLogger(__name__)
164
+
158
165
  # Use MRO to get all constructor parameters from the inheritance chain
159
166
  instance_class = type(instance)
160
167
  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__]}")
161
171
 
162
172
  # Traverse MRO from most specific to most general (like dual-axis resolver)
163
173
  for cls in instance_class.__mro__:
@@ -176,10 +186,15 @@ class UnifiedParameterAnalyzer:
176
186
  if 'self' in class_params:
177
187
  del class_params['self']
178
188
 
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")
183
198
  continue
184
199
 
185
200
  # Add parameters that haven't been seen yet (most specific wins)
@@ -200,6 +215,26 @@ class UnifiedParameterAnalyzer:
200
215
  # in MRO might not have analyzable constructors (e.g., ABC, object)
201
216
  continue
202
217
 
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
+
203
238
  return all_params
204
239
 
205
240
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-introspect
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Pure Python introspection toolkit for function signatures, dataclasses, and type hints
5
5
  Author-email: Tristan Simas <tristan.simas@mail.mcgill.ca>
6
6
  License: MIT