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,169 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Edge Strategy Base Classes
|
4
|
+
|
5
|
+
This module defines the abstract base classes for all edge strategy implementations:
|
6
|
+
- AEdgeStrategy: Base strategy for all edge implementations
|
7
|
+
- ALinearEdgeStrategy: Linear edge capabilities (sequential connections)
|
8
|
+
- ATreeEdgeStrategy: Tree edge capabilities (hierarchical connections)
|
9
|
+
- AGraphEdgeStrategy: Graph edge capabilities (network connections)
|
10
|
+
|
11
|
+
Company: eXonware.com
|
12
|
+
Author: Eng. Muhammad AlShehri
|
13
|
+
Email: connect@exonware.com
|
14
|
+
Version: 0.0.1.12
|
15
|
+
Generation Date: January 2, 2025
|
16
|
+
"""
|
17
|
+
|
18
|
+
from abc import ABC, abstractmethod
|
19
|
+
from typing import Any, Optional, List, Dict, Tuple
|
20
|
+
|
21
|
+
from ...contracts import iEdgeStrategy
|
22
|
+
from ...errors import XWNodeTypeError, XWNodeValueError
|
23
|
+
|
24
|
+
|
25
|
+
class AEdgeStrategy(ABC):
|
26
|
+
"""Base strategy for all edge implementations."""
|
27
|
+
|
28
|
+
def __init__(self, **options):
|
29
|
+
"""Initialize edge strategy."""
|
30
|
+
self._options = options
|
31
|
+
self._mode = options.get('mode', 'AUTO')
|
32
|
+
self._traits = options.get('traits', None)
|
33
|
+
|
34
|
+
@abstractmethod
|
35
|
+
def add_edge(self, from_node: Any, to_node: Any, **kwargs) -> None:
|
36
|
+
"""Add edge between nodes."""
|
37
|
+
pass
|
38
|
+
|
39
|
+
@abstractmethod
|
40
|
+
def remove_edge(self, from_node: Any, to_node: Any) -> bool:
|
41
|
+
"""Remove edge between nodes."""
|
42
|
+
pass
|
43
|
+
|
44
|
+
@abstractmethod
|
45
|
+
def has_edge(self, from_node: Any, to_node: Any) -> bool:
|
46
|
+
"""Check if edge exists."""
|
47
|
+
pass
|
48
|
+
|
49
|
+
@abstractmethod
|
50
|
+
def get_edge_count(self) -> int:
|
51
|
+
"""Get total number of edges."""
|
52
|
+
pass
|
53
|
+
|
54
|
+
@abstractmethod
|
55
|
+
def get_vertex_count(self) -> int:
|
56
|
+
"""Get total number of vertices."""
|
57
|
+
pass
|
58
|
+
|
59
|
+
def get_mode(self) -> str:
|
60
|
+
"""Get strategy mode."""
|
61
|
+
return self._mode
|
62
|
+
|
63
|
+
def get_traits(self):
|
64
|
+
"""Get strategy traits."""
|
65
|
+
return self._traits
|
66
|
+
|
67
|
+
|
68
|
+
class ALinearEdgeStrategy(AEdgeStrategy):
|
69
|
+
"""Linear edge capabilities (sequential connections)."""
|
70
|
+
|
71
|
+
def get_next(self, node: Any) -> Optional[Any]:
|
72
|
+
"""Get next node in sequence."""
|
73
|
+
raise NotImplementedError("Subclasses must implement get_next")
|
74
|
+
|
75
|
+
def get_previous(self, node: Any) -> Optional[Any]:
|
76
|
+
"""Get previous node in sequence."""
|
77
|
+
raise NotImplementedError("Subclasses must implement get_previous")
|
78
|
+
|
79
|
+
def get_first(self) -> Optional[Any]:
|
80
|
+
"""Get first node in sequence."""
|
81
|
+
raise NotImplementedError("Subclasses must implement get_first")
|
82
|
+
|
83
|
+
def get_last(self) -> Optional[Any]:
|
84
|
+
"""Get last node in sequence."""
|
85
|
+
raise NotImplementedError("Subclasses must implement get_last")
|
86
|
+
|
87
|
+
def insert_after(self, node: Any, new_node: Any) -> None:
|
88
|
+
"""Insert new node after specified node."""
|
89
|
+
raise NotImplementedError("Subclasses must implement insert_after")
|
90
|
+
|
91
|
+
def insert_before(self, node: Any, new_node: Any) -> None:
|
92
|
+
"""Insert new node before specified node."""
|
93
|
+
raise NotImplementedError("Subclasses must implement insert_before")
|
94
|
+
|
95
|
+
|
96
|
+
class ATreeEdgeStrategy(AEdgeStrategy):
|
97
|
+
"""Tree edge capabilities (hierarchical connections)."""
|
98
|
+
|
99
|
+
def get_parent(self, node: Any) -> Optional[Any]:
|
100
|
+
"""Get parent node."""
|
101
|
+
raise NotImplementedError("Subclasses must implement get_parent")
|
102
|
+
|
103
|
+
def get_children(self, node: Any) -> List[Any]:
|
104
|
+
"""Get child nodes."""
|
105
|
+
raise NotImplementedError("Subclasses must implement get_children")
|
106
|
+
|
107
|
+
def get_siblings(self, node: Any) -> List[Any]:
|
108
|
+
"""Get sibling nodes."""
|
109
|
+
raise NotImplementedError("Subclasses must implement get_siblings")
|
110
|
+
|
111
|
+
def get_root(self) -> Optional[Any]:
|
112
|
+
"""Get root node."""
|
113
|
+
raise NotImplementedError("Subclasses must implement get_root")
|
114
|
+
|
115
|
+
def get_leaves(self) -> List[Any]:
|
116
|
+
"""Get leaf nodes."""
|
117
|
+
raise NotImplementedError("Subclasses must implement get_leaves")
|
118
|
+
|
119
|
+
def get_depth(self, node: Any) -> int:
|
120
|
+
"""Get depth of node."""
|
121
|
+
raise NotImplementedError("Subclasses must implement get_depth")
|
122
|
+
|
123
|
+
def get_height(self) -> int:
|
124
|
+
"""Get height of tree."""
|
125
|
+
raise NotImplementedError("Subclasses must implement get_height")
|
126
|
+
|
127
|
+
def is_ancestor(self, ancestor: Any, descendant: Any) -> bool:
|
128
|
+
"""Check if one node is ancestor of another."""
|
129
|
+
raise NotImplementedError("Subclasses must implement is_ancestor")
|
130
|
+
|
131
|
+
|
132
|
+
class AGraphEdgeStrategy(AEdgeStrategy):
|
133
|
+
"""Graph edge capabilities (network connections)."""
|
134
|
+
|
135
|
+
def get_neighbors(self, node: Any) -> List[Any]:
|
136
|
+
"""Get all neighboring nodes."""
|
137
|
+
raise NotImplementedError("Subclasses must implement get_neighbors")
|
138
|
+
|
139
|
+
def get_edge_weight(self, from_node: Any, to_node: Any) -> float:
|
140
|
+
"""Get edge weight."""
|
141
|
+
raise NotImplementedError("Subclasses must implement get_edge_weight")
|
142
|
+
|
143
|
+
def set_edge_weight(self, from_node: Any, to_node: Any, weight: float) -> None:
|
144
|
+
"""Set edge weight."""
|
145
|
+
raise NotImplementedError("Subclasses must implement set_edge_weight")
|
146
|
+
|
147
|
+
def find_shortest_path(self, start: Any, end: Any) -> List[Any]:
|
148
|
+
"""Find shortest path between nodes."""
|
149
|
+
raise NotImplementedError("Subclasses must implement find_shortest_path")
|
150
|
+
|
151
|
+
def find_all_paths(self, start: Any, end: Any) -> List[List[Any]]:
|
152
|
+
"""Find all paths between nodes."""
|
153
|
+
raise NotImplementedError("Subclasses must implement find_all_paths")
|
154
|
+
|
155
|
+
def get_connected_components(self) -> List[List[Any]]:
|
156
|
+
"""Get all connected components."""
|
157
|
+
raise NotImplementedError("Subclasses must implement get_connected_components")
|
158
|
+
|
159
|
+
def is_connected(self, start: Any, end: Any) -> bool:
|
160
|
+
"""Check if two nodes are connected."""
|
161
|
+
raise NotImplementedError("Subclasses must implement is_connected")
|
162
|
+
|
163
|
+
def get_degree(self, node: Any) -> int:
|
164
|
+
"""Get degree of node."""
|
165
|
+
raise NotImplementedError("Subclasses must implement get_degree")
|
166
|
+
|
167
|
+
def is_cyclic(self) -> bool:
|
168
|
+
"""Check if graph contains cycles."""
|
169
|
+
raise NotImplementedError("Subclasses must implement is_cyclic")
|
@@ -0,0 +1,328 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/strategies/flyweight.py
|
3
|
+
"""
|
4
|
+
Strategy Flyweight Pattern Implementation
|
5
|
+
|
6
|
+
Optimizes memory usage by sharing strategy instances with identical configurations.
|
7
|
+
This prevents creating multiple instances of the same strategy type with the same
|
8
|
+
configuration, which is especially important for high-throughput applications.
|
9
|
+
|
10
|
+
Company: eXonware.com
|
11
|
+
Author: Eng. Muhammad AlShehri
|
12
|
+
Email: connect@exonware.com
|
13
|
+
Version: 0.0.1.12
|
14
|
+
Generation Date: 07-Sep-2025
|
15
|
+
"""
|
16
|
+
|
17
|
+
import threading
|
18
|
+
import hashlib
|
19
|
+
import json
|
20
|
+
from typing import Any, Dict, Hashable, Optional, Type, TypeVar, Union
|
21
|
+
from weakref import WeakValueDictionary
|
22
|
+
from exonware.xwsystem import get_logger
|
23
|
+
|
24
|
+
logger = get_logger(__name__)
|
25
|
+
|
26
|
+
from ..types import NodeMode, EdgeMode, NodeTrait, EdgeTrait
|
27
|
+
from .nodes.base import ANodeStrategy
|
28
|
+
from .edges.base import AEdgeStrategy
|
29
|
+
|
30
|
+
|
31
|
+
T = TypeVar('T', bound=Union[ANodeStrategy, AEdgeStrategy])
|
32
|
+
|
33
|
+
|
34
|
+
class StrategyFlyweight:
|
35
|
+
"""
|
36
|
+
Flyweight factory for strategy instances.
|
37
|
+
|
38
|
+
Manages shared strategy instances to reduce memory footprint and
|
39
|
+
improve performance by avoiding redundant object creation.
|
40
|
+
"""
|
41
|
+
|
42
|
+
def __init__(self):
|
43
|
+
"""Initialize the flyweight factory."""
|
44
|
+
self._node_instances: WeakValueDictionary[str, ANodeStrategy] = WeakValueDictionary()
|
45
|
+
self._edge_instances: WeakValueDictionary[str, AEdgeStrategy] = WeakValueDictionary()
|
46
|
+
self._lock = threading.RLock()
|
47
|
+
self._stats = {
|
48
|
+
'node_created': 0,
|
49
|
+
'node_reused': 0,
|
50
|
+
'edge_created': 0,
|
51
|
+
'edge_reused': 0,
|
52
|
+
'cache_hits': 0,
|
53
|
+
'cache_misses': 0,
|
54
|
+
'memory_saved_instances': 0
|
55
|
+
}
|
56
|
+
|
57
|
+
def get_node_strategy(
|
58
|
+
self,
|
59
|
+
strategy_class: Type[T],
|
60
|
+
mode: NodeMode,
|
61
|
+
traits: NodeTrait = NodeTrait.NONE,
|
62
|
+
**config: Any
|
63
|
+
) -> T:
|
64
|
+
"""
|
65
|
+
Get a node strategy instance, creating or reusing based on configuration.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
strategy_class: The strategy class to instantiate
|
69
|
+
mode: Node mode for the strategy
|
70
|
+
traits: Node traits for the strategy
|
71
|
+
**config: Configuration parameters for the strategy
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
Shared strategy instance
|
75
|
+
"""
|
76
|
+
# Create a hashable key from the class and configuration
|
77
|
+
cache_key = self._create_node_cache_key(strategy_class, mode, traits, config)
|
78
|
+
|
79
|
+
with self._lock:
|
80
|
+
# Check if we already have this instance
|
81
|
+
if cache_key in self._node_instances:
|
82
|
+
self._stats['cache_hits'] += 1
|
83
|
+
self._stats['node_reused'] += 1
|
84
|
+
self._stats['memory_saved_instances'] += 1
|
85
|
+
logger.debug(f"♻️ Reusing node strategy: {strategy_class.__name__}")
|
86
|
+
return self._node_instances[cache_key]
|
87
|
+
|
88
|
+
# Create new instance
|
89
|
+
self._stats['cache_misses'] += 1
|
90
|
+
self._stats['node_created'] += 1
|
91
|
+
|
92
|
+
try:
|
93
|
+
instance = strategy_class(traits=traits, mode=mode, **config)
|
94
|
+
self._node_instances[cache_key] = instance
|
95
|
+
logger.debug(f"🆕 Created new node strategy: {strategy_class.__name__}")
|
96
|
+
return instance
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
logger.error(f"❌ Failed to create {strategy_class.__name__} instance: {e}")
|
100
|
+
raise
|
101
|
+
|
102
|
+
def get_edge_strategy(
|
103
|
+
self,
|
104
|
+
strategy_class: Type[T],
|
105
|
+
mode: EdgeMode,
|
106
|
+
traits: EdgeTrait = EdgeTrait.NONE,
|
107
|
+
**config: Any
|
108
|
+
) -> T:
|
109
|
+
"""
|
110
|
+
Get an edge strategy instance, creating or reusing based on configuration.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
strategy_class: The strategy class to instantiate
|
114
|
+
mode: Edge mode for the strategy
|
115
|
+
traits: Edge traits for the strategy
|
116
|
+
**config: Configuration parameters for the strategy
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
Shared strategy instance
|
120
|
+
"""
|
121
|
+
# Create a hashable key from the class and configuration
|
122
|
+
cache_key = self._create_edge_cache_key(strategy_class, mode, traits, config)
|
123
|
+
|
124
|
+
with self._lock:
|
125
|
+
# Check if we already have this instance
|
126
|
+
if cache_key in self._edge_instances:
|
127
|
+
self._stats['cache_hits'] += 1
|
128
|
+
self._stats['edge_reused'] += 1
|
129
|
+
self._stats['memory_saved_instances'] += 1
|
130
|
+
logger.debug(f"♻️ Reusing edge strategy: {strategy_class.__name__}")
|
131
|
+
return self._edge_instances[cache_key]
|
132
|
+
|
133
|
+
# Create new instance
|
134
|
+
self._stats['cache_misses'] += 1
|
135
|
+
self._stats['edge_created'] += 1
|
136
|
+
|
137
|
+
try:
|
138
|
+
instance = strategy_class(traits=traits, mode=mode, **config)
|
139
|
+
self._edge_instances[cache_key] = instance
|
140
|
+
logger.debug(f"🆕 Created new edge strategy: {strategy_class.__name__}")
|
141
|
+
return instance
|
142
|
+
|
143
|
+
except Exception as e:
|
144
|
+
logger.error(f"❌ Failed to create {strategy_class.__name__} instance: {e}")
|
145
|
+
raise
|
146
|
+
|
147
|
+
def _create_node_cache_key(
|
148
|
+
self,
|
149
|
+
strategy_class: Type[T],
|
150
|
+
mode: NodeMode,
|
151
|
+
traits: NodeTrait,
|
152
|
+
config: Dict[str, Any]
|
153
|
+
) -> str:
|
154
|
+
"""
|
155
|
+
Create a hashable cache key from class, mode, traits, and configuration.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
strategy_class: The strategy class
|
159
|
+
mode: Node mode
|
160
|
+
traits: Node traits
|
161
|
+
config: Configuration dictionary
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
String cache key
|
165
|
+
"""
|
166
|
+
# Start with class name and module
|
167
|
+
key_parts = [f"{strategy_class.__module__}.{strategy_class.__name__}"]
|
168
|
+
|
169
|
+
# Add mode and traits
|
170
|
+
key_parts.append(f"mode:{mode.name}")
|
171
|
+
key_parts.append(f"traits:{traits.name}")
|
172
|
+
|
173
|
+
# Add configuration (sorted for consistency)
|
174
|
+
if config:
|
175
|
+
config_str = json.dumps(config, sort_keys=True, default=str)
|
176
|
+
key_parts.append(f"config:{config_str}")
|
177
|
+
|
178
|
+
# Create hash for shorter key
|
179
|
+
key_string = "|".join(key_parts)
|
180
|
+
return hashlib.md5(key_string.encode()).hexdigest()
|
181
|
+
|
182
|
+
def _create_edge_cache_key(
|
183
|
+
self,
|
184
|
+
strategy_class: Type[T],
|
185
|
+
mode: EdgeMode,
|
186
|
+
traits: EdgeTrait,
|
187
|
+
config: Dict[str, Any]
|
188
|
+
) -> str:
|
189
|
+
"""
|
190
|
+
Create a hashable cache key from class, mode, traits, and configuration.
|
191
|
+
|
192
|
+
Args:
|
193
|
+
strategy_class: The strategy class
|
194
|
+
mode: Edge mode
|
195
|
+
traits: Edge traits
|
196
|
+
config: Configuration dictionary
|
197
|
+
|
198
|
+
Returns:
|
199
|
+
String cache key
|
200
|
+
"""
|
201
|
+
# Start with class name and module
|
202
|
+
key_parts = [f"{strategy_class.__module__}.{strategy_class.__name__}"]
|
203
|
+
|
204
|
+
# Add mode and traits
|
205
|
+
key_parts.append(f"mode:{mode.name}")
|
206
|
+
key_parts.append(f"traits:{traits.name}")
|
207
|
+
|
208
|
+
# Add configuration (sorted for consistency)
|
209
|
+
if config:
|
210
|
+
config_str = json.dumps(config, sort_keys=True, default=str)
|
211
|
+
key_parts.append(f"config:{config_str}")
|
212
|
+
|
213
|
+
# Create hash for shorter key
|
214
|
+
key_string = "|".join(key_parts)
|
215
|
+
return hashlib.md5(key_string.encode()).hexdigest()
|
216
|
+
|
217
|
+
def get_stats(self) -> Dict[str, Any]:
|
218
|
+
"""
|
219
|
+
Get flyweight statistics.
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
Dictionary with cache statistics
|
223
|
+
"""
|
224
|
+
with self._lock:
|
225
|
+
total_created = self._stats['node_created'] + self._stats['edge_created']
|
226
|
+
total_reused = self._stats['node_reused'] + self._stats['edge_reused']
|
227
|
+
total_requests = total_created + total_reused
|
228
|
+
|
229
|
+
cache_hit_rate = (self._stats['cache_hits'] / total_requests * 100) if total_requests > 0 else 0
|
230
|
+
|
231
|
+
return {
|
232
|
+
'node_strategies': {
|
233
|
+
'created': self._stats['node_created'],
|
234
|
+
'reused': self._stats['node_reused'],
|
235
|
+
'active': len(self._node_instances)
|
236
|
+
},
|
237
|
+
'edge_strategies': {
|
238
|
+
'created': self._stats['edge_created'],
|
239
|
+
'reused': self._stats['edge_reused'],
|
240
|
+
'active': len(self._edge_instances)
|
241
|
+
},
|
242
|
+
'cache_performance': {
|
243
|
+
'hits': self._stats['cache_hits'],
|
244
|
+
'misses': self._stats['cache_misses'],
|
245
|
+
'hit_rate_percent': round(cache_hit_rate, 2),
|
246
|
+
'memory_saved_instances': self._stats['memory_saved_instances']
|
247
|
+
},
|
248
|
+
'total_instances': {
|
249
|
+
'created': total_created,
|
250
|
+
'reused': total_reused,
|
251
|
+
'active': len(self._node_instances) + len(self._edge_instances)
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
def clear_cache(self) -> None:
|
256
|
+
"""Clear all cached strategy instances."""
|
257
|
+
with self._lock:
|
258
|
+
node_count = len(self._node_instances)
|
259
|
+
edge_count = len(self._edge_instances)
|
260
|
+
|
261
|
+
self._node_instances.clear()
|
262
|
+
self._edge_instances.clear()
|
263
|
+
|
264
|
+
logger.info(f"🧹 Cleared flyweight cache: {node_count} node + {edge_count} edge instances")
|
265
|
+
|
266
|
+
def get_cache_info(self) -> Dict[str, Any]:
|
267
|
+
"""
|
268
|
+
Get detailed cache information.
|
269
|
+
|
270
|
+
Returns:
|
271
|
+
Dictionary with detailed cache information
|
272
|
+
"""
|
273
|
+
with self._lock:
|
274
|
+
return {
|
275
|
+
'node_cache_size': len(self._node_instances),
|
276
|
+
'edge_cache_size': len(self._edge_instances),
|
277
|
+
'total_cache_size': len(self._node_instances) + len(self._edge_instances),
|
278
|
+
'node_cache_keys': list(self._node_instances.keys()),
|
279
|
+
'edge_cache_keys': list(self._edge_instances.keys())
|
280
|
+
}
|
281
|
+
|
282
|
+
|
283
|
+
# Global flyweight instance
|
284
|
+
_flyweight_instance: Optional[StrategyFlyweight] = None
|
285
|
+
_flyweight_lock = threading.Lock()
|
286
|
+
|
287
|
+
|
288
|
+
def get_flyweight() -> StrategyFlyweight:
|
289
|
+
"""
|
290
|
+
Get the global strategy flyweight instance.
|
291
|
+
|
292
|
+
Returns:
|
293
|
+
Global StrategyFlyweight instance
|
294
|
+
"""
|
295
|
+
global _flyweight_instance
|
296
|
+
|
297
|
+
if _flyweight_instance is None:
|
298
|
+
with _flyweight_lock:
|
299
|
+
if _flyweight_instance is None:
|
300
|
+
_flyweight_instance = StrategyFlyweight()
|
301
|
+
logger.info("🏭 Initialized global strategy flyweight")
|
302
|
+
|
303
|
+
return _flyweight_instance
|
304
|
+
|
305
|
+
|
306
|
+
def get_flyweight_stats() -> Dict[str, Any]:
|
307
|
+
"""
|
308
|
+
Get flyweight statistics.
|
309
|
+
|
310
|
+
Returns:
|
311
|
+
Flyweight statistics dictionary
|
312
|
+
"""
|
313
|
+
return get_flyweight().get_stats()
|
314
|
+
|
315
|
+
|
316
|
+
def clear_flyweight_cache() -> None:
|
317
|
+
"""Clear the global flyweight cache."""
|
318
|
+
get_flyweight().clear_cache()
|
319
|
+
|
320
|
+
|
321
|
+
def get_flyweight_cache_info() -> Dict[str, Any]:
|
322
|
+
"""
|
323
|
+
Get flyweight cache information.
|
324
|
+
|
325
|
+
Returns:
|
326
|
+
Cache information dictionary
|
327
|
+
"""
|
328
|
+
return get_flyweight().get_cache_info()
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
Strategy Implementations
|
3
|
+
|
4
|
+
This package contains all strategy implementations for the strategy system.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from ._base_node import aNodeStrategy
|
8
|
+
from ._base_edge import aEdgeStrategy
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
'aNodeStrategy',
|
12
|
+
'aEdgeStrategy',
|
13
|
+
]
|