exonware-xwsystem 0.0.1.409__py3-none-any.whl → 0.0.1.411__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 +2 -2
- exonware/conf.py +10 -20
- exonware/xwsystem/__init__.py +6 -16
- exonware/xwsystem/caching/__init__.py +1 -1
- exonware/xwsystem/caching/base.py +16 -16
- exonware/xwsystem/caching/bloom_cache.py +5 -5
- exonware/xwsystem/caching/cache_manager.py +2 -2
- exonware/xwsystem/caching/conditional.py +4 -4
- exonware/xwsystem/caching/contracts.py +12 -12
- exonware/xwsystem/caching/decorators.py +2 -2
- exonware/xwsystem/caching/defs.py +1 -1
- exonware/xwsystem/caching/disk_cache.py +4 -4
- exonware/xwsystem/caching/distributed.py +1 -1
- exonware/xwsystem/caching/errors.py +1 -1
- exonware/xwsystem/caching/events.py +8 -8
- exonware/xwsystem/caching/eviction_strategies.py +9 -9
- exonware/xwsystem/caching/fluent.py +1 -1
- exonware/xwsystem/caching/integrity.py +1 -1
- exonware/xwsystem/caching/lfu_cache.py +24 -9
- exonware/xwsystem/caching/lfu_optimized.py +21 -21
- exonware/xwsystem/caching/lru_cache.py +14 -7
- exonware/xwsystem/caching/memory_bounded.py +8 -8
- exonware/xwsystem/caching/metrics_exporter.py +6 -6
- exonware/xwsystem/caching/observable_cache.py +1 -1
- exonware/xwsystem/caching/pluggable_cache.py +9 -9
- exonware/xwsystem/caching/rate_limiter.py +1 -1
- exonware/xwsystem/caching/read_through.py +6 -6
- exonware/xwsystem/caching/secure_cache.py +1 -1
- exonware/xwsystem/caching/serializable.py +3 -3
- exonware/xwsystem/caching/stats.py +7 -7
- exonware/xwsystem/caching/tagging.py +11 -11
- exonware/xwsystem/caching/ttl_cache.py +21 -6
- exonware/xwsystem/caching/two_tier_cache.py +5 -5
- exonware/xwsystem/caching/utils.py +3 -3
- exonware/xwsystem/caching/validation.py +1 -1
- exonware/xwsystem/caching/warming.py +9 -9
- exonware/xwsystem/caching/write_behind.py +5 -5
- exonware/xwsystem/cli/__init__.py +1 -1
- exonware/xwsystem/cli/args.py +10 -10
- exonware/xwsystem/cli/base.py +15 -15
- exonware/xwsystem/cli/colors.py +1 -1
- exonware/xwsystem/cli/console.py +1 -1
- exonware/xwsystem/cli/contracts.py +5 -5
- exonware/xwsystem/cli/defs.py +1 -1
- exonware/xwsystem/cli/errors.py +1 -1
- exonware/xwsystem/cli/progress.py +1 -1
- exonware/xwsystem/cli/prompts.py +1 -1
- exonware/xwsystem/cli/tables.py +7 -7
- exonware/xwsystem/config/__init__.py +1 -1
- exonware/xwsystem/config/base.py +14 -14
- exonware/xwsystem/config/contracts.py +22 -22
- exonware/xwsystem/config/defaults.py +2 -2
- exonware/xwsystem/config/defs.py +1 -1
- exonware/xwsystem/config/errors.py +2 -2
- exonware/xwsystem/config/logging.py +1 -1
- exonware/xwsystem/config/logging_setup.py +2 -2
- exonware/xwsystem/config/performance.py +7 -7
- exonware/xwsystem/config/performance_modes.py +20 -20
- exonware/xwsystem/config/version_manager.py +4 -4
- exonware/xwsystem/{http → http_client}/__init__.py +1 -1
- exonware/xwsystem/{http → http_client}/advanced_client.py +20 -20
- exonware/xwsystem/{http → http_client}/base.py +13 -13
- exonware/xwsystem/{http → http_client}/client.py +43 -43
- exonware/xwsystem/{http → http_client}/contracts.py +5 -5
- exonware/xwsystem/{http → http_client}/defs.py +2 -2
- exonware/xwsystem/{http → http_client}/errors.py +2 -2
- exonware/xwsystem/io/__init__.py +1 -1
- exonware/xwsystem/io/archive/__init__.py +1 -1
- exonware/xwsystem/io/archive/archive.py +5 -5
- exonware/xwsystem/io/archive/archive_files.py +8 -8
- exonware/xwsystem/io/archive/archivers.py +3 -3
- exonware/xwsystem/io/archive/base.py +17 -17
- exonware/xwsystem/io/archive/codec_integration.py +1 -1
- exonware/xwsystem/io/archive/compression.py +1 -1
- exonware/xwsystem/io/archive/formats/__init__.py +1 -1
- exonware/xwsystem/io/archive/formats/brotli_format.py +12 -9
- exonware/xwsystem/io/archive/formats/lz4_format.py +12 -9
- exonware/xwsystem/io/archive/formats/rar.py +12 -9
- exonware/xwsystem/io/archive/formats/sevenzip.py +12 -9
- exonware/xwsystem/io/archive/formats/squashfs_format.py +7 -7
- exonware/xwsystem/io/archive/formats/tar.py +8 -8
- exonware/xwsystem/io/archive/formats/wim_format.py +12 -9
- exonware/xwsystem/io/archive/formats/zip.py +8 -8
- exonware/xwsystem/io/archive/formats/zpaq_format.py +7 -7
- exonware/xwsystem/io/archive/formats/zstandard.py +12 -9
- exonware/xwsystem/io/base.py +17 -17
- exonware/xwsystem/io/codec/__init__.py +1 -1
- exonware/xwsystem/io/codec/base.py +261 -14
- exonware/xwsystem/io/codec/contracts.py +3 -6
- exonware/xwsystem/io/codec/registry.py +29 -29
- exonware/xwsystem/io/common/__init__.py +1 -1
- exonware/xwsystem/io/common/atomic.py +2 -2
- exonware/xwsystem/io/common/base.py +1 -1
- exonware/xwsystem/io/common/lock.py +1 -1
- exonware/xwsystem/io/common/watcher.py +4 -4
- exonware/xwsystem/io/contracts.py +34 -39
- exonware/xwsystem/io/data_operations.py +480 -0
- exonware/xwsystem/io/defs.py +2 -2
- exonware/xwsystem/io/errors.py +32 -3
- exonware/xwsystem/io/facade.py +4 -4
- exonware/xwsystem/io/file/__init__.py +1 -1
- exonware/xwsystem/io/file/base.py +2 -2
- exonware/xwsystem/io/file/conversion.py +1 -1
- exonware/xwsystem/io/file/file.py +10 -8
- exonware/xwsystem/io/file/paged_source.py +8 -1
- exonware/xwsystem/io/file/paging/__init__.py +1 -1
- exonware/xwsystem/io/file/paging/byte_paging.py +1 -1
- exonware/xwsystem/io/file/paging/line_paging.py +1 -1
- exonware/xwsystem/io/file/paging/record_paging.py +1 -1
- exonware/xwsystem/io/file/paging/registry.py +5 -5
- exonware/xwsystem/io/file/source.py +22 -11
- exonware/xwsystem/io/filesystem/__init__.py +1 -1
- exonware/xwsystem/io/filesystem/base.py +1 -1
- exonware/xwsystem/io/filesystem/local.py +9 -1
- exonware/xwsystem/io/folder/__init__.py +1 -1
- exonware/xwsystem/io/folder/base.py +2 -2
- exonware/xwsystem/io/folder/folder.py +6 -6
- exonware/xwsystem/io/serialization/__init__.py +1 -1
- exonware/xwsystem/io/serialization/auto_serializer.py +53 -40
- exonware/xwsystem/io/serialization/base.py +248 -35
- exonware/xwsystem/io/serialization/contracts.py +93 -4
- exonware/xwsystem/io/serialization/defs.py +1 -1
- exonware/xwsystem/io/serialization/errors.py +1 -1
- exonware/xwsystem/io/serialization/flyweight.py +22 -22
- exonware/xwsystem/io/serialization/format_detector.py +18 -15
- exonware/xwsystem/io/serialization/formats/__init__.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/bson.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/cbor.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/marshal.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/msgpack.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/pickle.py +1 -1
- exonware/xwsystem/io/serialization/formats/binary/plistlib.py +1 -1
- exonware/xwsystem/io/serialization/formats/database/dbm.py +53 -1
- exonware/xwsystem/io/serialization/formats/database/shelve.py +48 -1
- exonware/xwsystem/io/serialization/formats/database/sqlite3.py +85 -1
- exonware/xwsystem/io/serialization/formats/text/configparser.py +2 -2
- exonware/xwsystem/io/serialization/formats/text/csv.py +2 -2
- exonware/xwsystem/io/serialization/formats/text/formdata.py +2 -2
- exonware/xwsystem/io/serialization/formats/text/json.py +23 -5
- exonware/xwsystem/io/serialization/formats/text/json5.py +98 -13
- exonware/xwsystem/io/serialization/formats/text/jsonlines.py +230 -20
- exonware/xwsystem/io/serialization/formats/text/multipart.py +2 -2
- exonware/xwsystem/io/serialization/formats/text/toml.py +65 -4
- exonware/xwsystem/io/serialization/formats/text/xml.py +451 -69
- exonware/xwsystem/io/serialization/formats/text/yaml.py +52 -2
- exonware/xwsystem/io/serialization/registry.py +5 -5
- exonware/xwsystem/io/serialization/serializer.py +184 -12
- exonware/xwsystem/io/serialization/utils/__init__.py +1 -1
- exonware/xwsystem/io/serialization/utils/path_ops.py +3 -3
- exonware/xwsystem/io/stream/__init__.py +1 -1
- exonware/xwsystem/io/stream/async_operations.py +3 -3
- exonware/xwsystem/io/stream/base.py +3 -7
- exonware/xwsystem/io/stream/codec_io.py +4 -7
- exonware/xwsystem/ipc/async_fabric.py +7 -8
- exonware/xwsystem/ipc/base.py +9 -9
- exonware/xwsystem/ipc/contracts.py +5 -5
- exonware/xwsystem/ipc/defs.py +1 -1
- exonware/xwsystem/ipc/errors.py +2 -2
- exonware/xwsystem/ipc/message_queue.py +4 -6
- exonware/xwsystem/ipc/pipes.py +2 -2
- exonware/xwsystem/ipc/process_manager.py +7 -7
- exonware/xwsystem/ipc/process_pool.py +8 -8
- exonware/xwsystem/ipc/shared_memory.py +7 -7
- exonware/xwsystem/monitoring/base.py +33 -33
- exonware/xwsystem/monitoring/contracts.py +27 -27
- exonware/xwsystem/monitoring/defs.py +1 -1
- exonware/xwsystem/monitoring/error_recovery.py +16 -16
- exonware/xwsystem/monitoring/errors.py +2 -2
- exonware/xwsystem/monitoring/memory_monitor.py +12 -12
- exonware/xwsystem/monitoring/metrics.py +8 -8
- exonware/xwsystem/monitoring/performance_manager_generic.py +20 -20
- exonware/xwsystem/monitoring/performance_monitor.py +11 -11
- exonware/xwsystem/monitoring/performance_validator.py +21 -21
- exonware/xwsystem/monitoring/system_monitor.py +17 -17
- exonware/xwsystem/monitoring/tracing.py +20 -20
- exonware/xwsystem/monitoring/tracker.py +7 -7
- exonware/xwsystem/operations/__init__.py +5 -5
- exonware/xwsystem/operations/base.py +3 -3
- exonware/xwsystem/operations/contracts.py +3 -3
- exonware/xwsystem/operations/defs.py +5 -5
- exonware/xwsystem/operations/diff.py +5 -5
- exonware/xwsystem/operations/merge.py +2 -2
- exonware/xwsystem/operations/patch.py +5 -5
- exonware/xwsystem/patterns/base.py +4 -4
- exonware/xwsystem/patterns/context_manager.py +7 -7
- exonware/xwsystem/patterns/contracts.py +29 -31
- exonware/xwsystem/patterns/defs.py +1 -1
- exonware/xwsystem/patterns/dynamic_facade.py +9 -9
- exonware/xwsystem/patterns/errors.py +10 -10
- exonware/xwsystem/patterns/handler_factory.py +15 -14
- exonware/xwsystem/patterns/import_registry.py +22 -22
- exonware/xwsystem/patterns/object_pool.py +14 -13
- exonware/xwsystem/patterns/registry.py +45 -32
- exonware/xwsystem/plugins/__init__.py +1 -1
- exonware/xwsystem/plugins/base.py +25 -25
- exonware/xwsystem/plugins/contracts.py +28 -28
- exonware/xwsystem/plugins/defs.py +1 -1
- exonware/xwsystem/plugins/errors.py +9 -9
- exonware/xwsystem/runtime/__init__.py +1 -1
- exonware/xwsystem/runtime/base.py +42 -42
- exonware/xwsystem/runtime/contracts.py +9 -9
- exonware/xwsystem/runtime/defs.py +1 -1
- exonware/xwsystem/runtime/env.py +9 -9
- exonware/xwsystem/runtime/errors.py +1 -1
- exonware/xwsystem/runtime/reflection.py +15 -15
- exonware/xwsystem/security/auth.py +47 -15
- exonware/xwsystem/security/base.py +17 -17
- exonware/xwsystem/security/contracts.py +30 -30
- exonware/xwsystem/security/crypto.py +8 -8
- exonware/xwsystem/security/defs.py +1 -1
- exonware/xwsystem/security/errors.py +2 -2
- exonware/xwsystem/security/hazmat.py +7 -7
- exonware/xwsystem/security/path_validator.py +1 -1
- exonware/xwsystem/shared/__init__.py +1 -1
- exonware/xwsystem/shared/base.py +14 -14
- exonware/xwsystem/shared/contracts.py +6 -6
- exonware/xwsystem/shared/defs.py +1 -1
- exonware/xwsystem/shared/errors.py +1 -1
- exonware/xwsystem/structures/__init__.py +1 -1
- exonware/xwsystem/structures/base.py +29 -29
- exonware/xwsystem/structures/circular_detector.py +15 -15
- exonware/xwsystem/structures/contracts.py +9 -9
- exonware/xwsystem/structures/defs.py +1 -1
- exonware/xwsystem/structures/errors.py +2 -2
- exonware/xwsystem/structures/tree_walker.py +8 -8
- exonware/xwsystem/threading/async_primitives.py +7 -7
- exonware/xwsystem/threading/base.py +19 -19
- exonware/xwsystem/threading/contracts.py +13 -13
- exonware/xwsystem/threading/defs.py +1 -1
- exonware/xwsystem/threading/errors.py +2 -2
- exonware/xwsystem/threading/safe_factory.py +13 -12
- exonware/xwsystem/utils/base.py +34 -34
- exonware/xwsystem/utils/contracts.py +9 -9
- exonware/xwsystem/utils/dt/__init__.py +1 -1
- exonware/xwsystem/utils/dt/base.py +6 -6
- exonware/xwsystem/utils/dt/contracts.py +2 -2
- exonware/xwsystem/utils/dt/defs.py +1 -1
- exonware/xwsystem/utils/dt/errors.py +2 -2
- exonware/xwsystem/utils/dt/formatting.py +3 -3
- exonware/xwsystem/utils/dt/humanize.py +2 -2
- exonware/xwsystem/utils/dt/parsing.py +2 -2
- exonware/xwsystem/utils/dt/timezone_utils.py +5 -5
- exonware/xwsystem/utils/errors.py +2 -2
- exonware/xwsystem/utils/test_runner.py +6 -6
- exonware/xwsystem/utils/utils_contracts.py +1 -1
- exonware/xwsystem/validation/__init__.py +1 -1
- exonware/xwsystem/validation/base.py +48 -48
- exonware/xwsystem/validation/contracts.py +8 -8
- exonware/xwsystem/validation/data_validator.py +10 -0
- exonware/xwsystem/validation/declarative.py +15 -15
- exonware/xwsystem/validation/defs.py +1 -1
- exonware/xwsystem/validation/errors.py +2 -2
- exonware/xwsystem/validation/fluent_validator.py +10 -10
- exonware/xwsystem/version.py +2 -2
- {exonware_xwsystem-0.0.1.409.dist-info → exonware_xwsystem-0.0.1.411.dist-info}/METADATA +9 -11
- exonware_xwsystem-0.0.1.411.dist-info/RECORD +274 -0
- {exonware_xwsystem-0.0.1.409.dist-info → exonware_xwsystem-0.0.1.411.dist-info}/WHEEL +1 -1
- exonware/xwsystem/lazy_bootstrap.py +0 -79
- exonware_xwsystem-0.0.1.409.dist-info/RECORD +0 -274
- {exonware_xwsystem-0.0.1.409.dist-info → exonware_xwsystem-0.0.1.411.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#exonware/xwsystem/patterns/object_pool.py
|
|
1
2
|
"""
|
|
2
3
|
Generic object pool implementation for XSystem framework.
|
|
3
4
|
|
|
@@ -6,12 +7,12 @@ components to reduce memory allocation overhead and improve performance.
|
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
import threading
|
|
9
|
-
from typing import Any, Callable,
|
|
10
|
+
from typing import Any, Callable, Optional
|
|
11
|
+
# Root cause: Migrating to Python 3.12 built-in generic syntax for consistency
|
|
12
|
+
# Priority #3: Maintainability - Modern type annotations improve code clarity
|
|
10
13
|
|
|
11
14
|
from ..config.logging_setup import get_logger
|
|
12
15
|
|
|
13
|
-
T = TypeVar("T")
|
|
14
|
-
|
|
15
16
|
|
|
16
17
|
class ObjectPool:
|
|
17
18
|
"""
|
|
@@ -35,13 +36,13 @@ class ObjectPool:
|
|
|
35
36
|
enable_thread_safety: Whether to use thread-safe operations
|
|
36
37
|
component_name: Name for logging purposes
|
|
37
38
|
"""
|
|
38
|
-
self._pools:
|
|
39
|
+
self._pools: dict[type, list[Any]] = {}
|
|
39
40
|
self._max_size = max_size
|
|
40
41
|
self._lock = threading.RLock() if enable_thread_safety else None
|
|
41
42
|
self._logger = get_logger(f"{component_name}.object_pool")
|
|
42
43
|
self._stats = {"created": 0, "reused": 0, "released": 0, "discarded": 0}
|
|
43
44
|
|
|
44
|
-
def get(self, obj_type:
|
|
45
|
+
def get[T](self, obj_type: type[T], *args, **kwargs) -> T:
|
|
45
46
|
"""
|
|
46
47
|
Get an object from the pool or create a new one if the pool is empty.
|
|
47
48
|
|
|
@@ -61,7 +62,7 @@ class ObjectPool:
|
|
|
61
62
|
else:
|
|
62
63
|
return self._get_from_pool(obj_type, pool, *args, **kwargs)
|
|
63
64
|
|
|
64
|
-
def _get_from_pool(self, obj_type:
|
|
65
|
+
def _get_from_pool[T](self, obj_type: type[T], pool: list[Any], *args, **kwargs) -> T:
|
|
65
66
|
"""Internal method to get object from pool."""
|
|
66
67
|
if pool:
|
|
67
68
|
obj = pool.pop()
|
|
@@ -99,7 +100,7 @@ class ObjectPool:
|
|
|
99
100
|
else:
|
|
100
101
|
self._release_to_pool(obj, obj_type)
|
|
101
102
|
|
|
102
|
-
def _release_to_pool(self, obj: Any, obj_type:
|
|
103
|
+
def _release_to_pool(self, obj: Any, obj_type: type) -> None:
|
|
103
104
|
"""Internal method to release object to pool."""
|
|
104
105
|
if obj_type not in self._pools:
|
|
105
106
|
self._pools[obj_type] = []
|
|
@@ -114,7 +115,7 @@ class ObjectPool:
|
|
|
114
115
|
self._stats["discarded"] += 1
|
|
115
116
|
self._logger.debug(f"Discarded {obj_type.__name__} (pool full)")
|
|
116
117
|
|
|
117
|
-
def clear(self, obj_type: Optional[
|
|
118
|
+
def clear(self, obj_type: Optional[type] = None) -> None:
|
|
118
119
|
"""
|
|
119
120
|
Clear objects from the pool.
|
|
120
121
|
|
|
@@ -127,7 +128,7 @@ class ObjectPool:
|
|
|
127
128
|
else:
|
|
128
129
|
self._clear_pool(obj_type)
|
|
129
130
|
|
|
130
|
-
def _clear_pool(self, obj_type: Optional[
|
|
131
|
+
def _clear_pool(self, obj_type: Optional[type] = None) -> None:
|
|
131
132
|
"""Internal method to clear pool."""
|
|
132
133
|
if obj_type is None:
|
|
133
134
|
# Clear all pools
|
|
@@ -140,7 +141,7 @@ class ObjectPool:
|
|
|
140
141
|
self._pools[obj_type].clear()
|
|
141
142
|
self._logger.info(f"Cleared pool for {obj_type.__name__}")
|
|
142
143
|
|
|
143
|
-
def get_stats(self) ->
|
|
144
|
+
def get_stats(self) -> dict[str, Any]:
|
|
144
145
|
"""
|
|
145
146
|
Get pool statistics.
|
|
146
147
|
|
|
@@ -153,7 +154,7 @@ class ObjectPool:
|
|
|
153
154
|
else:
|
|
154
155
|
return self._get_pool_stats()
|
|
155
156
|
|
|
156
|
-
def _get_pool_stats(self) ->
|
|
157
|
+
def _get_pool_stats(self) -> dict[str, Any]:
|
|
157
158
|
"""Internal method to get pool statistics."""
|
|
158
159
|
pool_sizes = {t.__name__: len(pool) for t, pool in self._pools.items()}
|
|
159
160
|
|
|
@@ -199,7 +200,7 @@ class PooledObject:
|
|
|
199
200
|
|
|
200
201
|
|
|
201
202
|
# Global object pool registry
|
|
202
|
-
_pool_registry:
|
|
203
|
+
_pool_registry: dict[str, ObjectPool] = {}
|
|
203
204
|
_pool_registry_lock = threading.RLock()
|
|
204
205
|
|
|
205
206
|
|
|
@@ -242,7 +243,7 @@ def clear_object_pool(component_name: str) -> None:
|
|
|
242
243
|
del _pool_registry[component_name]
|
|
243
244
|
|
|
244
245
|
|
|
245
|
-
def get_all_pool_stats() ->
|
|
246
|
+
def get_all_pool_stats() -> dict[str, dict[str, Any]]:
|
|
246
247
|
"""
|
|
247
248
|
Get statistics for all object pools.
|
|
248
249
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
#exonware/xwsystem/patterns/registry.py
|
|
2
3
|
"""
|
|
3
4
|
Company: eXonware.com
|
|
4
5
|
Author: Eng. Muhammad AlShehri
|
|
5
6
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.0.1.
|
|
7
|
+
Version: 0.0.1.411
|
|
7
8
|
Generation Date: October 26, 2025
|
|
8
9
|
|
|
9
10
|
Generic registry pattern for dynamic registration and discovery.
|
|
@@ -11,25 +12,30 @@ Generic registry pattern for dynamic registration and discovery.
|
|
|
11
12
|
|
|
12
13
|
import threading
|
|
13
14
|
import time
|
|
14
|
-
from typing import Any,
|
|
15
|
+
from typing import Any, Optional, Callable
|
|
16
|
+
# Root cause: Migrating to Python 3.12 built-in generic syntax for consistency
|
|
17
|
+
# Priority #3: Maintainability - Modern type annotations improve code clarity
|
|
15
18
|
from abc import ABC, abstractmethod
|
|
16
19
|
from ..config.logging_setup import get_logger
|
|
17
20
|
|
|
18
21
|
logger = get_logger("xwsystem.patterns.registry")
|
|
19
22
|
|
|
20
|
-
T = TypeVar('T')
|
|
21
|
-
|
|
22
23
|
|
|
23
24
|
class RegistryError(Exception):
|
|
24
25
|
"""Registry-specific error."""
|
|
25
26
|
pass
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class IRegistry(ABC):
|
|
29
|
-
"""
|
|
29
|
+
class IRegistry[T](ABC):
|
|
30
|
+
"""
|
|
31
|
+
Interface for registry implementations.
|
|
32
|
+
|
|
33
|
+
Root cause: Adding generic type parameter for better type safety.
|
|
34
|
+
Priority #3: Maintainability - Generic types improve code clarity and type checking.
|
|
35
|
+
"""
|
|
30
36
|
|
|
31
37
|
@abstractmethod
|
|
32
|
-
def register(self, name: str, item:
|
|
38
|
+
def register(self, name: str, item: T, metadata: Optional[dict[str, Any]] = None) -> bool:
|
|
33
39
|
"""Register an item with optional metadata."""
|
|
34
40
|
pass
|
|
35
41
|
|
|
@@ -39,12 +45,12 @@ class IRegistry(ABC):
|
|
|
39
45
|
pass
|
|
40
46
|
|
|
41
47
|
@abstractmethod
|
|
42
|
-
def get(self, name: str) -> Optional[
|
|
48
|
+
def get(self, name: str) -> Optional[T]:
|
|
43
49
|
"""Get an item by name."""
|
|
44
50
|
pass
|
|
45
51
|
|
|
46
52
|
@abstractmethod
|
|
47
|
-
def list_names(self) ->
|
|
53
|
+
def list_names(self) -> list[str]:
|
|
48
54
|
"""List all registered names."""
|
|
49
55
|
pass
|
|
50
56
|
|
|
@@ -59,7 +65,7 @@ class IRegistry(ABC):
|
|
|
59
65
|
pass
|
|
60
66
|
|
|
61
67
|
|
|
62
|
-
class GenericRegistry(IRegistry):
|
|
68
|
+
class GenericRegistry[T](IRegistry[T]):
|
|
63
69
|
"""
|
|
64
70
|
Generic registry for dynamic registration and discovery.
|
|
65
71
|
|
|
@@ -75,7 +81,7 @@ class GenericRegistry(IRegistry):
|
|
|
75
81
|
def __init__(
|
|
76
82
|
self,
|
|
77
83
|
name: str = "generic",
|
|
78
|
-
item_type: Optional[
|
|
84
|
+
item_type: Optional[type] = None,
|
|
79
85
|
allow_overwrite: bool = False,
|
|
80
86
|
auto_discovery: bool = False
|
|
81
87
|
):
|
|
@@ -97,13 +103,13 @@ class GenericRegistry(IRegistry):
|
|
|
97
103
|
self._lock = threading.RLock()
|
|
98
104
|
|
|
99
105
|
# Storage
|
|
100
|
-
self._items:
|
|
101
|
-
self._metadata:
|
|
102
|
-
self._factories:
|
|
106
|
+
self._items: dict[str, T] = {}
|
|
107
|
+
self._metadata: dict[str, dict[str, Any]] = {}
|
|
108
|
+
self._factories: dict[str, Callable[[], T]] = {}
|
|
103
109
|
|
|
104
110
|
# Callbacks
|
|
105
|
-
self._registration_callbacks:
|
|
106
|
-
self._unregistration_callbacks:
|
|
111
|
+
self._registration_callbacks: list[Callable[[str, T, dict[str, Any]], None]] = []
|
|
112
|
+
self._unregistration_callbacks: list[Callable[[str, T], None]] = []
|
|
107
113
|
|
|
108
114
|
# Statistics
|
|
109
115
|
self._stats = {
|
|
@@ -119,9 +125,9 @@ class GenericRegistry(IRegistry):
|
|
|
119
125
|
def register(
|
|
120
126
|
self,
|
|
121
127
|
name: str,
|
|
122
|
-
item:
|
|
123
|
-
metadata: Optional[
|
|
124
|
-
factory: Optional[Callable[[],
|
|
128
|
+
item: T,
|
|
129
|
+
metadata: Optional[dict[str, Any]] = None,
|
|
130
|
+
factory: Optional[Callable[[], T]] = None
|
|
125
131
|
) -> bool:
|
|
126
132
|
"""
|
|
127
133
|
Register an item with optional metadata and factory.
|
|
@@ -229,7 +235,7 @@ class GenericRegistry(IRegistry):
|
|
|
229
235
|
logger.error(f"Failed to unregister item '{name}': {e}")
|
|
230
236
|
return False
|
|
231
237
|
|
|
232
|
-
def get(self, name: str) -> Optional[
|
|
238
|
+
def get(self, name: str) -> Optional[T]:
|
|
233
239
|
"""
|
|
234
240
|
Get an item by name.
|
|
235
241
|
|
|
@@ -265,7 +271,7 @@ class GenericRegistry(IRegistry):
|
|
|
265
271
|
self._stats['misses'] += 1
|
|
266
272
|
return None
|
|
267
273
|
|
|
268
|
-
def list_names(self) ->
|
|
274
|
+
def list_names(self) -> list[str]:
|
|
269
275
|
"""List all registered names."""
|
|
270
276
|
with self._lock:
|
|
271
277
|
return list(self._items.keys())
|
|
@@ -299,12 +305,12 @@ class GenericRegistry(IRegistry):
|
|
|
299
305
|
logger.error(f"Failed to clear registry '{self.name}': {e}")
|
|
300
306
|
return False
|
|
301
307
|
|
|
302
|
-
def get_metadata(self, name: str) -> Optional[
|
|
308
|
+
def get_metadata(self, name: str) -> Optional[dict[str, Any]]:
|
|
303
309
|
"""Get metadata for an item."""
|
|
304
310
|
with self._lock:
|
|
305
311
|
return self._metadata.get(name)
|
|
306
312
|
|
|
307
|
-
def update_metadata(self, name: str, metadata:
|
|
313
|
+
def update_metadata(self, name: str, metadata: dict[str, Any]) -> bool:
|
|
308
314
|
"""Update metadata for an item."""
|
|
309
315
|
with self._lock:
|
|
310
316
|
if name not in self._items:
|
|
@@ -316,17 +322,17 @@ class GenericRegistry(IRegistry):
|
|
|
316
322
|
self._metadata[name].update(metadata)
|
|
317
323
|
return True
|
|
318
324
|
|
|
319
|
-
def add_registration_callback(self, callback: Callable[[str,
|
|
325
|
+
def add_registration_callback(self, callback: Callable[[str, T, dict[str, Any]], None]):
|
|
320
326
|
"""Add callback for registration events."""
|
|
321
327
|
with self._lock:
|
|
322
328
|
self._registration_callbacks.append(callback)
|
|
323
329
|
|
|
324
|
-
def add_unregistration_callback(self, callback: Callable[[str,
|
|
330
|
+
def add_unregistration_callback(self, callback: Callable[[str, T], None]):
|
|
325
331
|
"""Add callback for unregistration events."""
|
|
326
332
|
with self._lock:
|
|
327
333
|
self._unregistration_callbacks.append(callback)
|
|
328
334
|
|
|
329
|
-
def get_stats(self) ->
|
|
335
|
+
def get_stats(self) -> dict[str, Any]:
|
|
330
336
|
"""Get registry statistics."""
|
|
331
337
|
with self._lock:
|
|
332
338
|
total_lookups = self._stats['hits'] + self._stats['misses']
|
|
@@ -346,7 +352,7 @@ class GenericRegistry(IRegistry):
|
|
|
346
352
|
'auto_discovery': self.auto_discovery,
|
|
347
353
|
}
|
|
348
354
|
|
|
349
|
-
def discover_items(self, discovery_func: Callable[[],
|
|
355
|
+
def discover_items(self, discovery_func: Callable[[], list[tuple[str, T, dict[str, Any]]]]) -> int:
|
|
350
356
|
"""
|
|
351
357
|
Discover and register items using a discovery function.
|
|
352
358
|
|
|
@@ -374,28 +380,35 @@ class GenericRegistry(IRegistry):
|
|
|
374
380
|
|
|
375
381
|
|
|
376
382
|
# Global registry manager
|
|
377
|
-
_registries:
|
|
383
|
+
_registries: dict[str, GenericRegistry[Any]] = {}
|
|
378
384
|
_registry_lock = threading.RLock()
|
|
379
385
|
|
|
380
386
|
|
|
381
|
-
def get_registry(name: str, **kwargs) -> GenericRegistry:
|
|
387
|
+
def get_registry(name: str, **kwargs) -> GenericRegistry[Any]:
|
|
382
388
|
"""
|
|
383
389
|
Get or create a registry by name.
|
|
384
390
|
|
|
391
|
+
Root cause: Global registry storage uses GenericRegistry[Any] for flexibility.
|
|
392
|
+
Users can create typed registries directly: GenericRegistry[MyType]().
|
|
393
|
+
Priority #3: Maintainability - Clear API design.
|
|
394
|
+
|
|
385
395
|
Args:
|
|
386
396
|
name: Registry name
|
|
387
397
|
**kwargs: Additional arguments for registry creation
|
|
388
398
|
|
|
389
399
|
Returns:
|
|
390
|
-
Registry instance
|
|
400
|
+
Registry instance (untyped for global registry flexibility)
|
|
401
|
+
|
|
402
|
+
Note:
|
|
403
|
+
For type-safe registries, create directly: GenericRegistry[MyType]()
|
|
391
404
|
"""
|
|
392
405
|
with _registry_lock:
|
|
393
406
|
if name not in _registries:
|
|
394
|
-
_registries[name] = GenericRegistry(name=name, **kwargs)
|
|
407
|
+
_registries[name] = GenericRegistry[Any](name=name, **kwargs)
|
|
395
408
|
return _registries[name]
|
|
396
409
|
|
|
397
410
|
|
|
398
|
-
def list_registries() ->
|
|
411
|
+
def list_registries() -> list[str]:
|
|
399
412
|
"""List all registry names."""
|
|
400
413
|
with _registry_lock:
|
|
401
414
|
return list(_registries.keys())
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#exonware/
|
|
1
|
+
#exonware/xwsystem/plugins/base.py
|
|
2
2
|
"""
|
|
3
3
|
Company: eXonware.com
|
|
4
4
|
Author: Eng. Muhammad AlShehri
|
|
5
5
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.0.1.
|
|
6
|
+
Version: 0.0.1.411
|
|
7
7
|
Generation Date: September 04, 2025
|
|
8
8
|
|
|
9
9
|
Plugin system base classes and management.
|
|
@@ -15,7 +15,7 @@ import threading
|
|
|
15
15
|
from abc import ABC, abstractmethod
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any,
|
|
18
|
+
from typing import Any, Optional, Union
|
|
19
19
|
|
|
20
20
|
from importlib.metadata import entry_points
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ from ..runtime.reflection import ReflectionUtils
|
|
|
24
24
|
from .contracts import IPlugin, PluginState, PluginType, PluginPriority
|
|
25
25
|
from .errors import PluginError
|
|
26
26
|
|
|
27
|
-
logger = get_logger("
|
|
27
|
+
logger = get_logger("xwsystem.plugins.base")
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@dataclass
|
|
@@ -39,8 +39,8 @@ class APluginInfo:
|
|
|
39
39
|
class_name: str = ""
|
|
40
40
|
enabled: bool = True
|
|
41
41
|
priority: int = 100
|
|
42
|
-
dependencies:
|
|
43
|
-
metadata:
|
|
42
|
+
dependencies: list[str] = field(default_factory=list)
|
|
43
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class APlugin(IPlugin):
|
|
@@ -50,7 +50,7 @@ class APlugin(IPlugin):
|
|
|
50
50
|
Plugins should inherit from this class and implement the required methods.
|
|
51
51
|
"""
|
|
52
52
|
|
|
53
|
-
def __init__(self, config: Optional[
|
|
53
|
+
def __init__(self, config: Optional[dict[str, Any]] = None) -> None:
|
|
54
54
|
"""
|
|
55
55
|
Initialize plugin with optional configuration.
|
|
56
56
|
|
|
@@ -84,7 +84,7 @@ class APlugin(IPlugin):
|
|
|
84
84
|
return ""
|
|
85
85
|
|
|
86
86
|
@property
|
|
87
|
-
def dependencies(self) ->
|
|
87
|
+
def dependencies(self) -> list[str]:
|
|
88
88
|
"""List of plugin dependencies."""
|
|
89
89
|
return []
|
|
90
90
|
|
|
@@ -102,7 +102,7 @@ class APlugin(IPlugin):
|
|
|
102
102
|
"""Check if plugin is initialized."""
|
|
103
103
|
return self._initialized
|
|
104
104
|
|
|
105
|
-
def get_info(self) ->
|
|
105
|
+
def get_info(self) -> dict[str, Any]:
|
|
106
106
|
"""Get plugin information."""
|
|
107
107
|
return {
|
|
108
108
|
'name': self.name,
|
|
@@ -133,7 +133,7 @@ class APlugin(IPlugin):
|
|
|
133
133
|
"""Get plugin priority."""
|
|
134
134
|
return PluginPriority.NORMAL
|
|
135
135
|
|
|
136
|
-
def get_dependencies(self) ->
|
|
136
|
+
def get_dependencies(self) -> list[str]:
|
|
137
137
|
"""Get plugin dependencies."""
|
|
138
138
|
return self.dependencies
|
|
139
139
|
|
|
@@ -169,9 +169,9 @@ class APluginRegistry:
|
|
|
169
169
|
|
|
170
170
|
def __init__(self) -> None:
|
|
171
171
|
"""Initialize plugin registry."""
|
|
172
|
-
self._plugins:
|
|
173
|
-
self._plugin_info:
|
|
174
|
-
self._enabled_plugins:
|
|
172
|
+
self._plugins: dict[str, APlugin] = {}
|
|
173
|
+
self._plugin_info: dict[str, APluginInfo] = {}
|
|
174
|
+
self._enabled_plugins: set[str] = set()
|
|
175
175
|
self._lock = threading.RLock()
|
|
176
176
|
|
|
177
177
|
def register(self, plugin: APlugin) -> None:
|
|
@@ -234,12 +234,12 @@ class APluginRegistry:
|
|
|
234
234
|
with self._lock:
|
|
235
235
|
return self._plugins.get(plugin_name)
|
|
236
236
|
|
|
237
|
-
def get_all(self) ->
|
|
237
|
+
def get_all(self) -> dict[str, APlugin]:
|
|
238
238
|
"""Get all registered plugins."""
|
|
239
239
|
with self._lock:
|
|
240
240
|
return self._plugins.copy()
|
|
241
241
|
|
|
242
|
-
def get_enabled(self) ->
|
|
242
|
+
def get_enabled(self) -> dict[str, APlugin]:
|
|
243
243
|
"""Get all enabled plugins."""
|
|
244
244
|
with self._lock:
|
|
245
245
|
return {name: plugin for name, plugin in self._plugins.items()
|
|
@@ -290,7 +290,7 @@ class APluginRegistry:
|
|
|
290
290
|
logger.info(f"Disabled plugin: {plugin_name}")
|
|
291
291
|
return True
|
|
292
292
|
|
|
293
|
-
def list_plugins(self) ->
|
|
293
|
+
def list_plugins(self) -> list[APluginInfo]:
|
|
294
294
|
"""Get information about all registered plugins."""
|
|
295
295
|
with self._lock:
|
|
296
296
|
return list(self._plugin_info.values())
|
|
@@ -326,9 +326,9 @@ class APluginManager:
|
|
|
326
326
|
registry: Optional plugin registry to use
|
|
327
327
|
"""
|
|
328
328
|
self.registry = registry or APluginRegistry()
|
|
329
|
-
self._discovered_plugins:
|
|
329
|
+
self._discovered_plugins: dict[str, dict[str, Any]] = {}
|
|
330
330
|
|
|
331
|
-
def load_plugin_from_module(self, module_path: str, class_name: str, config: Optional[
|
|
331
|
+
def load_plugin_from_module(self, module_path: str, class_name: str, config: Optional[dict[str, Any]] = None) -> APlugin:
|
|
332
332
|
"""
|
|
333
333
|
Load plugin from module path and class name.
|
|
334
334
|
|
|
@@ -357,7 +357,7 @@ class APluginManager:
|
|
|
357
357
|
except Exception as e:
|
|
358
358
|
raise PluginError(f"Failed to load plugin {module_path}.{class_name}: {e}") from e
|
|
359
359
|
|
|
360
|
-
def load_plugin_from_file(self, file_path: Union[str, Path], class_name: str, config: Optional[
|
|
360
|
+
def load_plugin_from_file(self, file_path: Union[str, Path], class_name: str, config: Optional[dict[str, Any]] = None) -> APlugin:
|
|
361
361
|
"""
|
|
362
362
|
Load plugin from Python file.
|
|
363
363
|
|
|
@@ -403,7 +403,7 @@ class APluginManager:
|
|
|
403
403
|
except Exception as e:
|
|
404
404
|
raise PluginError(f"Failed to load plugin from {file_path}: {e}") from e
|
|
405
405
|
|
|
406
|
-
def discover_entry_points(self, group: str = "
|
|
406
|
+
def discover_entry_points(self, group: str = "xwsystem.plugins") -> dict[str, dict[str, Any]]:
|
|
407
407
|
"""
|
|
408
408
|
Discover plugins through entry points.
|
|
409
409
|
|
|
@@ -439,7 +439,7 @@ class APluginManager:
|
|
|
439
439
|
|
|
440
440
|
return discovered
|
|
441
441
|
|
|
442
|
-
def discover_directory(self, directory: Union[str, Path], pattern: str = "*.py") ->
|
|
442
|
+
def discover_directory(self, directory: Union[str, Path], pattern: str = "*.py") -> dict[str, dict[str, Any]]:
|
|
443
443
|
"""
|
|
444
444
|
Discover plugins in a directory.
|
|
445
445
|
|
|
@@ -492,7 +492,7 @@ class APluginManager:
|
|
|
492
492
|
|
|
493
493
|
return discovered
|
|
494
494
|
|
|
495
|
-
def load_discovered_plugins(self, plugin_names: Optional[
|
|
495
|
+
def load_discovered_plugins(self, plugin_names: Optional[list[str]] = None, config: Optional[dict[str, dict[str, Any]]] = None) -> list[APlugin]:
|
|
496
496
|
"""
|
|
497
497
|
Load discovered plugins.
|
|
498
498
|
|
|
@@ -539,7 +539,7 @@ class APluginManager:
|
|
|
539
539
|
logger.info(f"Loaded {len(loaded)} plugins")
|
|
540
540
|
return loaded
|
|
541
541
|
|
|
542
|
-
def initialize_plugins(self, plugin_names: Optional[
|
|
542
|
+
def initialize_plugins(self, plugin_names: Optional[list[str]] = None) -> None:
|
|
543
543
|
"""
|
|
544
544
|
Initialize plugins with dependency resolution.
|
|
545
545
|
|
|
@@ -585,7 +585,7 @@ class APluginManager:
|
|
|
585
585
|
except Exception as e:
|
|
586
586
|
logger.error(f"Error shutting down plugin {plugin.name}: {e}")
|
|
587
587
|
|
|
588
|
-
def get_discovered_plugins(self) ->
|
|
588
|
+
def get_discovered_plugins(self) -> dict[str, dict[str, Any]]:
|
|
589
589
|
"""Get all discovered plugins."""
|
|
590
590
|
return self._discovered_plugins.copy()
|
|
591
591
|
|
|
@@ -605,7 +605,7 @@ def get_plugin_manager() -> APluginManager:
|
|
|
605
605
|
class BasePlugin(APlugin):
|
|
606
606
|
"""Base plugin class for backward compatibility."""
|
|
607
607
|
|
|
608
|
-
def __init__(self, config: Optional[
|
|
608
|
+
def __init__(self, config: Optional[dict[str, Any]] = None):
|
|
609
609
|
"""Initialize base plugin."""
|
|
610
610
|
super().__init__(config)
|
|
611
611
|
self._name = "BasePlugin"
|