exonware-xwnode 0.0.1.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,503 @@
|
|
1
|
+
"""
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/errors.py
|
3
|
+
|
4
|
+
A+ Best Practice Error Handling for xwnode Library.
|
5
|
+
|
6
|
+
This module implements zero-overhead error handling with rich context,
|
7
|
+
actionable suggestions, and performance optimization for critical paths.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import time
|
11
|
+
import difflib
|
12
|
+
from typing import Any, Dict, List, Optional, Union
|
13
|
+
from exonware.xwsystem import get_logger
|
14
|
+
|
15
|
+
logger = get_logger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
# ============================================================================
|
19
|
+
# A+ BASE ERROR SYSTEM
|
20
|
+
# ============================================================================
|
21
|
+
|
22
|
+
class XWNodeError(Exception):
|
23
|
+
"""
|
24
|
+
Base exception with rich context and zero overhead in success path.
|
25
|
+
|
26
|
+
This error system follows modern Python best practices:
|
27
|
+
- Zero overhead when no errors occur
|
28
|
+
- Rich context only created on failure path
|
29
|
+
- Chainable methods for fluent error building
|
30
|
+
- Performance-optimized with __slots__
|
31
|
+
"""
|
32
|
+
|
33
|
+
__slots__ = ('message', 'error_code', 'context', 'suggestions', 'timestamp', 'cause')
|
34
|
+
|
35
|
+
def __init__(self, message: str, *,
|
36
|
+
error_code: str = None,
|
37
|
+
context: Dict[str, Any] = None,
|
38
|
+
suggestions: List[str] = None,
|
39
|
+
cause: Exception = None):
|
40
|
+
super().__init__(message)
|
41
|
+
self.message = message
|
42
|
+
self.error_code = error_code or self.__class__.__name__
|
43
|
+
self.context = context or {}
|
44
|
+
self.suggestions = suggestions or []
|
45
|
+
self.timestamp = time.time()
|
46
|
+
self.cause = cause
|
47
|
+
|
48
|
+
def add_context(self, **kwargs) -> 'XWNodeError':
|
49
|
+
"""Add context information (chainable)."""
|
50
|
+
self.context.update(kwargs)
|
51
|
+
return self
|
52
|
+
|
53
|
+
def suggest(self, suggestion: str) -> 'XWNodeError':
|
54
|
+
"""Add actionable suggestion (chainable)."""
|
55
|
+
self.suggestions.append(suggestion)
|
56
|
+
return self
|
57
|
+
|
58
|
+
def __str__(self) -> str:
|
59
|
+
"""Rich string representation with context and suggestions."""
|
60
|
+
result = [self.message]
|
61
|
+
|
62
|
+
if self.context:
|
63
|
+
context_str = ', '.join(f"{k}={v}" for k, v in self.context.items())
|
64
|
+
result.append(f"Context: {context_str}")
|
65
|
+
|
66
|
+
if self.suggestions:
|
67
|
+
suggestions_str = '; '.join(self.suggestions)
|
68
|
+
result.append(f"Suggestions: {suggestions_str}")
|
69
|
+
|
70
|
+
return " | ".join(result)
|
71
|
+
|
72
|
+
|
73
|
+
# ============================================================================
|
74
|
+
# PATH NAVIGATION ERRORS (Most Common)
|
75
|
+
# ============================================================================
|
76
|
+
|
77
|
+
class XWNodePathError(XWNodeError, KeyError):
|
78
|
+
"""
|
79
|
+
Path navigation errors with AI-like suggestions.
|
80
|
+
|
81
|
+
This is the most common error type, so it's heavily optimized
|
82
|
+
with smart suggestion generation and fuzzy matching.
|
83
|
+
"""
|
84
|
+
|
85
|
+
__slots__ = ('path', 'segment', 'reason', 'node_type', 'available_keys')
|
86
|
+
|
87
|
+
def __init__(self, path: str, segment: str = None, reason: str = "not_found",
|
88
|
+
node_type: str = None, available_keys: List[str] = None):
|
89
|
+
self.path = path
|
90
|
+
self.segment = segment or path
|
91
|
+
self.reason = reason # "not_found", "type_mismatch", "out_of_bounds"
|
92
|
+
self.node_type = node_type
|
93
|
+
self.available_keys = available_keys or []
|
94
|
+
|
95
|
+
# Generate helpful message
|
96
|
+
message = f"Path navigation failed: '{path}'"
|
97
|
+
if segment and segment != path:
|
98
|
+
message += f" at segment '{segment}'"
|
99
|
+
message += f" ({reason})"
|
100
|
+
|
101
|
+
context = {
|
102
|
+
'path': path,
|
103
|
+
'segment': segment,
|
104
|
+
'reason': reason,
|
105
|
+
'node_type': node_type
|
106
|
+
}
|
107
|
+
|
108
|
+
suggestions = self._generate_smart_suggestions()
|
109
|
+
|
110
|
+
super().__init__(message,
|
111
|
+
error_code=f"PATH_{reason.upper()}",
|
112
|
+
context=context,
|
113
|
+
suggestions=suggestions)
|
114
|
+
|
115
|
+
def _generate_smart_suggestions(self) -> List[str]:
|
116
|
+
"""Generate AI-like suggestions based on error context."""
|
117
|
+
suggestions = []
|
118
|
+
|
119
|
+
if self.reason == "not_found" and self.available_keys:
|
120
|
+
# Fuzzy matching for typos (performance: only on error path)
|
121
|
+
close_matches = difflib.get_close_matches(
|
122
|
+
self.segment, self.available_keys, n=3, cutoff=0.6
|
123
|
+
)
|
124
|
+
if close_matches:
|
125
|
+
suggestions.append(f"Did you mean: {', '.join(close_matches)}?")
|
126
|
+
|
127
|
+
# Show available options (truncated for readability)
|
128
|
+
if len(self.available_keys) <= 5:
|
129
|
+
suggestions.append(f"Available: {', '.join(self.available_keys)}")
|
130
|
+
else:
|
131
|
+
preview = ', '.join(self.available_keys[:5])
|
132
|
+
suggestions.append(f"Available: {preview}... ({len(self.available_keys)} total)")
|
133
|
+
|
134
|
+
elif self.reason == "type_mismatch":
|
135
|
+
suggestions.append("Cannot access children of leaf nodes")
|
136
|
+
if self.node_type:
|
137
|
+
suggestions.append(f"Node type is '{self.node_type}' (not a container)")
|
138
|
+
|
139
|
+
elif self.reason == "out_of_bounds":
|
140
|
+
suggestions.append("Check list bounds before accessing by index")
|
141
|
+
if self.available_keys:
|
142
|
+
suggestions.append(f"Valid range: 0-{len(self.available_keys)-1}")
|
143
|
+
|
144
|
+
return suggestions
|
145
|
+
|
146
|
+
|
147
|
+
class XWNodeTypeError(XWNodeError, TypeError):
|
148
|
+
"""Enhanced type errors with operation context."""
|
149
|
+
|
150
|
+
__slots__ = ('attempted_operation', 'actual_type', 'expected_types')
|
151
|
+
|
152
|
+
def __init__(self, message: str, *,
|
153
|
+
attempted_operation: str = None,
|
154
|
+
actual_type: str = None,
|
155
|
+
expected_types: List[str] = None):
|
156
|
+
self.attempted_operation = attempted_operation
|
157
|
+
self.actual_type = actual_type
|
158
|
+
self.expected_types = expected_types or []
|
159
|
+
|
160
|
+
context = {
|
161
|
+
'operation': attempted_operation,
|
162
|
+
'actual_type': actual_type,
|
163
|
+
'expected_types': expected_types
|
164
|
+
}
|
165
|
+
|
166
|
+
suggestions = []
|
167
|
+
if expected_types:
|
168
|
+
suggestions.append(f"Expected types: {', '.join(expected_types)}")
|
169
|
+
if attempted_operation:
|
170
|
+
suggestions.append(f"Check node type before {attempted_operation}")
|
171
|
+
|
172
|
+
super().__init__(message,
|
173
|
+
error_code="TYPE_MISMATCH",
|
174
|
+
context=context,
|
175
|
+
suggestions=suggestions)
|
176
|
+
|
177
|
+
|
178
|
+
class XWNodeValueError(XWNodeError, ValueError):
|
179
|
+
"""Enhanced value errors with validation context."""
|
180
|
+
|
181
|
+
__slots__ = ('invalid_value', 'constraints', 'validation_rules')
|
182
|
+
|
183
|
+
def __init__(self, message: str, *,
|
184
|
+
invalid_value: Any = None,
|
185
|
+
constraints: Dict[str, Any] = None,
|
186
|
+
validation_rules: List[str] = None):
|
187
|
+
self.invalid_value = invalid_value
|
188
|
+
self.constraints = constraints or {}
|
189
|
+
self.validation_rules = validation_rules or []
|
190
|
+
|
191
|
+
context = {
|
192
|
+
'invalid_value': invalid_value,
|
193
|
+
'constraints': constraints
|
194
|
+
}
|
195
|
+
|
196
|
+
suggestions = []
|
197
|
+
if validation_rules:
|
198
|
+
suggestions.extend(validation_rules)
|
199
|
+
if constraints:
|
200
|
+
for rule, value in constraints.items():
|
201
|
+
suggestions.append(f"Value must satisfy {rule}: {value}")
|
202
|
+
|
203
|
+
super().__init__(message,
|
204
|
+
error_code="INVALID_VALUE",
|
205
|
+
context=context,
|
206
|
+
suggestions=suggestions)
|
207
|
+
|
208
|
+
|
209
|
+
# ============================================================================
|
210
|
+
# PERFORMANCE-OPTIMIZED ERROR FACTORY
|
211
|
+
# ============================================================================
|
212
|
+
|
213
|
+
class ErrorFactory:
|
214
|
+
"""
|
215
|
+
Zero-overhead error creation for critical paths.
|
216
|
+
|
217
|
+
This factory provides fast paths for common error scenarios,
|
218
|
+
avoiding expensive operations in hot code paths.
|
219
|
+
"""
|
220
|
+
|
221
|
+
@staticmethod
|
222
|
+
def path_not_found(path: str, segment: str = None, available_keys: List[str] = None) -> XWNodePathError:
|
223
|
+
"""Fast path for common 'not found' errors."""
|
224
|
+
return XWNodePathError(path, segment, "not_found", available_keys=available_keys)
|
225
|
+
|
226
|
+
@staticmethod
|
227
|
+
def type_mismatch(path: str, segment: str, node_type: str) -> XWNodePathError:
|
228
|
+
"""Fast path for type mismatch errors."""
|
229
|
+
return XWNodePathError(path, segment, "type_mismatch", node_type=node_type)
|
230
|
+
|
231
|
+
@staticmethod
|
232
|
+
def index_out_of_bounds(path: str, index: int, length: int) -> XWNodePathError:
|
233
|
+
"""Fast path for index errors."""
|
234
|
+
return XWNodePathError(path, str(index), "out_of_bounds").add_context(
|
235
|
+
index=index, length=length
|
236
|
+
).suggest(f"Valid range: 0-{length-1}")
|
237
|
+
|
238
|
+
@staticmethod
|
239
|
+
def operation_not_supported(operation: str, node_type: str, supported_ops: List[str] = None) -> XWNodeTypeError:
|
240
|
+
"""Fast path for unsupported operation errors."""
|
241
|
+
message = f"Operation '{operation}' not supported on {node_type}"
|
242
|
+
error = XWNodeTypeError(
|
243
|
+
message,
|
244
|
+
attempted_operation=operation,
|
245
|
+
actual_type=node_type
|
246
|
+
)
|
247
|
+
if supported_ops:
|
248
|
+
error.suggest(f"Supported operations: {', '.join(supported_ops)}")
|
249
|
+
return error
|
250
|
+
|
251
|
+
@staticmethod
|
252
|
+
def invalid_preset(preset_name: str, available_presets: List[str]) -> XWNodeValueError:
|
253
|
+
"""Fast path for invalid preset errors."""
|
254
|
+
close_matches = difflib.get_close_matches(preset_name, available_presets, n=3, cutoff=0.6)
|
255
|
+
error = XWNodeValueError(
|
256
|
+
f"Unknown preset '{preset_name}'",
|
257
|
+
invalid_value=preset_name,
|
258
|
+
constraints={'available_presets': available_presets}
|
259
|
+
)
|
260
|
+
if close_matches:
|
261
|
+
error.suggest(f"Did you mean: {', '.join(close_matches)}?")
|
262
|
+
error.suggest(f"Available presets: {', '.join(available_presets)}")
|
263
|
+
return error
|
264
|
+
|
265
|
+
|
266
|
+
# ============================================================================
|
267
|
+
# STRATEGY SYSTEM ERRORS (A+ Enhanced)
|
268
|
+
# ============================================================================
|
269
|
+
|
270
|
+
class XWNodeStrategyError(XWNodeError):
|
271
|
+
"""Base exception for strategy-related errors."""
|
272
|
+
pass
|
273
|
+
|
274
|
+
|
275
|
+
class XWNodeUnsupportedCapabilityError(XWNodeStrategyError):
|
276
|
+
"""Raised when a strategy doesn't support a requested capability."""
|
277
|
+
|
278
|
+
__slots__ = ('capability', 'strategy', 'available_capabilities')
|
279
|
+
|
280
|
+
def __init__(self, capability: str, strategy: str, available_capabilities: List[str] = None):
|
281
|
+
self.capability = capability
|
282
|
+
self.strategy = strategy
|
283
|
+
self.available_capabilities = available_capabilities or []
|
284
|
+
|
285
|
+
message = f"Strategy '{strategy}' does not support capability '{capability}'"
|
286
|
+
|
287
|
+
context = {
|
288
|
+
'capability': capability,
|
289
|
+
'strategy': strategy,
|
290
|
+
'available_capabilities': available_capabilities
|
291
|
+
}
|
292
|
+
|
293
|
+
suggestions = []
|
294
|
+
if available_capabilities:
|
295
|
+
suggestions.append(f"Available capabilities: {', '.join(available_capabilities)}")
|
296
|
+
|
297
|
+
# Suggest alternative strategies
|
298
|
+
if capability == 'graph_operations':
|
299
|
+
suggestions.append("Use preset='SOCIAL_GRAPH' or 'TREE_GRAPH_MIX' for graph features")
|
300
|
+
elif capability == 'spatial_operations':
|
301
|
+
suggestions.append("Use preset='SPATIAL_MAP' for spatial features")
|
302
|
+
|
303
|
+
super().__init__(message,
|
304
|
+
error_code="UNSUPPORTED_CAPABILITY",
|
305
|
+
context=context,
|
306
|
+
suggestions=suggestions)
|
307
|
+
|
308
|
+
|
309
|
+
class XWNodePresetError(XWNodeStrategyError):
|
310
|
+
"""Raised when preset operations fail."""
|
311
|
+
|
312
|
+
__slots__ = ('preset_name', 'operation', 'reason')
|
313
|
+
|
314
|
+
def __init__(self, preset_name: str, operation: str, reason: str = None):
|
315
|
+
self.preset_name = preset_name
|
316
|
+
self.operation = operation
|
317
|
+
self.reason = reason
|
318
|
+
|
319
|
+
message = f"Preset '{preset_name}' {operation} failed"
|
320
|
+
if reason:
|
321
|
+
message += f": {reason}"
|
322
|
+
|
323
|
+
context = {
|
324
|
+
'preset_name': preset_name,
|
325
|
+
'operation': operation,
|
326
|
+
'reason': reason
|
327
|
+
}
|
328
|
+
|
329
|
+
# Import here to avoid circular imports
|
330
|
+
try:
|
331
|
+
from .types import list_presets
|
332
|
+
available = list_presets()
|
333
|
+
suggestions = [f"Available presets: {', '.join(available)}"]
|
334
|
+
except ImportError:
|
335
|
+
suggestions = ["Check preset name spelling"]
|
336
|
+
|
337
|
+
super().__init__(message,
|
338
|
+
error_code="PRESET_ERROR",
|
339
|
+
context=context,
|
340
|
+
suggestions=suggestions)
|
341
|
+
|
342
|
+
|
343
|
+
# ============================================================================
|
344
|
+
# SECURITY ERRORS (A+ Enhanced)
|
345
|
+
# ============================================================================
|
346
|
+
|
347
|
+
class XWNodeSecurityError(XWNodeError):
|
348
|
+
"""Base exception for security-related errors."""
|
349
|
+
pass
|
350
|
+
|
351
|
+
|
352
|
+
class XWNodeLimitError(XWNodeSecurityError):
|
353
|
+
"""Raised when resource limits are exceeded."""
|
354
|
+
|
355
|
+
__slots__ = ('resource', 'limit', 'actual_value')
|
356
|
+
|
357
|
+
def __init__(self, resource: str, limit: Union[int, str], actual_value: Union[int, str] = None):
|
358
|
+
self.resource = resource
|
359
|
+
self.limit = limit
|
360
|
+
self.actual_value = actual_value
|
361
|
+
|
362
|
+
message = f"Resource limit exceeded for {resource}: limit={limit}"
|
363
|
+
if actual_value is not None:
|
364
|
+
message += f", actual={actual_value}"
|
365
|
+
|
366
|
+
context = {
|
367
|
+
'resource': resource,
|
368
|
+
'limit': limit,
|
369
|
+
'actual_value': actual_value
|
370
|
+
}
|
371
|
+
|
372
|
+
suggestions = [
|
373
|
+
f"Increase {resource} limit in configuration",
|
374
|
+
"Consider using a more efficient strategy",
|
375
|
+
"Break operation into smaller chunks"
|
376
|
+
]
|
377
|
+
|
378
|
+
super().__init__(message,
|
379
|
+
error_code="RESOURCE_LIMIT",
|
380
|
+
context=context,
|
381
|
+
suggestions=suggestions)
|
382
|
+
|
383
|
+
|
384
|
+
class XWNodePathSecurityError(XWNodeSecurityError):
|
385
|
+
"""Raised for path-related security violations."""
|
386
|
+
|
387
|
+
__slots__ = ('path', 'violation_type', 'security_policy')
|
388
|
+
|
389
|
+
def __init__(self, path: str, violation_type: str, security_policy: str = None):
|
390
|
+
self.path = path
|
391
|
+
self.violation_type = violation_type
|
392
|
+
self.security_policy = security_policy
|
393
|
+
|
394
|
+
message = f"Path security violation: {violation_type} in '{path}'"
|
395
|
+
if security_policy:
|
396
|
+
message += f" (policy: {security_policy})"
|
397
|
+
|
398
|
+
context = {
|
399
|
+
'path': path,
|
400
|
+
'violation_type': violation_type,
|
401
|
+
'security_policy': security_policy
|
402
|
+
}
|
403
|
+
|
404
|
+
suggestions = [
|
405
|
+
"Check path for invalid characters or patterns",
|
406
|
+
"Use only trusted data sources",
|
407
|
+
"Sanitize user input before path operations"
|
408
|
+
]
|
409
|
+
|
410
|
+
super().__init__(message,
|
411
|
+
error_code="PATH_SECURITY",
|
412
|
+
context=context,
|
413
|
+
suggestions=suggestions)
|
414
|
+
|
415
|
+
|
416
|
+
# ============================================================================
|
417
|
+
# PERFORMANCE OPTIMIZATION HELPERS
|
418
|
+
# ============================================================================
|
419
|
+
|
420
|
+
def safe_path_access(get_child_func, path: str, segment: str, node=None):
|
421
|
+
"""
|
422
|
+
Optimized wrapper for path access with minimal error overhead.
|
423
|
+
|
424
|
+
This function encapsulates the zero-overhead error handling pattern:
|
425
|
+
- Fast path when operation succeeds
|
426
|
+
- Rich error context only on failure
|
427
|
+
"""
|
428
|
+
try:
|
429
|
+
# Fast path - no error overhead when successful
|
430
|
+
return get_child_func(segment)
|
431
|
+
except KeyError:
|
432
|
+
# Only create rich error on failure path
|
433
|
+
available = []
|
434
|
+
if node and hasattr(node, 'keys'):
|
435
|
+
available = list(node.keys())
|
436
|
+
raise ErrorFactory.path_not_found(path, segment, available)
|
437
|
+
except IndexError:
|
438
|
+
# Index out of bounds
|
439
|
+
length = len(node) if node and hasattr(node, '__len__') else 0
|
440
|
+
try:
|
441
|
+
index = int(segment)
|
442
|
+
raise ErrorFactory.index_out_of_bounds(path, index, length)
|
443
|
+
except ValueError:
|
444
|
+
# Non-numeric index on list
|
445
|
+
raise ErrorFactory.type_mismatch(path, segment, "list")
|
446
|
+
except TypeError:
|
447
|
+
# Type mismatch - trying to access child of leaf
|
448
|
+
node_type = type(node).__name__ if node else "unknown"
|
449
|
+
raise ErrorFactory.type_mismatch(path, segment, node_type)
|
450
|
+
|
451
|
+
|
452
|
+
# ============================================================================
|
453
|
+
# LEGACY COMPATIBILITY (Backwards compatibility)
|
454
|
+
# ============================================================================
|
455
|
+
|
456
|
+
# Keep old error names for backwards compatibility
|
457
|
+
XWNodeValidationError = XWNodeValueError
|
458
|
+
XWNodeSerializationError = XWNodeError
|
459
|
+
XWNodePerformanceError = XWNodeError
|
460
|
+
XWNodeConfigurationError = XWNodeError
|
461
|
+
|
462
|
+
# Strategy errors (maintained for compatibility)
|
463
|
+
XWNodeIllegalMigrationError = XWNodeStrategyError
|
464
|
+
XWNodeStrategyNotFoundError = XWNodeStrategyError
|
465
|
+
XWNodeStrategyInitializationError = XWNodeStrategyError
|
466
|
+
XWNodeConcurrencyError = XWNodeStrategyError
|
467
|
+
|
468
|
+
|
469
|
+
# ============================================================================
|
470
|
+
# EXPORTS
|
471
|
+
# ============================================================================
|
472
|
+
|
473
|
+
__all__ = [
|
474
|
+
# Core error system
|
475
|
+
'XWNodeError',
|
476
|
+
'XWNodePathError',
|
477
|
+
'XWNodeTypeError',
|
478
|
+
'XWNodeValueError',
|
479
|
+
|
480
|
+
# Security errors
|
481
|
+
'XWNodeSecurityError',
|
482
|
+
'XWNodeLimitError',
|
483
|
+
'XWNodePathSecurityError',
|
484
|
+
|
485
|
+
# Strategy errors
|
486
|
+
'XWNodeStrategyError',
|
487
|
+
'XWNodeUnsupportedCapabilityError',
|
488
|
+
'XWNodePresetError',
|
489
|
+
|
490
|
+
# Performance helpers
|
491
|
+
'ErrorFactory',
|
492
|
+
'safe_path_access',
|
493
|
+
|
494
|
+
# Legacy compatibility
|
495
|
+
'XWNodeValidationError',
|
496
|
+
'XWNodeSerializationError',
|
497
|
+
'XWNodePerformanceError',
|
498
|
+
'XWNodeConfigurationError',
|
499
|
+
'XWNodeIllegalMigrationError',
|
500
|
+
'XWNodeStrategyNotFoundError',
|
501
|
+
'XWNodeStrategyInitializationError',
|
502
|
+
'XWNodeConcurrencyError'
|
503
|
+
]
|