exonware-xwlazy 0.1.0.23__py3-none-any.whl → 1.0.1.2__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 +85 -34
- exonware/xwlazy/version.py +5 -5
- exonware/xwlazy.py +2546 -0
- exonware/xwlazy_external_libs.toml +716 -0
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/METADATA +5 -1
- exonware_xwlazy-1.0.1.2.dist-info/RECORD +8 -0
- exonware/xwlazy/__init__.py +0 -379
- exonware/xwlazy/common/__init__.py +0 -55
- exonware/xwlazy/common/base.py +0 -65
- exonware/xwlazy/common/cache.py +0 -504
- exonware/xwlazy/common/logger.py +0 -257
- exonware/xwlazy/common/services/__init__.py +0 -72
- exonware/xwlazy/common/services/dependency_mapper.py +0 -250
- exonware/xwlazy/common/services/install_async_utils.py +0 -170
- exonware/xwlazy/common/services/install_cache_utils.py +0 -245
- exonware/xwlazy/common/services/keyword_detection.py +0 -283
- exonware/xwlazy/common/services/spec_cache.py +0 -165
- exonware/xwlazy/common/services/state_manager.py +0 -84
- exonware/xwlazy/common/strategies/__init__.py +0 -28
- exonware/xwlazy/common/strategies/caching_dict.py +0 -44
- exonware/xwlazy/common/strategies/caching_installation.py +0 -88
- exonware/xwlazy/common/strategies/caching_lfu.py +0 -66
- exonware/xwlazy/common/strategies/caching_lru.py +0 -63
- exonware/xwlazy/common/strategies/caching_multitier.py +0 -59
- exonware/xwlazy/common/strategies/caching_ttl.py +0 -59
- exonware/xwlazy/common/utils.py +0 -142
- exonware/xwlazy/config.py +0 -193
- exonware/xwlazy/contracts.py +0 -1533
- exonware/xwlazy/defs.py +0 -378
- exonware/xwlazy/errors.py +0 -276
- exonware/xwlazy/facade.py +0 -1137
- exonware/xwlazy/host/__init__.py +0 -8
- exonware/xwlazy/host/conf.py +0 -16
- exonware/xwlazy/module/__init__.py +0 -18
- exonware/xwlazy/module/base.py +0 -622
- exonware/xwlazy/module/data.py +0 -17
- exonware/xwlazy/module/facade.py +0 -246
- exonware/xwlazy/module/importer_engine.py +0 -2964
- exonware/xwlazy/module/partial_module_detector.py +0 -275
- exonware/xwlazy/module/strategies/__init__.py +0 -22
- exonware/xwlazy/module/strategies/module_helper_lazy.py +0 -93
- exonware/xwlazy/module/strategies/module_helper_simple.py +0 -65
- exonware/xwlazy/module/strategies/module_manager_advanced.py +0 -111
- exonware/xwlazy/module/strategies/module_manager_simple.py +0 -95
- exonware/xwlazy/package/__init__.py +0 -18
- exonware/xwlazy/package/base.py +0 -863
- exonware/xwlazy/package/conf.py +0 -324
- exonware/xwlazy/package/data.py +0 -17
- exonware/xwlazy/package/facade.py +0 -480
- exonware/xwlazy/package/services/__init__.py +0 -84
- exonware/xwlazy/package/services/async_install_handle.py +0 -87
- exonware/xwlazy/package/services/config_manager.py +0 -249
- exonware/xwlazy/package/services/discovery.py +0 -435
- exonware/xwlazy/package/services/host_packages.py +0 -180
- exonware/xwlazy/package/services/install_async.py +0 -291
- exonware/xwlazy/package/services/install_cache.py +0 -145
- exonware/xwlazy/package/services/install_interactive.py +0 -59
- exonware/xwlazy/package/services/install_policy.py +0 -156
- exonware/xwlazy/package/services/install_registry.py +0 -54
- exonware/xwlazy/package/services/install_result.py +0 -17
- exonware/xwlazy/package/services/install_sbom.py +0 -153
- exonware/xwlazy/package/services/install_utils.py +0 -79
- exonware/xwlazy/package/services/installer_engine.py +0 -406
- exonware/xwlazy/package/services/lazy_installer.py +0 -803
- exonware/xwlazy/package/services/manifest.py +0 -503
- exonware/xwlazy/package/services/strategy_registry.py +0 -324
- exonware/xwlazy/package/strategies/__init__.py +0 -57
- exonware/xwlazy/package/strategies/package_discovery_file.py +0 -129
- exonware/xwlazy/package/strategies/package_discovery_hybrid.py +0 -84
- exonware/xwlazy/package/strategies/package_discovery_manifest.py +0 -101
- exonware/xwlazy/package/strategies/package_execution_async.py +0 -113
- exonware/xwlazy/package/strategies/package_execution_cached.py +0 -90
- exonware/xwlazy/package/strategies/package_execution_pip.py +0 -99
- exonware/xwlazy/package/strategies/package_execution_wheel.py +0 -106
- exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +0 -100
- exonware/xwlazy/package/strategies/package_mapping_hybrid.py +0 -105
- exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +0 -100
- exonware/xwlazy/package/strategies/package_policy_allow_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_deny_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_permissive.py +0 -46
- exonware/xwlazy/package/strategies/package_timing_clean.py +0 -67
- exonware/xwlazy/package/strategies/package_timing_full.py +0 -66
- exonware/xwlazy/package/strategies/package_timing_smart.py +0 -68
- exonware/xwlazy/package/strategies/package_timing_temporary.py +0 -66
- exonware/xwlazy/runtime/__init__.py +0 -18
- exonware/xwlazy/runtime/adaptive_learner.py +0 -129
- exonware/xwlazy/runtime/base.py +0 -274
- exonware/xwlazy/runtime/facade.py +0 -94
- exonware/xwlazy/runtime/intelligent_selector.py +0 -170
- exonware/xwlazy/runtime/metrics.py +0 -60
- exonware/xwlazy/runtime/performance.py +0 -37
- exonware_xwlazy-0.1.0.23.dist-info/RECORD +0 -93
- xwlazy/__init__.py +0 -14
- xwlazy/lazy.py +0 -30
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Partial Module Detection Strategies
|
|
3
|
-
|
|
4
|
-
Detects if a module in sys.modules is partially initialized (still being imported).
|
|
5
|
-
This prevents returning partially initialized modules that cause ImportErrors.
|
|
6
|
-
|
|
7
|
-
Company: eXonware.com
|
|
8
|
-
Author: Eng. Muhammad AlShehri
|
|
9
|
-
Email: connect@exonware.com
|
|
10
|
-
|
|
11
|
-
Generation Date: 27-Dec-2025
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import sys
|
|
15
|
-
import threading
|
|
16
|
-
from types import ModuleType
|
|
17
|
-
from typing import Optional
|
|
18
|
-
from enum import Enum
|
|
19
|
-
|
|
20
|
-
class DetectionStrategy(Enum):
|
|
21
|
-
"""Different strategies for detecting partially initialized modules."""
|
|
22
|
-
FRAME_STACK = "frame_stack" # Check call stack for import functions
|
|
23
|
-
ATTRIBUTE_CHECK = "attribute_check" # Check if expected attributes exist
|
|
24
|
-
IMPORT_LOCK = "import_lock" # Use importlib's import lock state
|
|
25
|
-
MODULE_STATE = "module_state" # Check module's __spec__ and loader state
|
|
26
|
-
HYBRID = "hybrid" # Combine multiple strategies
|
|
27
|
-
|
|
28
|
-
# Track modules currently being imported (thread-safe)
|
|
29
|
-
_importing_modules: set[str] = set()
|
|
30
|
-
_import_lock = threading.RLock()
|
|
31
|
-
|
|
32
|
-
def mark_module_importing(module_name: str) -> None:
|
|
33
|
-
"""Mark a module as currently being imported."""
|
|
34
|
-
with _import_lock:
|
|
35
|
-
_importing_modules.add(module_name)
|
|
36
|
-
|
|
37
|
-
def unmark_module_importing(module_name: str) -> None:
|
|
38
|
-
"""Unmark a module as no longer being imported."""
|
|
39
|
-
with _import_lock:
|
|
40
|
-
_importing_modules.discard(module_name)
|
|
41
|
-
|
|
42
|
-
def is_module_importing(module_name: str) -> bool:
|
|
43
|
-
"""Check if a module is currently being imported."""
|
|
44
|
-
with _import_lock:
|
|
45
|
-
return module_name in _importing_modules
|
|
46
|
-
|
|
47
|
-
class PartialModuleDetector:
|
|
48
|
-
"""
|
|
49
|
-
Modular detector for partially initialized modules.
|
|
50
|
-
|
|
51
|
-
Uses different strategies to detect if a module in sys.modules
|
|
52
|
-
is still being initialized and shouldn't be returned yet.
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
def __init__(self, strategy: DetectionStrategy = DetectionStrategy.HYBRID):
|
|
56
|
-
self.strategy = strategy
|
|
57
|
-
|
|
58
|
-
def is_partially_initialized(self, module_name: str, module: ModuleType) -> bool:
|
|
59
|
-
"""
|
|
60
|
-
Check if module is partially initialized (still being imported).
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
module_name: Name of the module
|
|
64
|
-
module: Module object from sys.modules
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
True if module is partially initialized, False if fully loaded
|
|
68
|
-
"""
|
|
69
|
-
if self.strategy == DetectionStrategy.FRAME_STACK:
|
|
70
|
-
return self._check_frame_stack(module_name)
|
|
71
|
-
elif self.strategy == DetectionStrategy.ATTRIBUTE_CHECK:
|
|
72
|
-
return self._check_attributes(module_name, module)
|
|
73
|
-
elif self.strategy == DetectionStrategy.IMPORT_LOCK:
|
|
74
|
-
return self._check_import_lock(module_name)
|
|
75
|
-
elif self.strategy == DetectionStrategy.MODULE_STATE:
|
|
76
|
-
return self._check_module_state(module_name, module)
|
|
77
|
-
elif self.strategy == DetectionStrategy.HYBRID:
|
|
78
|
-
return self._check_hybrid(module_name, module)
|
|
79
|
-
else:
|
|
80
|
-
return False
|
|
81
|
-
|
|
82
|
-
def _check_frame_stack(self, module_name: str) -> bool:
|
|
83
|
-
"""
|
|
84
|
-
Strategy 1: Check call stack for import-related functions.
|
|
85
|
-
|
|
86
|
-
If we're in _find_and_load, _load_unlocked, or exec_module,
|
|
87
|
-
the module is likely still being imported.
|
|
88
|
-
|
|
89
|
-
CRITICAL: This checks if the CURRENT import call is for this module,
|
|
90
|
-
not just if we're in an import function.
|
|
91
|
-
"""
|
|
92
|
-
try:
|
|
93
|
-
# Start from frame 2 (skip our own function and the caller)
|
|
94
|
-
frame = sys._getframe(2)
|
|
95
|
-
depth = 0
|
|
96
|
-
max_depth = 30 # Increased depth to catch deeper import chains
|
|
97
|
-
|
|
98
|
-
while frame and depth < max_depth:
|
|
99
|
-
code = frame.f_code
|
|
100
|
-
func_name = code.co_name
|
|
101
|
-
|
|
102
|
-
# Check for import-related functions in Python's importlib
|
|
103
|
-
import_keywords = [
|
|
104
|
-
'_find_and_load', '_load_unlocked', 'exec_module',
|
|
105
|
-
'_call_with_frames_removed', '_load_module_spec',
|
|
106
|
-
'_intercepting_import', '__import__'
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
if any(keyword in func_name for keyword in import_keywords):
|
|
110
|
-
# Check if this module is in the frame's locals or globals
|
|
111
|
-
if hasattr(frame, 'f_locals'):
|
|
112
|
-
locals_dict = frame.f_locals
|
|
113
|
-
# Check various ways module name might appear
|
|
114
|
-
frame_module_name = (
|
|
115
|
-
locals_dict.get('name') or
|
|
116
|
-
locals_dict.get('fullname') or
|
|
117
|
-
locals_dict.get('__name__') or
|
|
118
|
-
(locals_dict.get('module') and getattr(locals_dict.get('module'), '__name__', None))
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
if frame_module_name == module_name:
|
|
122
|
-
return True
|
|
123
|
-
|
|
124
|
-
# Also check globals
|
|
125
|
-
if hasattr(frame, 'f_globals'):
|
|
126
|
-
globals_dict = frame.f_globals
|
|
127
|
-
if '__name__' in globals_dict and globals_dict.get('__name__') == module_name:
|
|
128
|
-
return True
|
|
129
|
-
|
|
130
|
-
frame = frame.f_back
|
|
131
|
-
depth += 1
|
|
132
|
-
except (AttributeError, ValueError):
|
|
133
|
-
# Frame inspection failed, assume not importing
|
|
134
|
-
pass
|
|
135
|
-
|
|
136
|
-
return False
|
|
137
|
-
|
|
138
|
-
def _check_attributes(self, module_name: str, module: ModuleType) -> bool:
|
|
139
|
-
"""
|
|
140
|
-
Strategy 2: Check if module has expected attributes.
|
|
141
|
-
|
|
142
|
-
A fully initialized module should have certain attributes.
|
|
143
|
-
If key attributes are missing, it might be partially initialized.
|
|
144
|
-
|
|
145
|
-
CRITICAL: We need to be careful - some modules legitimately don't have
|
|
146
|
-
__file__ (namespace packages, built-ins). We only flag as partial if
|
|
147
|
-
we're CERTAIN it's not fully initialized.
|
|
148
|
-
"""
|
|
149
|
-
module_dict = getattr(module, '__dict__', {})
|
|
150
|
-
|
|
151
|
-
# Check for placeholder patterns (lazy loaders often add __getattr__)
|
|
152
|
-
if hasattr(module, '__getattr__'):
|
|
153
|
-
# If it has __getattr__ but very few attributes, it's likely a placeholder
|
|
154
|
-
if len(module_dict) <= 5: # Only __name__, __loader__, __spec__, __getattr__, etc.
|
|
155
|
-
if not hasattr(module, '__file__') and not hasattr(module, '__path__'):
|
|
156
|
-
# Not a namespace package and no file - likely placeholder
|
|
157
|
-
return True
|
|
158
|
-
|
|
159
|
-
# Check if module has __spec__ but loader hasn't executed yet
|
|
160
|
-
if hasattr(module, '__spec__') and module.__spec__ is not None:
|
|
161
|
-
spec = module.__spec__
|
|
162
|
-
# If loader exists but module dict is very small, it might not be fully loaded
|
|
163
|
-
if hasattr(spec, 'loader') and spec.loader is not None:
|
|
164
|
-
# Check if module dict is suspiciously small (only metadata, no actual content)
|
|
165
|
-
if len(module_dict) <= 3:
|
|
166
|
-
# Only has __name__, __loader__, __spec__ - likely not executed
|
|
167
|
-
if not hasattr(module, '__file__') and not hasattr(module, '__path__'):
|
|
168
|
-
# Not a namespace package - likely partial
|
|
169
|
-
return True
|
|
170
|
-
|
|
171
|
-
# If module is in sys.modules but has no meaningful content, it's likely partial
|
|
172
|
-
# This is a heuristic - be conservative
|
|
173
|
-
if module_name in sys.modules:
|
|
174
|
-
# Check if module has been executed by looking for non-metadata attributes
|
|
175
|
-
metadata_attrs = {'__name__', '__loader__', '__spec__', '__package__', '__file__', '__path__', '__cached__'}
|
|
176
|
-
content_attrs = set(module_dict.keys()) - metadata_attrs
|
|
177
|
-
if len(content_attrs) == 0 and len(module_dict) > 0:
|
|
178
|
-
# Has metadata but no content - might be partial
|
|
179
|
-
# But be conservative - only flag if we're sure
|
|
180
|
-
if hasattr(module, '__loader__') and module.__loader__ is not None:
|
|
181
|
-
# Has loader but no content - likely partial
|
|
182
|
-
return True
|
|
183
|
-
|
|
184
|
-
return False
|
|
185
|
-
|
|
186
|
-
def _check_import_lock(self, module_name: str) -> bool:
|
|
187
|
-
"""
|
|
188
|
-
Strategy 3: Use our own import tracking.
|
|
189
|
-
|
|
190
|
-
Check if module is marked as currently being imported.
|
|
191
|
-
"""
|
|
192
|
-
return is_module_importing(module_name)
|
|
193
|
-
|
|
194
|
-
def _check_module_state(self, module_name: str, module: ModuleType) -> bool:
|
|
195
|
-
"""
|
|
196
|
-
Strategy 4: Check module's internal state.
|
|
197
|
-
|
|
198
|
-
Look at __spec__, __loader__, and other state indicators.
|
|
199
|
-
"""
|
|
200
|
-
# If module doesn't have __spec__, it's likely not fully initialized
|
|
201
|
-
if not hasattr(module, '__spec__') or module.__spec__ is None:
|
|
202
|
-
# Exception: Some built-in modules don't have __spec__
|
|
203
|
-
if module_name not in sys.builtin_module_names:
|
|
204
|
-
return True
|
|
205
|
-
|
|
206
|
-
# Check if module has __loader__ but loader hasn't executed
|
|
207
|
-
if hasattr(module, '__loader__') and module.__loader__ is not None:
|
|
208
|
-
loader = module.__loader__
|
|
209
|
-
# If loader has exec_module but module doesn't have expected attributes,
|
|
210
|
-
# it might not be fully executed
|
|
211
|
-
if hasattr(loader, 'exec_module'):
|
|
212
|
-
# Check if module has been executed by looking for common attributes
|
|
213
|
-
# This is heuristic - modules should have some content after execution
|
|
214
|
-
module_dict = getattr(module, '__dict__', {})
|
|
215
|
-
# If dict is mostly empty (only has __name__, __loader__, __spec__),
|
|
216
|
-
# it might not be fully initialized
|
|
217
|
-
if len(module_dict) <= 3:
|
|
218
|
-
# Check if it's a legitimate empty module
|
|
219
|
-
if '__file__' not in module_dict and '__path__' not in module_dict:
|
|
220
|
-
return True
|
|
221
|
-
|
|
222
|
-
return False
|
|
223
|
-
|
|
224
|
-
def _check_hybrid(self, module_name: str, module: ModuleType) -> bool:
|
|
225
|
-
"""
|
|
226
|
-
Strategy 5: Hybrid - combine multiple strategies.
|
|
227
|
-
|
|
228
|
-
Returns True if ANY strategy indicates partial initialization.
|
|
229
|
-
This is the most conservative approach.
|
|
230
|
-
|
|
231
|
-
NOTE: We check frame stack FIRST because it's most accurate for detecting
|
|
232
|
-
modules currently being imported. Import lock is secondary.
|
|
233
|
-
"""
|
|
234
|
-
# Check frame stack first (most accurate for detecting active imports)
|
|
235
|
-
if self._check_frame_stack(module_name):
|
|
236
|
-
return True
|
|
237
|
-
|
|
238
|
-
# Check attributes (fast, reliable)
|
|
239
|
-
if self._check_attributes(module_name, module):
|
|
240
|
-
return True
|
|
241
|
-
|
|
242
|
-
# Check module state (medium speed)
|
|
243
|
-
if self._check_module_state(module_name, module):
|
|
244
|
-
return True
|
|
245
|
-
|
|
246
|
-
# Check import lock last (may have false positives)
|
|
247
|
-
# Only use if other checks are inconclusive
|
|
248
|
-
if is_module_importing(module_name):
|
|
249
|
-
# Double-check with frame stack to avoid false positives
|
|
250
|
-
if self._check_frame_stack(module_name):
|
|
251
|
-
return True
|
|
252
|
-
|
|
253
|
-
return False
|
|
254
|
-
|
|
255
|
-
# Global detector instance (defaults to HYBRID strategy)
|
|
256
|
-
_default_detector = PartialModuleDetector(DetectionStrategy.HYBRID)
|
|
257
|
-
|
|
258
|
-
def is_partially_initialized(module_name: str, module: ModuleType,
|
|
259
|
-
strategy: Optional[DetectionStrategy] = None) -> bool:
|
|
260
|
-
"""
|
|
261
|
-
Convenience function to check if module is partially initialized.
|
|
262
|
-
|
|
263
|
-
Args:
|
|
264
|
-
module_name: Name of the module
|
|
265
|
-
module: Module object from sys.modules
|
|
266
|
-
strategy: Optional strategy override
|
|
267
|
-
|
|
268
|
-
Returns:
|
|
269
|
-
True if module is partially initialized
|
|
270
|
-
"""
|
|
271
|
-
if strategy:
|
|
272
|
-
detector = PartialModuleDetector(strategy)
|
|
273
|
-
return detector.is_partially_initialized(module_name, module)
|
|
274
|
-
return _default_detector.is_partially_initialized(module_name, module)
|
|
275
|
-
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Module Strategies - Helper and Manager implementations.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from .module_helper_simple import SimpleHelper
|
|
12
|
-
from .module_helper_lazy import LazyHelper
|
|
13
|
-
from .module_manager_simple import SimpleManager
|
|
14
|
-
from .module_manager_advanced import AdvancedManager
|
|
15
|
-
|
|
16
|
-
__all__ = [
|
|
17
|
-
'SimpleHelper',
|
|
18
|
-
'LazyHelper',
|
|
19
|
-
'SimpleManager',
|
|
20
|
-
'AdvancedManager',
|
|
21
|
-
]
|
|
22
|
-
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Lazy Module Helper Strategy - Deferred loading.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Lazy loading - loads module on first access with caching.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import importlib
|
|
14
|
-
import importlib.util
|
|
15
|
-
import threading
|
|
16
|
-
from types import ModuleType
|
|
17
|
-
from typing import Any, Optional
|
|
18
|
-
from ...module.base import AModuleHelperStrategy
|
|
19
|
-
from ..data import ModuleData
|
|
20
|
-
|
|
21
|
-
class LazyHelper(AModuleHelperStrategy):
|
|
22
|
-
"""
|
|
23
|
-
Lazy helper - deferred loading with caching.
|
|
24
|
-
|
|
25
|
-
Loads module on first access and caches it.
|
|
26
|
-
Thread-safe with circular import detection.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
def __init__(self):
|
|
30
|
-
"""Initialize lazy helper."""
|
|
31
|
-
self._cache: dict[str, ModuleType] = {}
|
|
32
|
-
self._loading: dict[str, bool] = {}
|
|
33
|
-
self._lock = threading.RLock()
|
|
34
|
-
|
|
35
|
-
def load(self, module_path: str, package_helper: Any) -> ModuleType:
|
|
36
|
-
"""
|
|
37
|
-
Load the module lazily (with caching).
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
module_path: Module path to load
|
|
41
|
-
package_helper: Package helper (unused)
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
Loaded module
|
|
45
|
-
"""
|
|
46
|
-
# Check cache first
|
|
47
|
-
if module_path in self._cache:
|
|
48
|
-
return self._cache[module_path]
|
|
49
|
-
|
|
50
|
-
with self._lock:
|
|
51
|
-
# Double-check after acquiring lock
|
|
52
|
-
if module_path in self._cache:
|
|
53
|
-
return self._cache[module_path]
|
|
54
|
-
|
|
55
|
-
# Check for circular import
|
|
56
|
-
if self._loading.get(module_path, False):
|
|
57
|
-
raise ImportError(f"Circular import detected for {module_path}")
|
|
58
|
-
|
|
59
|
-
try:
|
|
60
|
-
self._loading[module_path] = True
|
|
61
|
-
module = importlib.import_module(module_path)
|
|
62
|
-
self._cache[module_path] = module
|
|
63
|
-
return module
|
|
64
|
-
finally:
|
|
65
|
-
self._loading[module_path] = False
|
|
66
|
-
|
|
67
|
-
def unload(self, module_path: str) -> None:
|
|
68
|
-
"""
|
|
69
|
-
Unload the module from cache.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
module_path: Module path to unload
|
|
73
|
-
"""
|
|
74
|
-
with self._lock:
|
|
75
|
-
self._cache.pop(module_path, None)
|
|
76
|
-
self._loading.pop(module_path, None)
|
|
77
|
-
|
|
78
|
-
def check_importability(self, path: str) -> bool:
|
|
79
|
-
"""
|
|
80
|
-
Check if module is importable.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
path: Module path to check
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
True if importable, False otherwise
|
|
87
|
-
"""
|
|
88
|
-
try:
|
|
89
|
-
spec = importlib.util.find_spec(path)
|
|
90
|
-
return spec is not None
|
|
91
|
-
except (ValueError, AttributeError, ImportError):
|
|
92
|
-
return False
|
|
93
|
-
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Simple Module Helper Strategy - Basic synchronous loading.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Simple synchronous module loading - no lazy loading.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import importlib
|
|
14
|
-
from types import ModuleType
|
|
15
|
-
from typing import Any
|
|
16
|
-
from ...module.base import AModuleHelperStrategy
|
|
17
|
-
|
|
18
|
-
class SimpleHelper(AModuleHelperStrategy):
|
|
19
|
-
"""
|
|
20
|
-
Simple helper - standard synchronous import.
|
|
21
|
-
|
|
22
|
-
No lazy loading, no caching, just direct importlib.import_module.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def load(self, module_path: str, package_helper: Any) -> ModuleType:
|
|
26
|
-
"""
|
|
27
|
-
Load the module synchronously.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
module_path: Module path to load
|
|
31
|
-
package_helper: Package helper (unused in simple mode)
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
Loaded module
|
|
35
|
-
"""
|
|
36
|
-
return importlib.import_module(module_path)
|
|
37
|
-
|
|
38
|
-
def unload(self, module_path: str) -> None:
|
|
39
|
-
"""
|
|
40
|
-
Unload the module.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
module_path: Module path to unload
|
|
44
|
-
"""
|
|
45
|
-
# Simple mode doesn't track loaded modules
|
|
46
|
-
# Could remove from sys.modules if needed
|
|
47
|
-
pass
|
|
48
|
-
|
|
49
|
-
def check_importability(self, path: str) -> bool:
|
|
50
|
-
"""
|
|
51
|
-
Check if module is importable.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
path: Module path to check
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
True if importable, False otherwise
|
|
58
|
-
"""
|
|
59
|
-
import importlib.util
|
|
60
|
-
try:
|
|
61
|
-
spec = importlib.util.find_spec(path)
|
|
62
|
-
return spec is not None
|
|
63
|
-
except (ValueError, AttributeError, ImportError):
|
|
64
|
-
return False
|
|
65
|
-
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Advanced Module Manager Strategy - Full features.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Advanced manager - with hooks, error handling, and full features.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import sys
|
|
14
|
-
from types import ModuleType
|
|
15
|
-
from typing import Optional
|
|
16
|
-
from ...module.base import AModuleManagerStrategy
|
|
17
|
-
from ...contracts import ICachingStrategy, IModuleHelperStrategy
|
|
18
|
-
from ...package.base import APackageHelper
|
|
19
|
-
from ...module import importer_engine
|
|
20
|
-
|
|
21
|
-
class AdvancedManager(AModuleManagerStrategy):
|
|
22
|
-
"""
|
|
23
|
-
Advanced manager - full features with hooks and error handling.
|
|
24
|
-
|
|
25
|
-
Supports import hooks, error recovery, and all advanced features.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
package_name: str,
|
|
31
|
-
package_helper: APackageHelper,
|
|
32
|
-
caching: ICachingStrategy,
|
|
33
|
-
helper: IModuleHelperStrategy
|
|
34
|
-
):
|
|
35
|
-
"""
|
|
36
|
-
Initialize advanced manager.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
package_name: Package name for isolation
|
|
40
|
-
package_helper: Package helper instance
|
|
41
|
-
caching: Caching strategy
|
|
42
|
-
helper: Helper strategy
|
|
43
|
-
"""
|
|
44
|
-
self._package_name = package_name
|
|
45
|
-
self._package_helper = package_helper
|
|
46
|
-
self._caching = caching
|
|
47
|
-
self._helper = helper
|
|
48
|
-
self._import_hook: Optional[importer_engine.LazyImportHook] = None
|
|
49
|
-
|
|
50
|
-
def load_module(self, module_path: str) -> ModuleType:
|
|
51
|
-
"""
|
|
52
|
-
Load a module.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
module_path: Module path to load
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
Loaded module
|
|
59
|
-
"""
|
|
60
|
-
# Check cache first
|
|
61
|
-
cached = self._caching.get(module_path)
|
|
62
|
-
if cached is not None:
|
|
63
|
-
return cached
|
|
64
|
-
|
|
65
|
-
# Load using helper
|
|
66
|
-
module = self._helper.load(module_path, self._package_helper)
|
|
67
|
-
|
|
68
|
-
# Cache it
|
|
69
|
-
self._caching.set(module_path, module)
|
|
70
|
-
|
|
71
|
-
return module
|
|
72
|
-
|
|
73
|
-
def unload_module(self, module_path: str) -> None:
|
|
74
|
-
"""
|
|
75
|
-
Unload a module.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
module_path: Module path to unload
|
|
79
|
-
"""
|
|
80
|
-
self._helper.unload(module_path)
|
|
81
|
-
self._caching.invalidate(module_path)
|
|
82
|
-
|
|
83
|
-
# Also remove from sys.modules if present
|
|
84
|
-
if module_path in sys.modules:
|
|
85
|
-
del sys.modules[module_path]
|
|
86
|
-
|
|
87
|
-
def install_hook(self) -> None:
|
|
88
|
-
"""Install import hook into sys.meta_path."""
|
|
89
|
-
importer_engine.install_import_hook(self._package_name)
|
|
90
|
-
|
|
91
|
-
def uninstall_hook(self) -> None:
|
|
92
|
-
"""Uninstall import hook from sys.meta_path."""
|
|
93
|
-
importer_engine.uninstall_import_hook(self._package_name)
|
|
94
|
-
|
|
95
|
-
def handle_import_error(self, module_name: str) -> Optional[ModuleType]:
|
|
96
|
-
"""
|
|
97
|
-
Handle ImportError by attempting to install and re-import.
|
|
98
|
-
|
|
99
|
-
Args:
|
|
100
|
-
module_name: Name of module that failed to import
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
Imported module if successful, None otherwise
|
|
104
|
-
"""
|
|
105
|
-
if self._import_hook is None:
|
|
106
|
-
self._import_hook = importer_engine.LazyImportHook(
|
|
107
|
-
self._package_name,
|
|
108
|
-
self._package_helper
|
|
109
|
-
)
|
|
110
|
-
return self._import_hook.handle_import_error(module_name)
|
|
111
|
-
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Simple Module Manager Strategy - Basic operations.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Simple manager - basic module loading/unloading only.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from types import ModuleType
|
|
14
|
-
from typing import Optional
|
|
15
|
-
from ...module.base import AModuleManagerStrategy
|
|
16
|
-
from ...contracts import ICachingStrategy, IModuleHelperStrategy
|
|
17
|
-
|
|
18
|
-
class SimpleManager(AModuleManagerStrategy):
|
|
19
|
-
"""
|
|
20
|
-
Simple manager - basic module operations only.
|
|
21
|
-
|
|
22
|
-
No hooks, no error handling, just load/unload.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def __init__(
|
|
26
|
-
self,
|
|
27
|
-
package_name: str,
|
|
28
|
-
caching: ICachingStrategy,
|
|
29
|
-
helper: IModuleHelperStrategy
|
|
30
|
-
):
|
|
31
|
-
"""
|
|
32
|
-
Initialize simple manager.
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
package_name: Package name for isolation
|
|
36
|
-
caching: Caching strategy
|
|
37
|
-
helper: Helper strategy
|
|
38
|
-
"""
|
|
39
|
-
self._package_name = package_name
|
|
40
|
-
self._caching = caching
|
|
41
|
-
self._helper = helper
|
|
42
|
-
|
|
43
|
-
def load_module(self, module_path: str) -> ModuleType:
|
|
44
|
-
"""
|
|
45
|
-
Load a module.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
module_path: Module path to load
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
Loaded module
|
|
52
|
-
"""
|
|
53
|
-
# Check cache first
|
|
54
|
-
cached = self._caching.get(module_path)
|
|
55
|
-
if cached is not None:
|
|
56
|
-
return cached
|
|
57
|
-
|
|
58
|
-
# Load using helper
|
|
59
|
-
module = self._helper.load(module_path, None)
|
|
60
|
-
|
|
61
|
-
# Cache it
|
|
62
|
-
self._caching.set(module_path, module)
|
|
63
|
-
|
|
64
|
-
return module
|
|
65
|
-
|
|
66
|
-
def unload_module(self, module_path: str) -> None:
|
|
67
|
-
"""
|
|
68
|
-
Unload a module.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
module_path: Module path to unload
|
|
72
|
-
"""
|
|
73
|
-
self._helper.unload(module_path)
|
|
74
|
-
self._caching.invalidate(module_path)
|
|
75
|
-
|
|
76
|
-
def install_hook(self) -> None:
|
|
77
|
-
"""Install import hook (not supported in simple mode)."""
|
|
78
|
-
pass
|
|
79
|
-
|
|
80
|
-
def uninstall_hook(self) -> None:
|
|
81
|
-
"""Uninstall import hook (not supported in simple mode)."""
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
def handle_import_error(self, module_name: str) -> Optional[ModuleType]:
|
|
85
|
-
"""
|
|
86
|
-
Handle import error (not supported in simple mode).
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
module_name: Module name that failed
|
|
90
|
-
|
|
91
|
-
Returns:
|
|
92
|
-
None (simple mode doesn't handle errors)
|
|
93
|
-
"""
|
|
94
|
-
return None
|
|
95
|
-
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Package Operations Module
|
|
3
|
-
|
|
4
|
-
This module provides concrete implementations for package operations.
|
|
5
|
-
Main facade: XWPackageHelper extends APackageHelper
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
# Lazy imports to avoid circular dependencies
|
|
9
|
-
def __getattr__(name: str):
|
|
10
|
-
if name == 'XWPackageHelper':
|
|
11
|
-
from .facade import XWPackageHelper
|
|
12
|
-
return XWPackageHelper
|
|
13
|
-
if name == 'XWPackage': # Backward compatibility alias
|
|
14
|
-
from .facade import XWPackageHelper
|
|
15
|
-
return XWPackageHelper
|
|
16
|
-
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
17
|
-
|
|
18
|
-
__all__ = ['XWPackageHelper', 'XWPackage'] # XWPackage is backward compatibility alias
|