Coverage for openhcs/ui/shared/pattern_data_manager.py: 17.2%

80 statements  

« 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. 

3 

4This service handles pattern data structure operations and transformations 

5with order determinism and immutable operations. 

6 

7Framework-agnostic - can be used by any UI framework (PyQt, Textual, etc.). 

8""" 

9 

10import copy 

11from typing import Union, List, Dict, Tuple, Optional, Callable, Any 

12 

13 

14class PatternDataManager: 

15 """ 

16 Pure data operations for function patterns. 

17  

18 Handles List↔Dict conversions, cloning, and data transformations 

19 with order determinism and immutable operations. 

20 """ 

21 

22 @staticmethod 

23 def clone_pattern(pattern: Union[List, Dict]) -> Union[List, Dict]: 

24 """ 

25 Deep clone preserving callable references exactly. 

26  

27 Args: 

28 pattern: Pattern to clone (List or Dict) 

29  

30 Returns: 

31 Deep cloned pattern with preserved callable references 

32 """ 

33 if pattern is None: 

34 return [] 

35 return copy.deepcopy(pattern) 

36 

37 @staticmethod 

38 def convert_list_to_dict(pattern: List) -> Dict: 

39 """ 

40 Convert List pattern to empty Dict - user must add component keys manually. 

41 

42 Args: 

43 pattern: List pattern to convert (will be discarded) 

44 

45 Returns: 

46 Empty dict for user to populate with experimental component identifiers 

47 """ 

48 if not isinstance(pattern, list): 

49 raise ValueError(f"Expected list, got {type(pattern)}") 

50 

51 # Return empty dict - user will add experimental component keys manually 

52 return {} 

53 

54 @staticmethod 

55 def convert_dict_to_list(pattern: Dict) -> Union[List, Dict]: 

56 """ 

57 Convert Dict pattern to List when empty. 

58 

59 Args: 

60 pattern: Dict pattern to potentially convert 

61 

62 Returns: 

63 Empty list if dict is empty, otherwise returns original dict 

64 """ 

65 if not isinstance(pattern, dict): 

66 raise ValueError(f"Expected dict, got {type(pattern)}") 

67 

68 # Convert to empty list if dict is empty 

69 if not pattern: 

70 return [] 

71 

72 # Keep as dict if it has keys 

73 return pattern 

74 

75 @staticmethod 

76 def extract_func_and_kwargs(func_item) -> Tuple[Optional[Callable], Dict]: 

77 """ 

78 Parse (func, kwargs) tuples and bare callables. 

79 

80 Handles both tuple format and bare callable format exactly as current logic. 

81 

82 Args: 

83 func_item: Either (callable, kwargs) tuple or bare callable 

84 

85 Returns: 

86 Tuple of (callable, kwargs_dict) 

87 """ 

88 # EXACT current logic preservation 

89 if isinstance(func_item, tuple) and len(func_item) == 2 and callable(func_item[0]): 

90 result = func_item[0], func_item[1] 

91 print(f"🔍 PATTERN DATA MANAGER extract_func_and_kwargs: tuple case - returning {result}") 

92 return result 

93 elif callable(func_item): 

94 result = func_item, {} 

95 print(f"🔍 PATTERN DATA MANAGER extract_func_and_kwargs: callable case - returning {result}") 

96 return result 

97 else: 

98 print("🔍 PATTERN DATA MANAGER extract_func_and_kwargs: neither tuple nor callable - returning None, {}") 

99 return None, {} 

100 

101 @staticmethod 

102 def validate_pattern_structure(pattern: Union[List, Dict]) -> bool: 

103 """ 

104 Basic structural validation of pattern. 

105  

106 Args: 

107 pattern: Pattern to validate 

108  

109 Returns: 

110 True if structure is valid, False otherwise 

111 """ 

112 if pattern is None: 

113 return True 

114 

115 if isinstance(pattern, list): 

116 # Validate list items are callables or (callable, dict) tuples 

117 for item in pattern: 

118 func, kwargs = PatternDataManager.extract_func_and_kwargs(item) 

119 if func is None: 

120 return False 

121 if not isinstance(kwargs, dict): 

122 return False 

123 return True 

124 

125 elif isinstance(pattern, dict): 

126 # Validate dict values are lists of callables 

127 for key, value in pattern.items(): 

128 if not isinstance(value, list): 

129 return False 

130 # Recursively validate the list 

131 if not PatternDataManager.validate_pattern_structure(value): 

132 return False 

133 return True 

134 

135 else: 

136 return False 

137 

138 @staticmethod 

139 def get_current_functions(pattern: Union[List, Dict], key: Any, is_dict: bool) -> List: 

140 """ 

141 Extract function list for current context. 

142  

143 Args: 

144 pattern: Full pattern (List or Dict) 

145 key: Current key (for Dict patterns) 

146 is_dict: Whether pattern is currently in dict mode 

147  

148 Returns: 

149 List of functions for current context 

150 """ 

151 if is_dict and isinstance(pattern, dict): 

152 return pattern.get(key, []) 

153 elif not is_dict and isinstance(pattern, list): 

154 return pattern 

155 else: 

156 return [] 

157 

158 @staticmethod 

159 def update_pattern_functions(pattern: Union[List, Dict], key: Any, is_dict: bool, 

160 new_functions: List) -> Union[List, Dict]: 

161 """ 

162 Update functions in pattern for current context. 

163  

164 Returns new pattern object (immutable operation). 

165  

166 Args: 

167 pattern: Original pattern 

168 key: Current key (for Dict patterns) 

169 is_dict: Whether pattern is in dict mode 

170 new_functions: New function list 

171  

172 Returns: 

173 New pattern with updated functions 

174 """ 

175 if is_dict and isinstance(pattern, dict): 

176 new_pattern = copy.deepcopy(pattern) 

177 new_pattern[key] = new_functions 

178 return new_pattern 

179 elif not is_dict and isinstance(pattern, list): 

180 return copy.deepcopy(new_functions) 

181 else: 

182 # Fallback - return original pattern 

183 return copy.deepcopy(pattern) 

184 

185 @staticmethod 

186 def add_new_key(pattern: Dict, new_key: str) -> Dict: 

187 """ 

188 Add new key to dict pattern. 

189  

190 Args: 

191 pattern: Dict pattern 

192 new_key: Key to add 

193  

194 Returns: 

195 New dict with added key 

196 """ 

197 new_pattern = copy.deepcopy(pattern) 

198 if new_key not in new_pattern: 

199 new_pattern[new_key] = [] 

200 return new_pattern 

201 

202 @staticmethod 

203 def remove_key(pattern: Dict, key_to_remove: Any) -> Union[List, Dict]: 

204 """ 

205 Remove key from dict pattern. 

206 

207 If dict becomes empty after removal, converts back to list. 

208 

209 Args: 

210 pattern: Dict pattern 

211 key_to_remove: Key to remove 

212 

213 Returns: 

214 New pattern (List if dict becomes empty, Dict otherwise) 

215 """ 

216 new_pattern = copy.deepcopy(pattern) 

217 if key_to_remove in new_pattern: 

218 del new_pattern[key_to_remove] 

219 

220 # Check if should convert back to list (when empty) 

221 return PatternDataManager.convert_dict_to_list(new_pattern) 

222