Coverage for openhcs/textual_tui/services/pattern_data_manager.py: 0.0%
80 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"""
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 result = func_item[0], func_item[1]
89 print(f"🔍 PATTERN DATA MANAGER extract_func_and_kwargs: tuple case - returning {result}")
90 return result
91 elif callable(func_item):
92 result = func_item, {}
93 print(f"🔍 PATTERN DATA MANAGER extract_func_and_kwargs: callable case - returning {result}")
94 return result
95 else:
96 print("🔍 PATTERN DATA MANAGER extract_func_and_kwargs: neither tuple nor callable - returning None, {}")
97 return None, {}
99 @staticmethod
100 def validate_pattern_structure(pattern: Union[List, Dict]) -> bool:
101 """
102 Basic structural validation of pattern.
104 Args:
105 pattern: Pattern to validate
107 Returns:
108 True if structure is valid, False otherwise
109 """
110 if pattern is None:
111 return True
113 if isinstance(pattern, list):
114 # Validate list items are callables or (callable, dict) tuples
115 for item in pattern:
116 func, kwargs = PatternDataManager.extract_func_and_kwargs(item)
117 if func is None:
118 return False
119 if not isinstance(kwargs, dict):
120 return False
121 return True
123 elif isinstance(pattern, dict):
124 # Validate dict values are lists of callables
125 for key, value in pattern.items():
126 if not isinstance(value, list):
127 return False
128 # Recursively validate the list
129 if not PatternDataManager.validate_pattern_structure(value):
130 return False
131 return True
133 else:
134 return False
136 @staticmethod
137 def get_current_functions(pattern: Union[List, Dict], key: Any, is_dict: bool) -> List:
138 """
139 Extract function list for current context.
141 Args:
142 pattern: Full pattern (List or Dict)
143 key: Current key (for Dict patterns)
144 is_dict: Whether pattern is currently in dict mode
146 Returns:
147 List of functions for current context
148 """
149 if is_dict and isinstance(pattern, dict):
150 return pattern.get(key, [])
151 elif not is_dict and isinstance(pattern, list):
152 return pattern
153 else:
154 return []
156 @staticmethod
157 def update_pattern_functions(pattern: Union[List, Dict], key: Any, is_dict: bool,
158 new_functions: List) -> Union[List, Dict]:
159 """
160 Update functions in pattern for current context.
162 Returns new pattern object (immutable operation).
164 Args:
165 pattern: Original pattern
166 key: Current key (for Dict patterns)
167 is_dict: Whether pattern is in dict mode
168 new_functions: New function list
170 Returns:
171 New pattern with updated functions
172 """
173 if is_dict and isinstance(pattern, dict):
174 new_pattern = copy.deepcopy(pattern)
175 new_pattern[key] = new_functions
176 return new_pattern
177 elif not is_dict and isinstance(pattern, list):
178 return copy.deepcopy(new_functions)
179 else:
180 # Fallback - return original pattern
181 return copy.deepcopy(pattern)
183 @staticmethod
184 def add_new_key(pattern: Dict, new_key: str) -> Dict:
185 """
186 Add new key to dict pattern.
188 Args:
189 pattern: Dict pattern
190 new_key: Key to add
192 Returns:
193 New dict with added key
194 """
195 new_pattern = copy.deepcopy(pattern)
196 if new_key not in new_pattern:
197 new_pattern[new_key] = []
198 return new_pattern
200 @staticmethod
201 def remove_key(pattern: Dict, key_to_remove: Any) -> Union[List, Dict]:
202 """
203 Remove key from dict pattern.
205 If dict becomes empty after removal, converts back to list.
207 Args:
208 pattern: Dict pattern
209 key_to_remove: Key to remove
211 Returns:
212 New pattern (List if dict becomes empty, Dict otherwise)
213 """
214 new_pattern = copy.deepcopy(pattern)
215 if key_to_remove in new_pattern:
216 del new_pattern[key_to_remove]
218 # Check if should convert back to list (when empty)
219 return PatternDataManager.convert_dict_to_list(new_pattern)