Coverage for openhcs/textual_tui/services/pattern_data_manager.py: 0.0%
75 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"""
2Pattern Data Manager - Pure data operations for function patterns.
4This service handles pattern data structure operations and transformations
5with order determinism and immutable operations.
6"""
8import copy
9from typing import Union, List, Dict, Tuple, Optional, Callable, Any
12class PatternDataManager:
13 """
14 Pure data operations for function patterns.
16 Handles List↔Dict conversions, cloning, and data transformations
17 with order determinism and immutable operations.
18 """
20 @staticmethod
21 def clone_pattern(pattern: Union[List, Dict]) -> Union[List, Dict]:
22 """
23 Deep clone preserving callable references exactly.
25 Args:
26 pattern: Pattern to clone (List or Dict)
28 Returns:
29 Deep cloned pattern with preserved callable references
30 """
31 if pattern is None:
32 return []
33 return copy.deepcopy(pattern)
35 @staticmethod
36 def convert_list_to_dict(pattern: List) -> Dict:
37 """
38 Convert List pattern to empty Dict - user must add component keys manually.
40 Args:
41 pattern: List pattern to convert (will be discarded)
43 Returns:
44 Empty dict for user to populate with experimental component identifiers
45 """
46 if not isinstance(pattern, list):
47 raise ValueError(f"Expected list, got {type(pattern)}")
49 # Return empty dict - user will add experimental component keys manually
50 return {}
52 @staticmethod
53 def convert_dict_to_list(pattern: Dict) -> Union[List, Dict]:
54 """
55 Convert Dict pattern to List when empty.
57 Args:
58 pattern: Dict pattern to potentially convert
60 Returns:
61 Empty list if dict is empty, otherwise returns original dict
62 """
63 if not isinstance(pattern, dict):
64 raise ValueError(f"Expected dict, got {type(pattern)}")
66 # Convert to empty list if dict is empty
67 if not pattern:
68 return []
70 # Keep as dict if it has keys
71 return pattern
73 @staticmethod
74 def extract_func_and_kwargs(func_item) -> Tuple[Optional[Callable], Dict]:
75 """
76 Parse (func, kwargs) tuples and bare callables.
78 Handles both tuple format and bare callable format exactly as current logic.
80 Args:
81 func_item: Either (callable, kwargs) tuple or bare callable
83 Returns:
84 Tuple of (callable, kwargs_dict)
85 """
86 # EXACT current logic preservation
87 if isinstance(func_item, tuple) and len(func_item) == 2 and callable(func_item[0]):
88 return func_item[0], func_item[1]
89 elif callable(func_item):
90 return func_item, {}
91 else:
92 return None, {}
94 @staticmethod
95 def validate_pattern_structure(pattern: Union[List, Dict]) -> bool:
96 """
97 Basic structural validation of pattern.
99 Args:
100 pattern: Pattern to validate
102 Returns:
103 True if structure is valid, False otherwise
104 """
105 if pattern is None:
106 return True
108 if isinstance(pattern, list):
109 # Validate list items are callables or (callable, dict) tuples
110 for item in pattern:
111 func, kwargs = PatternDataManager.extract_func_and_kwargs(item)
112 if func is None:
113 return False
114 if not isinstance(kwargs, dict):
115 return False
116 return True
118 elif isinstance(pattern, dict):
119 # Validate dict values are lists of callables
120 for key, value in pattern.items():
121 if not isinstance(value, list):
122 return False
123 # Recursively validate the list
124 if not PatternDataManager.validate_pattern_structure(value):
125 return False
126 return True
128 else:
129 return False
131 @staticmethod
132 def get_current_functions(pattern: Union[List, Dict], key: Any, is_dict: bool) -> List:
133 """
134 Extract function list for current context.
136 Args:
137 pattern: Full pattern (List or Dict)
138 key: Current key (for Dict patterns)
139 is_dict: Whether pattern is currently in dict mode
141 Returns:
142 List of functions for current context
143 """
144 if is_dict and isinstance(pattern, dict):
145 return pattern.get(key, [])
146 elif not is_dict and isinstance(pattern, list):
147 return pattern
148 else:
149 return []
151 @staticmethod
152 def update_pattern_functions(pattern: Union[List, Dict], key: Any, is_dict: bool,
153 new_functions: List) -> Union[List, Dict]:
154 """
155 Update functions in pattern for current context.
157 Returns new pattern object (immutable operation).
159 Args:
160 pattern: Original pattern
161 key: Current key (for Dict patterns)
162 is_dict: Whether pattern is in dict mode
163 new_functions: New function list
165 Returns:
166 New pattern with updated functions
167 """
168 if is_dict and isinstance(pattern, dict):
169 new_pattern = copy.deepcopy(pattern)
170 new_pattern[key] = new_functions
171 return new_pattern
172 elif not is_dict and isinstance(pattern, list):
173 return copy.deepcopy(new_functions)
174 else:
175 # Fallback - return original pattern
176 return copy.deepcopy(pattern)
178 @staticmethod
179 def add_new_key(pattern: Dict, new_key: str) -> Dict:
180 """
181 Add new key to dict pattern.
183 Args:
184 pattern: Dict pattern
185 new_key: Key to add
187 Returns:
188 New dict with added key
189 """
190 new_pattern = copy.deepcopy(pattern)
191 if new_key not in new_pattern:
192 new_pattern[new_key] = []
193 return new_pattern
195 @staticmethod
196 def remove_key(pattern: Dict, key_to_remove: Any) -> Union[List, Dict]:
197 """
198 Remove key from dict pattern.
200 If dict becomes empty after removal, converts back to list.
202 Args:
203 pattern: Dict pattern
204 key_to_remove: Key to remove
206 Returns:
207 New pattern (List if dict becomes empty, Dict otherwise)
208 """
209 new_pattern = copy.deepcopy(pattern)
210 if key_to_remove in new_pattern:
211 del new_pattern[key_to_remove]
213 # Check if should convert back to list (when empty)
214 return PatternDataManager.convert_dict_to_list(new_pattern)