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.
Files changed (132) hide show
  1. exonware/__init__.py +14 -0
  2. exonware/xwnode/__init__.py +127 -0
  3. exonware/xwnode/base.py +676 -0
  4. exonware/xwnode/config.py +178 -0
  5. exonware/xwnode/contracts.py +730 -0
  6. exonware/xwnode/errors.py +503 -0
  7. exonware/xwnode/facade.py +460 -0
  8. exonware/xwnode/strategies/__init__.py +158 -0
  9. exonware/xwnode/strategies/advisor.py +463 -0
  10. exonware/xwnode/strategies/edges/__init__.py +32 -0
  11. exonware/xwnode/strategies/edges/adj_list.py +227 -0
  12. exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
  13. exonware/xwnode/strategies/edges/base.py +169 -0
  14. exonware/xwnode/strategies/flyweight.py +328 -0
  15. exonware/xwnode/strategies/impls/__init__.py +13 -0
  16. exonware/xwnode/strategies/impls/_base_edge.py +403 -0
  17. exonware/xwnode/strategies/impls/_base_node.py +307 -0
  18. exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
  19. exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
  20. exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
  21. exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
  22. exonware/xwnode/strategies/impls/edge_coo.py +533 -0
  23. exonware/xwnode/strategies/impls/edge_csc.py +447 -0
  24. exonware/xwnode/strategies/impls/edge_csr.py +492 -0
  25. exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
  26. exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
  27. exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
  28. exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
  29. exonware/xwnode/strategies/impls/edge_octree.py +574 -0
  30. exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
  31. exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
  32. exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
  33. exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
  34. exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
  35. exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
  36. exonware/xwnode/strategies/manager.py +775 -0
  37. exonware/xwnode/strategies/metrics.py +538 -0
  38. exonware/xwnode/strategies/migration.py +432 -0
  39. exonware/xwnode/strategies/nodes/__init__.py +50 -0
  40. exonware/xwnode/strategies/nodes/_base_node.py +307 -0
  41. exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
  42. exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
  43. exonware/xwnode/strategies/nodes/array_list.py +209 -0
  44. exonware/xwnode/strategies/nodes/base.py +247 -0
  45. exonware/xwnode/strategies/nodes/deque.py +200 -0
  46. exonware/xwnode/strategies/nodes/hash_map.py +135 -0
  47. exonware/xwnode/strategies/nodes/heap.py +307 -0
  48. exonware/xwnode/strategies/nodes/linked_list.py +232 -0
  49. exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
  50. exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
  51. exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
  52. exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
  53. exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
  54. exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
  55. exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
  56. exonware/xwnode/strategies/nodes/node_btree.py +357 -0
  57. exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
  58. exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
  59. exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
  60. exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
  61. exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
  62. exonware/xwnode/strategies/nodes/node_heap.py +191 -0
  63. exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
  64. exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
  65. exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
  66. exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
  67. exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
  68. exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
  69. exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
  70. exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
  71. exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
  72. exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
  73. exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
  74. exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
  75. exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
  76. exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
  77. exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
  78. exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
  79. exonware/xwnode/strategies/nodes/node_treap.py +387 -0
  80. exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
  81. exonware/xwnode/strategies/nodes/node_trie.py +252 -0
  82. exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
  83. exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
  84. exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
  85. exonware/xwnode/strategies/nodes/queue.py +161 -0
  86. exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
  87. exonware/xwnode/strategies/nodes/stack.py +152 -0
  88. exonware/xwnode/strategies/nodes/trie.py +274 -0
  89. exonware/xwnode/strategies/nodes/union_find.py +283 -0
  90. exonware/xwnode/strategies/pattern_detector.py +603 -0
  91. exonware/xwnode/strategies/performance_monitor.py +487 -0
  92. exonware/xwnode/strategies/queries/__init__.py +24 -0
  93. exonware/xwnode/strategies/queries/base.py +236 -0
  94. exonware/xwnode/strategies/queries/cql.py +201 -0
  95. exonware/xwnode/strategies/queries/cypher.py +181 -0
  96. exonware/xwnode/strategies/queries/datalog.py +70 -0
  97. exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
  98. exonware/xwnode/strategies/queries/eql.py +70 -0
  99. exonware/xwnode/strategies/queries/flux.py +70 -0
  100. exonware/xwnode/strategies/queries/gql.py +70 -0
  101. exonware/xwnode/strategies/queries/graphql.py +240 -0
  102. exonware/xwnode/strategies/queries/gremlin.py +181 -0
  103. exonware/xwnode/strategies/queries/hiveql.py +214 -0
  104. exonware/xwnode/strategies/queries/hql.py +70 -0
  105. exonware/xwnode/strategies/queries/jmespath.py +219 -0
  106. exonware/xwnode/strategies/queries/jq.py +66 -0
  107. exonware/xwnode/strategies/queries/json_query.py +66 -0
  108. exonware/xwnode/strategies/queries/jsoniq.py +248 -0
  109. exonware/xwnode/strategies/queries/kql.py +70 -0
  110. exonware/xwnode/strategies/queries/linq.py +238 -0
  111. exonware/xwnode/strategies/queries/logql.py +70 -0
  112. exonware/xwnode/strategies/queries/mql.py +68 -0
  113. exonware/xwnode/strategies/queries/n1ql.py +210 -0
  114. exonware/xwnode/strategies/queries/partiql.py +70 -0
  115. exonware/xwnode/strategies/queries/pig.py +215 -0
  116. exonware/xwnode/strategies/queries/promql.py +70 -0
  117. exonware/xwnode/strategies/queries/sparql.py +220 -0
  118. exonware/xwnode/strategies/queries/sql.py +275 -0
  119. exonware/xwnode/strategies/queries/xml_query.py +66 -0
  120. exonware/xwnode/strategies/queries/xpath.py +223 -0
  121. exonware/xwnode/strategies/queries/xquery.py +258 -0
  122. exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
  123. exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
  124. exonware/xwnode/strategies/registry.py +604 -0
  125. exonware/xwnode/strategies/simple.py +273 -0
  126. exonware/xwnode/strategies/utils.py +532 -0
  127. exonware/xwnode/types.py +912 -0
  128. exonware/xwnode/version.py +78 -0
  129. exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
  130. exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
  131. exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
  132. exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,403 @@
