Coverage for openhcs/textual_tui/widgets/shared/unified_parameter_analyzer.py: 0.0%
83 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
1"""Unified parameter analysis interface for all parameter sources in OpenHCS TUI.
3This module provides a single, consistent interface for analyzing parameters from:
4- Functions and methods
5- Dataclasses and their fields
6- Nested dataclass structures
7- Any callable or type with parameters
9Replaces the fragmented approach of SignatureAnalyzer vs FieldIntrospector.
10"""
12import inspect
13import dataclasses
14from typing import Dict, Union, Callable, Type, Any, Optional
15from dataclasses import dataclass
17from .signature_analyzer import SignatureAnalyzer, ParameterInfo, DocstringExtractor
20@dataclass
21class UnifiedParameterInfo:
22 """Unified parameter information that works for all parameter sources."""
23 name: str
24 param_type: Type
25 default_value: Any
26 is_required: bool
27 description: Optional[str] = None
28 source_type: str = "unknown" # "function", "dataclass", "nested"
30 @classmethod
31 def from_parameter_info(cls, param_info: ParameterInfo, source_type: str = "function") -> "UnifiedParameterInfo":
32 """Convert from existing ParameterInfo to unified format."""
33 return cls(
34 name=param_info.name,
35 param_type=param_info.param_type,
36 default_value=param_info.default_value,
37 is_required=param_info.is_required,
38 description=param_info.description,
39 source_type=source_type
40 )
43class UnifiedParameterAnalyzer:
44 """Single interface for analyzing parameters from any source.
46 This class provides a unified way to extract parameter information
47 from functions, dataclasses, and other parameter sources, ensuring
48 consistent behavior across the entire application.
49 """
51 @staticmethod
52 def analyze(target: Union[Callable, Type, object]) -> Dict[str, UnifiedParameterInfo]:
53 """Analyze parameters from any source.
55 Args:
56 target: Function, method, dataclass type, or instance to analyze
58 Returns:
59 Dictionary mapping parameter names to UnifiedParameterInfo objects
61 Examples:
62 # Function analysis
63 param_info = UnifiedParameterAnalyzer.analyze(my_function)
65 # Dataclass analysis
66 param_info = UnifiedParameterAnalyzer.analyze(MyDataclass)
68 # Instance analysis
69 param_info = UnifiedParameterAnalyzer.analyze(my_instance)
70 """
71 if target is None:
72 return {}
74 # Determine the type of target and route to appropriate analyzer
75 if inspect.isfunction(target) or inspect.ismethod(target):
76 return UnifiedParameterAnalyzer._analyze_callable(target)
77 elif inspect.isclass(target):
78 if dataclasses.is_dataclass(target):
79 return UnifiedParameterAnalyzer._analyze_dataclass_type(target)
80 else:
81 # Try to analyze constructor
82 return UnifiedParameterAnalyzer._analyze_callable(target.__init__)
83 elif dataclasses.is_dataclass(target):
84 # Instance of dataclass
85 return UnifiedParameterAnalyzer._analyze_dataclass_instance(target)
86 else:
87 # Try to analyze as callable
88 if callable(target):
89 return UnifiedParameterAnalyzer._analyze_callable(target)
90 else:
91 return {}
93 @staticmethod
94 def _analyze_callable(callable_obj: Callable) -> Dict[str, UnifiedParameterInfo]:
95 """Analyze a callable (function, method, etc.)."""
96 try:
97 # Use existing SignatureAnalyzer for callables
98 param_info_dict = SignatureAnalyzer.analyze(callable_obj)
100 # Convert to unified format
101 unified_params = {}
102 for name, param_info in param_info_dict.items():
103 unified_params[name] = UnifiedParameterInfo.from_parameter_info(
104 param_info,
105 source_type="function"
106 )
108 return unified_params
110 except Exception:
111 return {}
113 @staticmethod
114 def _analyze_dataclass_type(dataclass_type: Type) -> Dict[str, UnifiedParameterInfo]:
115 """Analyze a dataclass type."""
116 try:
117 # Extract docstring information
118 docstring_info = DocstringExtractor.extract(dataclass_type)
120 # Get field information
121 fields = dataclasses.fields(dataclass_type)
122 unified_params = {}
124 for field in fields:
125 # Get field description from docstring
126 field_description = docstring_info.parameters.get(field.name)
128 # Determine if field is required
129 is_required = field.default == dataclasses.MISSING and field.default_factory == dataclasses.MISSING
131 # Get default value
132 if field.default != dataclasses.MISSING:
133 default_value = field.default
134 elif field.default_factory != dataclasses.MISSING:
135 default_value = field.default_factory()
136 else:
137 default_value = None
139 unified_params[field.name] = UnifiedParameterInfo(
140 name=field.name,
141 param_type=field.type,
142 default_value=default_value,
143 is_required=is_required,
144 description=field_description,
145 source_type="dataclass"
146 )
148 return unified_params
150 except Exception:
151 return {}
153 @staticmethod
154 def _analyze_dataclass_instance(instance: object) -> Dict[str, UnifiedParameterInfo]:
155 """Analyze a dataclass instance."""
156 try:
157 # Get the type and analyze it
158 dataclass_type = type(instance)
159 unified_params = UnifiedParameterAnalyzer._analyze_dataclass_type(dataclass_type)
161 # Update default values with current instance values
162 for name, param_info in unified_params.items():
163 if hasattr(instance, name):
164 current_value = getattr(instance, name)
165 # Create new UnifiedParameterInfo with current value as default
166 unified_params[name] = UnifiedParameterInfo(
167 name=param_info.name,
168 param_type=param_info.param_type,
169 default_value=current_value,
170 is_required=param_info.is_required,
171 description=param_info.description,
172 source_type="dataclass_instance"
173 )
175 return unified_params
177 except Exception:
178 return {}
180 @staticmethod
181 def analyze_nested(target: Union[Callable, Type, object], parent_info: Dict[str, UnifiedParameterInfo] = None) -> Dict[str, UnifiedParameterInfo]:
182 """Analyze parameters with nested dataclass support.
184 This method provides enhanced analysis that can handle nested dataclasses
185 and maintain parent context information.
187 Args:
188 target: The target to analyze
189 parent_info: Optional parent parameter information for context
191 Returns:
192 Dictionary of unified parameter information with nested support
193 """
194 base_params = UnifiedParameterAnalyzer.analyze(target)
196 # For each parameter, check if it's a nested dataclass
197 enhanced_params = {}
198 for name, param_info in base_params.items():
199 enhanced_params[name] = param_info
201 # If this parameter is a dataclass, mark it as having nested structure
202 if dataclasses.is_dataclass(param_info.param_type):
203 # Update source type to indicate nesting capability
204 enhanced_params[name] = UnifiedParameterInfo(
205 name=param_info.name,
206 param_type=param_info.param_type,
207 default_value=param_info.default_value,
208 is_required=param_info.is_required,
209 description=param_info.description,
210 source_type=f"{param_info.source_type}_nested"
211 )
213 return enhanced_params
216# Backward compatibility aliases
217# These allow existing code to continue working while migration happens
218ParameterAnalyzer = UnifiedParameterAnalyzer
219analyze_parameters = UnifiedParameterAnalyzer.analyze