Coverage for openhcs/textual_tui/services/config_reflection_service.py: 0.0%
45 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
1"""Config reflection service - port from old TUI."""
3import dataclasses
4from dataclasses import dataclass, fields
5from typing import Any, List, Optional, get_origin, get_args, Union as TypingUnion
8@dataclass
9class FieldSpec:
10 """Specification for a dataclass field."""
11 name: str
12 label: str
13 field_type: type
14 actual_type: type
15 current_value: Any
16 default_value: Any
17 is_optional: bool
18 is_nested_dataclass: bool = False
19 nested_fields: Optional[List['FieldSpec']] = None
22class FieldIntrospector:
23 """Analyzes dataclass fields for form generation."""
25 @staticmethod
26 def analyze_dataclass(dataclass_type: type, instance: Any) -> List[FieldSpec]:
27 """Analyze dataclass and return field specifications."""
28 if not dataclasses.is_dataclass(dataclass_type):
29 raise ValueError(f"{dataclass_type} is not a dataclass")
31 specs = []
32 for field in fields(dataclass_type):
33 if field.name.startswith('_'):
34 continue
36 spec = FieldIntrospector._analyze_field(field, instance)
37 specs.append(spec)
39 return specs
41 @staticmethod
42 def _analyze_field(field: dataclasses.Field, instance: Any) -> FieldSpec:
43 """Analyze individual field."""
44 # Extract type information
45 field_type = field.type
46 actual_type = field_type
47 is_optional = get_origin(field_type) is TypingUnion and type(None) in get_args(field_type)
49 if is_optional:
50 actual_type = next((t for t in get_args(field_type) if t is not type(None)), actual_type)
52 # Get values
53 current_value = getattr(instance, field.name, None)
54 default_value = None
56 if field.default is not dataclasses.MISSING:
57 default_value = field.default
58 elif field.default_factory is not dataclasses.MISSING:
59 default_value = field.default_factory()
61 # Create label
62 label = field.name.replace('_', ' ').title()
64 # Check if this is a nested dataclass
65 is_nested_dataclass = dataclasses.is_dataclass(actual_type)
66 nested_fields = None
68 if is_nested_dataclass and current_value is not None:
69 # Recursively analyze nested dataclass
70 nested_fields = FieldIntrospector.analyze_dataclass(actual_type, current_value)
72 return FieldSpec(
73 name=field.name,
74 label=label,
75 field_type=field_type,
76 actual_type=actual_type,
77 current_value=current_value,
78 default_value=default_value,
79 is_optional=is_optional,
80 is_nested_dataclass=is_nested_dataclass,
81 nested_fields=nested_fields
82 )