1
+ """
2
+ Abstract Edge Strategy Interface
3
+
4
+ This module defines the abstract base class that all edge strategies must implement
5
+ in the strategy system.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, Dict, List, Optional, Iterator, Union, Set
10
+ from ...types import EdgeMode, EdgeTrait
11
+
12
+
13
+ class aEdgeStrategy(ABC):
14
+ """
15
+ Abstract base class for all edge strategies.
16
+
17
+ This abstract base class defines the contract that all edge strategy
18
+ implementations must follow, ensuring consistency and interoperability.
19
+ """
20
+
21
+ def __init__(self, mode: EdgeMode, traits: EdgeTrait = EdgeTrait.NONE, **options):
22
+ """Initialize the abstract edge strategy."""
23
+ self.mode = mode
24
+ self.traits = traits
25
+ self.options = options
26
+ self._edges: Dict[tuple[Any, Any], Dict[str, Any]] = {}
27
+ self._vertices: Set[Any] = set()
28
+ self._edge_count = 0
29
+
30
+ # Validate traits compatibility with mode
31
+ self._validate_traits()
32
+
33
+ def _validate_traits(self) -> None:
34
+ """Validate that the requested traits are compatible with this strategy."""
35
+ supported_traits = self.get_supported_traits()
36
+ unsupported = self.traits & ~supported_traits
37
+ if unsupported != EdgeTrait.NONE:
38
+ unsupported_names = [trait.name for trait in EdgeTrait if trait in unsupported]
39
+ raise ValueError(f"Strategy {self.mode.name} does not support traits: {unsupported_names}")
40
+
41
+ def get_supported_traits(self) -> EdgeTrait:
42
+ """Get the traits supported by this strategy implementation."""
43
+ # Default implementation - subclasses should override
44
+ return EdgeTrait.NONE
45
+
46
+ def has_trait(self, trait: EdgeTrait) -> bool:
47
+ """Check if this strategy has a specific trait."""
48
+ return bool(self.traits & trait)
49
+
50
+ def require_trait(self, trait: EdgeTrait, operation: str = "operation") -> None:
51
+ """Require a specific trait for an operation."""
52
+ if not self.has_trait(trait):
53
+ from ...errors import UnsupportedCapabilityError
54
+ raise UnsupportedCapabilityError(f"{operation} requires {trait.name} capability")
55
+
56
+ # ============================================================================
57
+ # CORE OPERATIONS (Required)
58
+ # ============================================================================
59
+
60
+ @abstractmethod
61
+ def add_edge(self, u: Any, v: Any, **properties) -> None:
62
+ """
63
+ Add an edge between vertices u and v.
64
+
65
+ Args:
66
+ u: Source vertex
67
+ v: Target vertex
68
+ **properties: Edge properties (weight, label, etc.)
69
+ """
70
+ pass
71
+
72
+ @abstractmethod
73
+ def remove_edge(self, u: Any, v: Any) -> bool:
74
+ """
75
+ Remove an edge between vertices u and v.
76
+
77
+ Args:
78
+ u: Source vertex
79
+ v: Target vertex
80
+
81
+ Returns:
82
+ True if edge was found and removed, False otherwise
83
+ """
84
+ pass
85
+
86
+ @abstractmethod
87
+ def has_edge(self, u: Any, v: Any) -> bool:
88
+ """
89
+ Check if an edge exists between vertices u and v.
90
+
91
+ Args:
92
+ u: Source vertex
93
+ v: Target vertex
94
+
95
+ Returns:
96
+ True if edge exists, False otherwise
97
+ """
98
+ pass
99
+
100
+ @abstractmethod
101
+ def neighbors(self, u: Any) -> Iterator[Any]:
102
+ """
103
+ Get neighbors of vertex u.
104
+
105
+ Args:
106
+ u: Vertex to get neighbors for
107
+
108
+ Returns:
109
+ Iterator over neighbor vertices
110
+ """
111
+ pass
112
+
113
+ @abstractmethod
114
+ def degree(self, u: Any) -> int:
115
+ """
116
+ Get the degree (number of edges) of vertex u.
117
+
118
+ Args:
119
+ u: Vertex to get degree for
120
+
121
+ Returns:
122
+ Number of edges incident to u
123
+ """
124
+ pass
125
+
126
+ @abstractmethod
127
+ def edges(self) -> Iterator[tuple[Any, Any, Dict[str, Any]]]:
128
+ """
129
+ Get all edges in the graph.
130
+
131
+ Returns:
132
+ Iterator over (u, v, properties) tuples
133
+ """
134
+ pass
135
+
136
+ @abstractmethod
137
+ def vertices(self) -> Iterator[Any]:
138
+ """
139
+ Get all vertices in the graph.
140
+
141
+ Returns:
142
+ Iterator over vertices
143
+ """
144
+ pass
145
+
146
+ @abstractmethod
147
+ def __len__(self) -> int:
148
+ """Get the number of edges."""
149
+ pass
150
+
151
+ # ============================================================================
152
+ # CAPABILITY-BASED OPERATIONS (Optional)
153
+ # ============================================================================
154
+
155
+ def get_edge_weight(self, u: Any, v: Any) -> float:
156
+ """
157
+ Get weight of edge (u, v) (requires WEIGHTED trait).
158
+
159
+ Args:
160
+ u: Source vertex
161
+ v: Target vertex
162
+
163
+ Returns:
164
+ Weight of the edge
165
+
166
+ Raises:
167
+ UnsupportedCapabilityError: If WEIGHTED trait not supported
168
+ """
169
+ if EdgeTrait.WEIGHTED not in self.traits:
170
+ raise XWNodeUnsupportedCapabilityError("WEIGHTED", self.mode.name, [str(t) for t in self.traits])
171
+
172
+ # Default implementation for weighted strategies
173
+ edge_data = self._edges.get((u, v), {})
174
+ return edge_data.get('weight', 1.0)
175
+
176
+ def set_edge_weight(self, u: Any, v: Any, weight: float) -> None:
177
+ """
178
+ Set weight of edge (u, v) (requires WEIGHTED trait).
179
+
180
+ Args:
181
+ u: Source vertex
182
+ v: Target vertex
183
+ weight: New weight value
184
+
185
+ Raises:
186
+ UnsupportedCapabilityError: If WEIGHTED trait not supported
187
+ """
188
+ if EdgeTrait.WEIGHTED not in self.traits:
189
+ raise XWNodeUnsupportedCapabilityError("WEIGHTED", self.mode.name, [str(t) for t in self.traits])
190
+
191
+ # Default implementation for weighted strategies
192
+ if (u, v) in self._edges:
193
+ self._edges[(u, v)]['weight'] = weight
194
+
195
+ def get_edge_property(self, u: Any, v: Any, property_name: str) -> Any:
196
+ """
197
+ Get a property of edge (u, v).
198
+
199
+ Args:
200
+ u: Source vertex
201
+ v: Target vertex
202
+ property_name: Name of the property
203
+
204
+ Returns:
205
+ Property value, or None if not found
206
+ """
207
+ edge_data = self._edges.get((u, v), {})
208
+ return edge_data.get(property_name)
209
+
210
+ def set_edge_property(self, u: Any, v: Any, property_name: str, value: Any) -> None:
211
+ """
212
+ Set a property of edge (u, v).
213
+
214
+ Args:
215
+ u: Source vertex
216
+ v: Target vertex
217
+ property_name: Name of the property
218
+ value: Property value
219
+ """
220
+ if (u, v) in self._edges:
221
+ self._edges[(u, v)][property_name] = value
222
+
223
+ def get_spatial_edges(self, bounds: Dict[str, Any]) -> List[tuple[Any, Any, Dict[str, Any]]]:
224
+ """
225
+ Get edges within spatial bounds (requires SPATIAL trait).
226
+
227
+ Args:
228
+ bounds: Spatial bounds (e.g., {'x_min': 0, 'x_max': 10, 'y_min': 0, 'y_max': 10})
229
+
230
+ Returns:
231
+ List of (u, v, properties) tuples within bounds
232
+
233
+ Raises:
234
+ UnsupportedCapabilityError: If SPATIAL trait not supported
235
+ """
236
+ if EdgeTrait.SPATIAL not in self.traits:
237
+ raise XWNodeUnsupportedCapabilityError("SPATIAL", self.mode.name, [str(t) for t in self.traits])
238
+
239
+ # Default implementation for spatial strategies
240
+ # This would be overridden by actual spatial implementations
241
+ return list(self.edges())
242
+
243
+ def get_temporal_edges(self, start_time: Any, end_time: Any) -> List[tuple[Any, Any, Dict[str, Any]]]:
244
+ """
245
+ Get edges within temporal range (requires TEMPORAL trait).
246
+
247
+ Args:
248
+ start_time: Start time (inclusive)
249
+ end_time: End time (exclusive)
250
+
251
+ Returns:
252
+ List of (u, v, properties) tuples within time range
253
+
254
+ Raises:
255
+ UnsupportedCapabilityError: If TEMPORAL trait not supported
256
+ """
257
+ if EdgeTrait.TEMPORAL not in self.traits:
258
+ raise XWNodeUnsupportedCapabilityError("TEMPORAL", self.mode.name, [str(t) for t in self.traits])
259
+
260
+ # Default implementation for temporal strategies
261
+ result = []
262
+ for u, v, props in self.edges():
263
+ edge_time = props.get('time', 0)
264
+ if start_time <= edge_time < end_time:
265
+ result.append((u, v, props))
266
+ return result
267
+
268
+ def add_hyperedge(self, vertices: List[Any], **properties) -> str:
269
+ """
270
+ Add a hyperedge connecting multiple vertices (requires HYPER trait).
271
+
272
+ Args:
273
+ vertices: List of vertices to connect
274
+ **properties: Hyperedge properties
275
+
276
+ Returns:
277
+ Hyperedge identifier
278
+
279
+ Raises:
280
+ UnsupportedCapabilityError: If HYPER trait not supported
281
+ """
282
+ if EdgeTrait.HYPER not in self.traits:
283
+ raise XWNodeUnsupportedCapabilityError("HYPER", self.mode.name, [str(t) for t in self.traits])
284
+
285
+ # Default implementation for hyperedge strategies
286
+ # This would be overridden by actual hyperedge implementations
287
+ raise NotImplementedError("Hyperedge support not implemented in base class")
288
+
289
+ # ============================================================================
290
+ # GRAPH ANALYSIS OPERATIONS
291
+ # ============================================================================
292
+
293
+ def vertex_count(self) -> int:
294
+ """Get the number of vertices."""
295
+ return len(self._vertices)
296
+
297
+ def edge_count(self) -> int:
298
+ """Get the number of edges."""
299
+ return len(self)
300
+
301
+ def density(self) -> float:
302
+ """Calculate graph density."""
303
+ n = self.vertex_count()
304
+ if n <= 1:
305
+ return 0.0
306
+ return self.edge_count() / (n * (n - 1))
307
+
308
+ def is_directed(self) -> bool:
309
+ """Check if graph is directed."""
310
+ return EdgeTrait.DIRECTED in self.traits
311
+
312
+ def is_weighted(self) -> bool:
313
+ """Check if graph is weighted."""
314
+ return EdgeTrait.WEIGHTED in self.traits
315
+
316
+ def is_spatial(self) -> bool:
317
+ """Check if graph has spatial properties."""
318
+ return EdgeTrait.SPATIAL in self.traits
319
+
320
+ def is_temporal(self) -> bool:
321
+ """Check if graph has temporal properties."""
322
+ return EdgeTrait.TEMPORAL in self.traits
323
+
324
+ def is_hyper(self) -> bool:
325
+ """Check if graph supports hyperedges."""
326
+ return EdgeTrait.HYPER in self.traits
327
+
328
+ # ============================================================================
329
+ # STRATEGY METADATA
330
+ # ============================================================================
331
+
332
+ def capabilities(self) -> EdgeTrait:
333
+ """Get the capabilities supported by this strategy."""
334
+ return self.traits
335
+
336
+ def backend_info(self) -> Dict[str, Any]:
337
+ """Get information about the backend implementation."""
338
+ return {
339
+ "mode": self.mode.name,
340
+ "traits": str(self.traits),
341
+ "vertices": self.vertex_count(),
342
+ "edges": self.edge_count(),
343
+ "density": self.density(),
344
+ "options": self.options.copy()
345
+ }
346
+
347
+ def metrics(self) -> Dict[str, Any]:
348
+ """Get performance metrics for this strategy."""
349
+ return {
350
+ "vertices": self.vertex_count(),
351
+ "edges": self.edge_count(),
352
+ "density": self.density(),
353
+ "mode": self.mode.name,
354
+ "traits": str(self.traits)
355
+ }
356
+
357
+ # ============================================================================
358
+ # UTILITY METHODS
359
+ # ============================================================================
360
+
361
+ def clear(self) -> None:
362
+ """Clear all edges and vertices."""
363
+ self._edges.clear()
364
+ self._vertices.clear()
365
+ self._edge_count = 0
366
+
367
+ def add_vertex(self, v: Any) -> None:
368
+ """Add a vertex to the graph."""
369
+ self._vertices.add(v)
370
+
371
+ def remove_vertex(self, v: Any) -> bool:
372
+ """Remove a vertex and all its incident edges."""
373
+ if v not in self._vertices:
374
+ return False
375
+
376
+ # Remove all edges incident to v
377
+ edges_to_remove = []
378
+ for (u, w) in self._edges.keys():
379
+ if u == v or w == v:
380
+ edges_to_remove.append((u, w))
381
+
382
+ for (u, w) in edges_to_remove:
383
+ self.remove_edge(u, w)
384
+
385
+ self._vertices.remove(v)
386
+ return True
387
+
388
+ def __contains__(self, edge: tuple[Any, Any]) -> bool:
389
+ """Check if edge exists."""
390
+ u, v = edge
391
+ return self.has_edge(u, v)
392
+
393
+ def __str__(self) -> str:
394
+ """String representation."""
395
+ return f"{self.__class__.__name__}(mode={self.mode.name}, vertices={self.vertex_count()}, edges={self.edge_count()})"
396
+
397
+ def __repr__(self) -> str:
398
+ """Detailed string representation."""
399
+ return f"{self.__class__.__name__}(mode={self.mode.name}, traits={self.traits}, vertices={self.vertex_count()}, edges={self.edge_count()})"
400
+
401
+
402
+ # Import here to avoid circular imports
403
+ from ...errors import XWNodeUnsupportedCapabilityError
@@ -0,0 +1,307 @@
1
+ """
2
+ Abstract Node Strategy Interface
3
+
4
+ This module defines the abstract base class that all node strategies must implement
5
+ in the strategy system.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, Dict, List, Optional, Iterator, Union
10
+ from ...types import NodeMode, NodeTrait
11
+ from ...errors import XWNodeUnsupportedCapabilityError
12
+
13
+
14
+ class aNodeStrategy(ABC):
15
+ """
16
+ Abstract base class for all node strategies.
17
+
18
+ This abstract base class defines the contract that all node strategy
19
+ implementations must follow, ensuring consistency and interoperability.
20
+ """
21
+
22
+ def __init__(self, mode: NodeMode, traits: NodeTrait = NodeTrait.NONE, **options):
23
+ """Initialize the abstract node strategy."""
24
+ self.mode = mode
25
+ self.traits = traits
26
+ self.options = options
27
+ self._data: Dict[str, Any] = {}
28
+ self._size = 0
29
+
30
+ # Validate traits compatibility with mode
31
+ self._validate_traits()
32
+
33
+ def _validate_traits(self) -> None:
34
+ """Validate that the requested traits are compatible with this strategy."""
35
+ supported_traits = self.get_supported_traits()
36
+ unsupported = self.traits & ~supported_traits
37
+ if unsupported != NodeTrait.NONE:
38
+ unsupported_names = [trait.name for trait in NodeTrait if trait in unsupported]
39
+ raise ValueError(f"Strategy {self.mode.name} does not support traits: {unsupported_names}")
40
+
41
+ def get_supported_traits(self) -> NodeTrait:
42
+ """Get the traits supported by this strategy implementation."""
43
+ # Default implementation - subclasses should override
44
+ return NodeTrait.NONE
45
+
46
+ def has_trait(self, trait: NodeTrait) -> bool:
47
+ """Check if this strategy has a specific trait."""
48
+ return bool(self.traits & trait)
49
+
50
+ def require_trait(self, trait: NodeTrait, operation: str = "operation") -> None:
51
+ """Require a specific trait for an operation."""
52
+ if not self.has_trait(trait):
53
+ from ...errors import UnsupportedCapabilityError
54
+ raise UnsupportedCapabilityError(f"{operation} requires {trait.name} capability")
55
+
56
+ # ============================================================================
57
+ # CORE OPERATIONS (Required)
58
+ # ============================================================================
59
+
60
+ @abstractmethod
61
+ def put(self, key: Any, value: Any = None) -> None:
62
+ """
63
+ Store a key-value pair.
64
+
65
+ Args:
66
+ key: The key to store
67
+ value: The value to associate with the key
68
+ """
69
+ pass
70
+
71
+ @abstractmethod
72
+ def get(self, key: Any, default: Any = None) -> Any:
73
+ """
74
+ Retrieve a value by key.
75
+
76
+ Args:
77
+ key: The key to look up
78
+ default: Default value if key not found
79
+
80
+ Returns:
81
+ The value associated with the key, or default if not found
82
+ """
83
+ pass
84
+
85
+ @abstractmethod
86
+ def delete(self, key: Any) -> bool:
87
+ """
88
+ Remove a key-value pair.
89
+
90
+ Args:
91
+ key: The key to remove
92
+
93
+ Returns:
94
+ True if key was found and removed, False otherwise
95
+ """
96
+ pass
97
+
98
+ @abstractmethod
99
+ def has(self, key: Any) -> bool:
100
+ """
101
+ Check if a key exists.
102
+
103
+ Args:
104
+ key: The key to check
105
+
106
+ Returns:
107
+ True if key exists, False otherwise
108
+ """
109
+ pass
110
+
111
+ @abstractmethod
112
+ def __len__(self) -> int:
113
+ """Get the number of key-value pairs."""
114
+ pass
115
+
116
+ @abstractmethod
117
+ def keys(self) -> Iterator[Any]:
118
+ """Get an iterator over all keys."""
119
+ pass
120
+
121
+ @abstractmethod
122
+ def values(self) -> Iterator[Any]:
123
+ """Get an iterator over all values."""
124
+ pass
125
+
126
+ @abstractmethod
127
+ def items(self) -> Iterator[tuple[Any, Any]]:
128
+ """Get an iterator over all key-value pairs."""
129
+ pass
130
+
131
+ # ============================================================================
132
+ # CAPABILITY-BASED OPERATIONS (Optional)
133
+ # ============================================================================
134
+
135
+ def get_ordered(self, start: Any = None, end: Any = None) -> List[tuple[Any, Any]]:
136
+ """
137
+ Get items in order (requires ORDERED trait).
138
+
139
+ Args:
140
+ start: Start key (inclusive)
141
+ end: End key (exclusive)
142
+
143
+ Returns:
144
+ List of (key, value) pairs in order
145
+
146
+ Raises:
147
+ UnsupportedCapabilityError: If ORDERED trait not supported
148
+ """
149
+ if NodeTrait.ORDERED not in self.traits:
150
+ raise XWNodeUnsupportedCapabilityError("ORDERED", self.mode.name, [str(t) for t in self.traits])
151
+
152
+ # Default implementation for ordered strategies
153
+ items = list(self.items())
154
+ if start is not None:
155
+ items = [(k, v) for k, v in items if k >= start]
156
+ if end is not None:
157
+ items = [(k, v) for k, v in items if k < end]
158
+ return items
159
+
160
+ def get_with_prefix(self, prefix: str) -> List[tuple[Any, Any]]:
161
+ """
162
+ Get items with given prefix (requires HIERARCHICAL trait).
163
+
164
+ Args:
165
+ prefix: The prefix to match
166
+
167
+ Returns:
168
+ List of (key, value) pairs with matching prefix
169
+
170
+ Raises:
171
+ UnsupportedCapabilityError: If HIERARCHICAL trait not supported
172
+ """
173
+ if NodeTrait.HIERARCHICAL not in self.traits:
174
+ raise XWNodeUnsupportedCapabilityError("HIERARCHICAL", self.mode.name, [str(t) for t in self.traits])
175
+
176
+ # Default implementation for hierarchical strategies
177
+ return [(k, v) for k, v in self.items() if str(k).startswith(prefix)]
178
+
179
+ def get_priority(self) -> Optional[tuple[Any, Any]]:
180
+ """
181
+ Get highest priority item (requires PRIORITY trait).
182
+
183
+ Returns:
184
+ (key, value) pair with highest priority, or None if empty
185
+
186
+ Raises:
187
+ UnsupportedCapabilityError: If PRIORITY trait not supported
188
+ """
189
+ if NodeTrait.PRIORITY not in self.traits:
190
+ raise XWNodeUnsupportedCapabilityError("PRIORITY", self.mode.name, [str(t) for t in self.traits])
191
+
192
+ # Default implementation for priority strategies
193
+ if not self._data:
194
+ return None
195
+ return min(self.items(), key=lambda x: x[0])
196
+
197
+ def get_weighted(self, key: Any) -> float:
198
+ """
199
+ Get weight for a key (requires WEIGHTED trait).
200
+
201
+ Args:
202
+ key: The key to get weight for
203
+
204
+ Returns:
205
+ Weight value for the key
206
+
207
+ Raises:
208
+ UnsupportedCapabilityError: If WEIGHTED trait not supported
209
+ """
210
+ if NodeTrait.WEIGHTED not in self.traits:
211
+ raise XWNodeUnsupportedCapabilityError("WEIGHTED", self.mode.name, [str(t) for t in self.traits])
212
+
213
+ # Default implementation for weighted strategies
214
+ return self._data.get(key, {}).get('weight', 1.0)
215
+
216
+ # ============================================================================
217
+ # STRATEGY METADATA
218
+ # ============================================================================
219
+
220
+ def capabilities(self) -> NodeTrait:
221
+ """Get the capabilities supported by this strategy."""
222
+ return self.traits
223
+
224
+ def backend_info(self) -> Dict[str, Any]:
225
+ """Get information about the backend implementation."""
226
+ return {
227
+ "mode": self.mode.name,
228
+ "traits": str(self.traits),
229
+ "size": len(self),
230
+ "options": self.options.copy()
231
+ }
232
+
233
+ def metrics(self) -> Dict[str, Any]:
234
+ """Get performance metrics for this strategy."""
235
+ return {
236
+ "size": len(self),
237
+ "mode": self.mode.name,
238
+ "traits": str(self.traits)
239
+ }
240
+
241
+ # ============================================================================
242
+ # FACTORY METHODS
243
+ # ============================================================================
244
+
245
+ @classmethod
246
+ def create_from_data(cls, data: Any) -> 'aNodeStrategy':
247
+ """
248
+ Create a new strategy instance from data.
249
+
250
+ Args:
251
+ data: The data to create the strategy from
252
+
253
+ Returns:
254
+ A new strategy instance containing the data
255
+ """
256
+ instance = cls()
257
+ if isinstance(data, dict):
258
+ for key, value in data.items():
259
+ instance.put(key, value)
260
+ elif isinstance(data, (list, tuple)):
261
+ for i, value in enumerate(data):
262
+ instance.put(i, value)
263
+ else:
264
+ # For primitive values, store as root value
265
+ instance.put('_value', data)
266
+ return instance
267
+
268
+ # ============================================================================
269
+ # UTILITY METHODS
270
+ # ============================================================================
271
+
272
+ def clear(self) -> None:
273
+ """Clear all data."""
274
+ self._data.clear()
275
+ self._size = 0
276
+
277
+ def __contains__(self, key: Any) -> bool:
278
+ """Check if key exists."""
279
+ return self.has(key)
280
+
281
+ def __getitem__(self, key: Any) -> Any:
282
+ """Get value by key."""
283
+ return self.get(key)
284
+
285
+ def __setitem__(self, key: Any, value: Any) -> None:
286
+ """Set value by key."""
287
+ self.put(key, value)
288
+
289
+ def __delitem__(self, key: Any) -> None:
290
+ """Delete key."""
291
+ if not self.delete(key):
292
+ raise KeyError(key)
293
+
294
+ def __iter__(self) -> Iterator[Any]:
295
+ """Iterate over keys."""
296
+ return self.keys()
297
+
298
+ def __str__(self) -> str:
299
+ """String representation."""
300
+ return f"{self.__class__.__name__}(mode={self.mode.name}, size={len(self)})"
301
+
302
+ def __repr__(self) -> str:
303
+ """Detailed string representation."""
304
+ return f"{self.__class__.__name__}(mode={self.mode.name}, traits={self.traits}, size={len(self)})"
305
+
306
+
307
+