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

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""" 

7 

8import copy 

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

10 

11 

12class PatternDataManager: 

13 """ 

14 Pure data operations for function patterns. 

15  

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

17 with order determinism and immutable operations. 

18 """ 

19 

20 @staticmethod 

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

22 """ 

23 Deep clone preserving callable references exactly. 

24  

25 Args: 

26 pattern: Pattern to clone (List or Dict) 

27  

28 Returns: 

29 Deep cloned pattern with preserved callable references 

30 """ 

31 if pattern is None: 

32 return [] 

33 return copy.deepcopy(pattern) 

34 

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. 

39 

40 Args: 

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

42 

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)}") 

48 

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

50 return {} 

51 

52 @staticmethod 

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

54 """ 

55 Convert Dict pattern to List when empty. 

56 

57 Args: 

58 pattern: Dict pattern to potentially convert 

59 

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)}") 

65 

66 # Convert to empty list if dict is empty 

67 if not pattern: 

68 return [] 

69 

70 # Keep as dict if it has keys 

71 return pattern 

72 

73 @staticmethod 

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

75 """ 

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

77  

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

79  

80 Args: 

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

82  

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, {} 

93 

94 @staticmethod 

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

96 """ 

97 Basic structural validation of pattern. 

98  

99 Args: 

100 pattern: Pattern to validate 

101  

102 Returns: 

103 True if structure is valid, False otherwise 

104 """ 

105 if pattern is None: 

106 return True 

107 

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 

117 

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 

127 

128 else: 

129 return False 

130 

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. 

135  

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 

140  

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 [] 

150 

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. 

156  

157 Returns new pattern object (immutable operation). 

158  

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 

164  

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) 

177 

178 @staticmethod 

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

180 """ 

181 Add new key to dict pattern. 

182  

183 Args: 

184 pattern: Dict pattern 

185 new_key: Key to add 

186  

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 

194 

195 @staticmethod 

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

197 """ 

198 Remove key from dict pattern. 

199 

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

201 

202 Args: 

203 pattern: Dict pattern 

204 key_to_remove: Key to remove 

205 

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] 

212 

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

214 return PatternDataManager.convert_dict_to_list(new_pattern)