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,532 @@
1
+ """
2
+ Shared utilities for XWNode strategies.
3
+
4
+ This module provides common functionality that can be used by multiple strategies
5
+ without creating cross-dependencies between strategies.
6
+ """
7
+
8
+ import threading
9
+ from typing import Any, Dict, List, Optional, Iterator, Tuple, Union
10
+ from collections import OrderedDict
11
+ import weakref
12
+ import time
13
+
14
+ # Use xSystem logging
15
+ from exonware.xwsystem import get_logger
16
+
17
+ logger = get_logger('xnode.strategies.utils')
18
+
19
+
20
+ # ============================================================================
21
+ # PATH PARSING UTILITIES
22
+ # ============================================================================
23
+
24
+ class PathParser:
25
+ """Thread-safe path parser with caching for use by multiple strategies."""
26
+
27
+ def __init__(self, max_cache_size: int = 1024):
28
+ self._cache = OrderedDict()
29
+ self._max_cache_size = max_cache_size
30
+ self._lock = threading.RLock()
31
+
32
+ def parse(self, path: str) -> List[str]:
33
+ """Parse a path string into parts."""
34
+ with self._lock:
35
+ if path in self._cache:
36
+ return self._cache[path]
37
+
38
+ parts = self._parse_path(path)
39
+
40
+ # Cache the result
41
+ if len(self._cache) >= self._max_cache_size:
42
+ self._cache.popitem(last=False)
43
+ self._cache[path] = parts
44
+
45
+ return parts
46
+
47
+ def _parse_path(self, path: str) -> List[str]:
48
+ """Internal path parsing logic."""
49
+ if not path:
50
+ return []
51
+
52
+ # Simple dot-separated path parsing
53
+ return [part for part in path.split('.') if part]
54
+
55
+
56
+ # ============================================================================
57
+ # ADVANCED DATA STRUCTURES (Shared implementations)
58
+ # ============================================================================
59
+
60
+ class TrieNode:
61
+ """Internal node for Trie structure - shared across strategies."""
62
+
63
+ def __init__(self):
64
+ self.children: Dict[str, 'TrieNode'] = {}
65
+ self.is_end_word: bool = False
66
+ self.value: Any = None
67
+
68
+ def __repr__(self):
69
+ return f"TrieNode(children={len(self.children)}, is_end={self.is_end_word})"
70
+
71
+
72
+ class UnionFind:
73
+ """Union-Find (Disjoint Set) data structure - shared across strategies."""
74
+
75
+ def __init__(self):
76
+ self._parent: Dict[Any, Any] = {}
77
+ self._rank: Dict[Any, int] = {}
78
+ self._sets_count = 0
79
+
80
+ def make_set(self, x: Any) -> None:
81
+ """Create new set with element x. O(1)"""
82
+ if x not in self._parent:
83
+ self._parent[x] = x
84
+ self._rank[x] = 0
85
+ self._sets_count += 1
86
+
87
+ def find(self, x: Any) -> Any:
88
+ """Find root of set containing x with path compression. α(n) ≈ O(1)"""
89
+ if x not in self._parent:
90
+ raise ValueError(f"Element {x} not found in union-find structure")
91
+
92
+ # Path compression
93
+ if self._parent[x] != x:
94
+ self._parent[x] = self.find(self._parent[x])
95
+
96
+ return self._parent[x]
97
+
98
+ def union(self, x: Any, y: Any) -> None:
99
+ """Union sets containing x and y by rank. α(n) ≈ O(1)"""
100
+ # Ensure both elements exist
101
+ self.make_set(x)
102
+ self.make_set(y)
103
+
104
+ root_x = self.find(x)
105
+ root_y = self.find(y)
106
+
107
+ if root_x == root_y:
108
+ return # Already in same set
109
+
110
+ # Union by rank
111
+ if self._rank[root_x] < self._rank[root_y]:
112
+ root_x, root_y = root_y, root_x
113
+
114
+ self._parent[root_y] = root_x
115
+ if self._rank[root_x] == self._rank[root_y]:
116
+ self._rank[root_x] += 1
117
+
118
+ self._sets_count -= 1
119
+
120
+ def connected(self, x: Any, y: Any) -> bool:
121
+ """Check if x and y are in same set. α(n) ≈ O(1)"""
122
+ try:
123
+ return self.find(x) == self.find(y)
124
+ except ValueError:
125
+ return False
126
+
127
+ def size(self) -> int:
128
+ """Get number of elements. O(1)"""
129
+ return len(self._parent)
130
+
131
+ def sets_count(self) -> int:
132
+ """Get number of disjoint sets. O(1)"""
133
+ return self._sets_count
134
+
135
+
136
+ class MinHeap:
137
+ """Min-heap implementation for priority queue operations - shared across strategies."""
138
+
139
+ def __init__(self):
140
+ self._heap: List[Tuple[float, Any]] = []
141
+ self._size = 0
142
+
143
+ def push(self, value: Any, priority: float = 0.0) -> None:
144
+ """Push item with priority. O(log n)"""
145
+ self._heap.append((priority, value))
146
+ self._size += 1
147
+ self._heapify_up(self._size - 1)
148
+
149
+ def pop_min(self) -> Any:
150
+ """Pop minimum priority item. O(log n)"""
151
+ if self._size == 0:
152
+ raise IndexError("Heap is empty")
153
+
154
+ min_val = self._heap[0][1]
155
+ self._heap[0] = self._heap[self._size - 1]
156
+ self._heap.pop()
157
+ self._size -= 1
158
+
159
+ if self._size > 0:
160
+ self._heapify_down(0)
161
+
162
+ return min_val
163
+
164
+ def peek_min(self) -> Any:
165
+ """Peek at minimum without removing. O(1)"""
166
+ if self._size == 0:
167
+ raise IndexError("Heap is empty")
168
+ return self._heap[0][1]
169
+
170
+ def _heapify_up(self, index: int) -> None:
171
+ """Move element up to maintain heap property."""
172
+ parent = (index - 1) // 2
173
+ if parent >= 0 and self._heap[index][0] < self._heap[parent][0]:
174
+ self._heap[index], self._heap[parent] = self._heap[parent], self._heap[index]
175
+ self._heapify_up(parent)
176
+
177
+ def _heapify_down(self, index: int) -> None:
178
+ """Move element down to maintain heap property."""
179
+ smallest = index
180
+ left = 2 * index + 1
181
+ right = 2 * index + 2
182
+
183
+ if left < self._size and self._heap[left][0] < self._heap[smallest][0]:
184
+ smallest = left
185
+
186
+ if right < self._size and self._heap[right][0] < self._heap[smallest][0]:
187
+ smallest = right
188
+
189
+ if smallest != index:
190
+ self._heap[index], self._heap[smallest] = self._heap[smallest], self._heap[index]
191
+ self._heapify_down(smallest)
192
+
193
+ def size(self) -> int:
194
+ """Get heap size. O(1)"""
195
+ return self._size
196
+
197
+ def is_empty(self) -> bool:
198
+ """Check if heap is empty. O(1)"""
199
+ return self._size == 0
200
+
201
+
202
+ # ============================================================================
203
+ # COMMON UTILITY FUNCTIONS
204
+ # ============================================================================
205
+
206
+ def recursive_to_native(obj: Any) -> Any:
207
+ """
208
+ Recursively convert objects to native Python types.
209
+
210
+ This is a shared utility for converting complex objects (including XWNode objects)
211
+ to native Python types for serialization and comparison.
212
+ """
213
+ if hasattr(obj, 'to_native'):
214
+ # This is an XWNode, recursively convert it
215
+ return recursive_to_native(obj.to_native())
216
+ elif isinstance(obj, dict):
217
+ return {k: recursive_to_native(v) for k, v in obj.items()}
218
+ elif isinstance(obj, list):
219
+ return [recursive_to_native(item) for item in obj]
220
+ else:
221
+ return obj
222
+
223
+
224
+ def is_sequential_numeric_keys(data: Dict[str, Any]) -> bool:
225
+ """
226
+ Check if dictionary keys are sequential numeric indices (for list detection).
227
+
228
+ This is useful for determining if a dict represents a list structure.
229
+ """
230
+ if not data:
231
+ return False
232
+
233
+ keys = list(data.keys())
234
+ try:
235
+ indices = [int(k) for k in keys]
236
+ return indices == list(range(len(indices)))
237
+ except ValueError:
238
+ return False
239
+
240
+
241
+ def calculate_structural_hash(data: Dict[str, Any]) -> int:
242
+ """
243
+ Calculate a structural hash based on keys only (not values).
244
+
245
+ This is useful for fast equality checking when values don't matter.
246
+ """
247
+ return hash(tuple(sorted(data.keys())))
248
+
249
+
250
+ def validate_traits(supported_traits, requested_traits, strategy_name: str) -> None:
251
+ """
252
+ Validate that requested traits are supported by a strategy.
253
+
254
+ Args:
255
+ supported_traits: Traits supported by the strategy
256
+ requested_traits: Traits requested by the user
257
+ strategy_name: Name of the strategy for error messages
258
+ """
259
+ unsupported = requested_traits & ~supported_traits
260
+ if unsupported != 0:
261
+ unsupported_names = [trait.name for trait in unsupported]
262
+ raise ValueError(f"Strategy {strategy_name} does not support traits: {unsupported_names}")
263
+
264
+
265
+ # ============================================================================
266
+ # PERFORMANCE MONITORING UTILITIES
267
+ # ============================================================================
268
+
269
+ class PerformanceTracker:
270
+ """Shared performance tracking utilities for strategies."""
271
+
272
+ def __init__(self):
273
+ self._access_count = 0
274
+ self._cache_hits = 0
275
+ self._cache_misses = 0
276
+ self._operation_times: Dict[str, List[float]] = {}
277
+ self._lock = threading.RLock()
278
+
279
+ def record_access(self) -> None:
280
+ """Record a data access operation."""
281
+ with self._lock:
282
+ self._access_count += 1
283
+
284
+ def record_cache_hit(self) -> None:
285
+ """Record a cache hit."""
286
+ with self._lock:
287
+ self._cache_hits += 1
288
+
289
+ def record_cache_miss(self) -> None:
290
+ """Record a cache miss."""
291
+ with self._lock:
292
+ self._cache_misses += 1
293
+
294
+ def record_operation_time(self, operation: str, time_taken: float) -> None:
295
+ """Record the time taken for an operation."""
296
+ with self._lock:
297
+ if operation not in self._operation_times:
298
+ self._operation_times[operation] = []
299
+ self._operation_times[operation].append(time_taken)
300
+
301
+ def get_metrics(self) -> Dict[str, Any]:
302
+ """Get performance metrics."""
303
+ with self._lock:
304
+ metrics = {
305
+ 'access_count': self._access_count,
306
+ 'cache_hits': self._cache_hits,
307
+ 'cache_misses': self._cache_misses,
308
+ }
309
+
310
+ # Calculate cache hit rate
311
+ total_cache_ops = self._cache_hits + self._cache_misses
312
+ if total_cache_ops > 0:
313
+ metrics['cache_hit_rate'] = self._cache_hits / total_cache_ops
314
+ else:
315
+ metrics['cache_hit_rate'] = 0.0
316
+
317
+ # Calculate average operation times
318
+ for operation, times in self._operation_times.items():
319
+ if times:
320
+ metrics[f'{operation}_avg_time'] = sum(times) / len(times)
321
+ metrics[f'{operation}_min_time'] = min(times)
322
+ metrics[f'{operation}_max_time'] = max(times)
323
+
324
+ return metrics
325
+
326
+ def reset(self) -> None:
327
+ """Reset all performance counters."""
328
+ with self._lock:
329
+ self._access_count = 0
330
+ self._cache_hits = 0
331
+ self._cache_misses = 0
332
+ self._operation_times.clear()
333
+
334
+
335
+ # ============================================================================
336
+ # OBJECT POOLING UTILITIES
337
+ # ============================================================================
338
+
339
+ class ObjectPool:
340
+ """Generic object pool for strategies that need pooling."""
341
+
342
+ def __init__(self, max_size: int = 100):
343
+ self._pool: List[Any] = []
344
+ self._max_size = max_size
345
+ self._lock = threading.RLock()
346
+ self._stats = {
347
+ 'created': 0,
348
+ 'reused': 0,
349
+ 'pooled': 0
350
+ }
351
+
352
+ def get_object(self, factory_func, *args, **kwargs) -> Any:
353
+ """
354
+ Get an object from the pool or create a new one.
355
+
356
+ Args:
357
+ factory_func: Function to create new objects
358
+ *args, **kwargs: Arguments for factory function
359
+
360
+ Returns:
361
+ Object from pool or newly created
362
+ """
363
+ with self._lock:
364
+ if self._pool:
365
+ # Reuse existing object
366
+ obj = self._pool.pop()
367
+ self._stats['reused'] += 1
368
+ logger.debug(f"♻️ Reused object from pool")
369
+ return obj
370
+ else:
371
+ # Create new object
372
+ obj = factory_func(*args, **kwargs)
373
+ self._stats['created'] += 1
374
+ logger.debug(f"🆕 Created new object")
375
+ return obj
376
+
377
+ def return_object(self, obj: Any, reset_func=None) -> None:
378
+ """
379
+ Return an object to the pool for reuse.
380
+
381
+ Args:
382
+ obj: Object to return to pool
383
+ reset_func: Optional function to reset object state
384
+ """
385
+ with self._lock:
386
+ if len(self._pool) < self._max_size:
387
+ if reset_func:
388
+ reset_func(obj)
389
+ self._pool.append(obj)
390
+ self._stats['pooled'] += 1
391
+ logger.debug(f"🔄 Returned object to pool")
392
+
393
+ def get_stats(self) -> Dict[str, int]:
394
+ """Get pool statistics."""
395
+ with self._lock:
396
+ stats = self._stats.copy()
397
+ stats['pool_size'] = len(self._pool)
398
+ return stats
399
+
400
+ @property
401
+ def efficiency(self) -> float:
402
+ """Get pool efficiency (reuse rate)."""
403
+ total = self._stats['created'] + self._stats['reused']
404
+ return self._stats['reused'] / total if total > 0 else 0.0
405
+
406
+ def clear(self) -> None:
407
+ """Clear all pooled objects."""
408
+ with self._lock:
409
+ self._pool.clear()
410
+ logger.info("🧹 Cleared object pool")
411
+
412
+
413
+ # ============================================================================
414
+ # FACTORY FUNCTIONS
415
+ # ============================================================================
416
+
417
+ def create_path_parser(max_cache_size: int = 1024) -> PathParser:
418
+ """Create a path parser instance."""
419
+ return PathParser(max_cache_size)
420
+
421
+
422
+ def create_performance_tracker() -> PerformanceTracker:
423
+ """Create a performance tracker instance."""
424
+ return PerformanceTracker()
425
+
426
+
427
+ def create_object_pool(max_size: int = 100) -> ObjectPool:
428
+ """Create an object pool instance."""
429
+ return ObjectPool(max_size)
430
+
431
+
432
+ def create_basic_metrics(strategy_name: str, size: int, **additional_metrics) -> Dict[str, Any]:
433
+ """Create basic metrics dictionary for strategies."""
434
+ metrics = {
435
+ 'strategy': strategy_name,
436
+ 'size': size,
437
+ 'memory_usage': f"{size * 64} bytes (estimated)",
438
+ 'timestamp': time.time()
439
+ }
440
+ metrics.update(additional_metrics)
441
+ return metrics
442
+
443
+
444
+ def create_basic_backend_info(strategy_name: str, backend_type: str, **additional_info) -> Dict[str, Any]:
445
+ """Create basic backend info dictionary for strategies."""
446
+ info = {
447
+ 'strategy': strategy_name,
448
+ 'backend': backend_type,
449
+ 'complexity': {
450
+ 'get': 'O(1) average',
451
+ 'put': 'O(1) average',
452
+ 'has': 'O(1) average',
453
+ 'remove': 'O(1) average'
454
+ }
455
+ }
456
+ info.update(additional_info)
457
+ return info
458
+
459
+
460
+ def is_list_like(keys: List[str]) -> bool:
461
+ """Check if keys represent a list-like structure."""
462
+ if not keys:
463
+ return False
464
+
465
+ # Check if all keys are numeric and consecutive starting from 0
466
+ try:
467
+ numeric_keys = [int(key) for key in keys]
468
+ return numeric_keys == list(range(len(numeric_keys)))
469
+ except (ValueError, TypeError):
470
+ return False
471
+
472
+
473
+ def safe_to_native_conversion(data: Any) -> Any:
474
+ """Safely convert data to native Python types, handling XWNode objects."""
475
+ if hasattr(data, 'to_native'):
476
+ # This is an XWNode, recursively convert it
477
+ return safe_to_native_conversion(data.to_native())
478
+ elif isinstance(data, dict):
479
+ return {k: safe_to_native_conversion(v) for k, v in data.items()}
480
+ elif isinstance(data, list):
481
+ return [safe_to_native_conversion(item) for item in data]
482
+ elif isinstance(data, (set, frozenset)):
483
+ return {safe_to_native_conversion(item) for item in data}
484
+ else:
485
+ return data
486
+
487
+
488
+ def create_strategy_logger(strategy_name: str):
489
+ """Create a logger for a specific strategy."""
490
+ return get_logger(f"xnode.strategy.{strategy_name}")
491
+
492
+
493
+ def validate_strategy_options(options: Dict[str, Any], allowed_options: List[str]) -> Dict[str, Any]:
494
+ """Validate strategy options and return only allowed ones."""
495
+ return {k: v for k, v in options.items() if k in allowed_options}
496
+
497
+
498
+ def create_size_tracker() -> Dict[str, int]:
499
+ """Create a size tracking dictionary."""
500
+ return {'size': 0}
501
+
502
+
503
+ def update_size_tracker(tracker: Dict[str, int], delta: int) -> None:
504
+ """Update size tracker with delta change."""
505
+ tracker['size'] = max(0, tracker['size'] + delta)
506
+
507
+
508
+ def create_access_tracker() -> Dict[str, int]:
509
+ """Create an access tracking dictionary."""
510
+ return {
511
+ 'get_count': 0,
512
+ 'put_count': 0,
513
+ 'delete_count': 0,
514
+ 'access_count': 0
515
+ }
516
+
517
+
518
+ def record_access(tracker: Dict[str, int], operation: str) -> None:
519
+ """Record an access operation."""
520
+ if operation in tracker:
521
+ tracker[operation] += 1
522
+ tracker['access_count'] += 1
523
+
524
+
525
+ def get_access_metrics(tracker: Dict[str, int]) -> Dict[str, Any]:
526
+ """Get access metrics from tracker."""
527
+ return {
528
+ 'total_accesses': tracker['access_count'],
529
+ 'get_operations': tracker['get_count'],
530
+ 'put_operations': tracker['put_count'],
531
+ 'delete_operations': tracker['delete_count']
532
+ }