exonware-xwlazy 0.1.0.11__py3-none-any.whl → 0.1.0.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. exonware/__init__.py +22 -0
  2. exonware/xwlazy/__init__.py +0 -0
  3. exonware/xwlazy/common/__init__.py +47 -0
  4. exonware/xwlazy/common/base.py +58 -0
  5. exonware/xwlazy/common/cache.py +506 -0
  6. exonware/xwlazy/common/logger.py +268 -0
  7. exonware/xwlazy/common/services/__init__.py +72 -0
  8. exonware/xwlazy/common/services/dependency_mapper.py +234 -0
  9. exonware/xwlazy/common/services/install_async_utils.py +169 -0
  10. exonware/xwlazy/common/services/install_cache_utils.py +257 -0
  11. exonware/xwlazy/common/services/keyword_detection.py +292 -0
  12. exonware/xwlazy/common/services/spec_cache.py +173 -0
  13. exonware/xwlazy/common/strategies/__init__.py +28 -0
  14. exonware/xwlazy/common/strategies/caching_dict.py +45 -0
  15. exonware/xwlazy/common/strategies/caching_installation.py +89 -0
  16. exonware/xwlazy/common/strategies/caching_lfu.py +67 -0
  17. exonware/xwlazy/common/strategies/caching_lru.py +64 -0
  18. exonware/xwlazy/common/strategies/caching_multitier.py +60 -0
  19. exonware/xwlazy/common/strategies/caching_ttl.py +60 -0
  20. {xwlazy/lazy → exonware/xwlazy}/config.py +52 -20
  21. exonware/xwlazy/contracts.py +1410 -0
  22. exonware/xwlazy/defs.py +397 -0
  23. xwlazy/lazy/lazy_errors.py → exonware/xwlazy/errors.py +21 -8
  24. exonware/xwlazy/facade.py +1049 -0
  25. exonware/xwlazy/module/__init__.py +18 -0
  26. exonware/xwlazy/module/base.py +569 -0
  27. exonware/xwlazy/module/data.py +17 -0
  28. exonware/xwlazy/module/facade.py +247 -0
  29. exonware/xwlazy/module/importer_engine.py +2161 -0
  30. exonware/xwlazy/module/strategies/__init__.py +22 -0
  31. exonware/xwlazy/module/strategies/module_helper_lazy.py +94 -0
  32. exonware/xwlazy/module/strategies/module_helper_simple.py +66 -0
  33. exonware/xwlazy/module/strategies/module_manager_advanced.py +112 -0
  34. exonware/xwlazy/module/strategies/module_manager_simple.py +96 -0
  35. exonware/xwlazy/package/__init__.py +18 -0
  36. exonware/xwlazy/package/base.py +807 -0
  37. xwlazy/lazy/host_conf.py → exonware/xwlazy/package/conf.py +62 -10
  38. exonware/xwlazy/package/data.py +17 -0
  39. exonware/xwlazy/package/facade.py +481 -0
  40. exonware/xwlazy/package/services/__init__.py +84 -0
  41. exonware/xwlazy/package/services/async_install_handle.py +89 -0
  42. exonware/xwlazy/package/services/config_manager.py +246 -0
  43. exonware/xwlazy/package/services/discovery.py +374 -0
  44. {xwlazy/lazy → exonware/xwlazy/package/services}/host_packages.py +43 -16
  45. exonware/xwlazy/package/services/install_async.py +278 -0
  46. exonware/xwlazy/package/services/install_cache.py +146 -0
  47. exonware/xwlazy/package/services/install_interactive.py +60 -0
  48. exonware/xwlazy/package/services/install_policy.py +158 -0
  49. exonware/xwlazy/package/services/install_registry.py +56 -0
  50. exonware/xwlazy/package/services/install_result.py +17 -0
  51. exonware/xwlazy/package/services/install_sbom.py +154 -0
  52. exonware/xwlazy/package/services/install_utils.py +83 -0
  53. exonware/xwlazy/package/services/installer_engine.py +408 -0
  54. exonware/xwlazy/package/services/lazy_installer.py +720 -0
  55. {xwlazy/lazy → exonware/xwlazy/package/services}/manifest.py +42 -25
  56. exonware/xwlazy/package/services/strategy_registry.py +188 -0
  57. exonware/xwlazy/package/strategies/__init__.py +57 -0
  58. exonware/xwlazy/package/strategies/package_discovery_file.py +130 -0
  59. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +85 -0
  60. exonware/xwlazy/package/strategies/package_discovery_manifest.py +102 -0
  61. exonware/xwlazy/package/strategies/package_execution_async.py +114 -0
  62. exonware/xwlazy/package/strategies/package_execution_cached.py +91 -0
  63. exonware/xwlazy/package/strategies/package_execution_pip.py +100 -0
  64. exonware/xwlazy/package/strategies/package_execution_wheel.py +107 -0
  65. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +101 -0
  66. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +106 -0
  67. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +101 -0
  68. exonware/xwlazy/package/strategies/package_policy_allow_list.py +58 -0
  69. exonware/xwlazy/package/strategies/package_policy_deny_list.py +58 -0
  70. exonware/xwlazy/package/strategies/package_policy_permissive.py +47 -0
  71. exonware/xwlazy/package/strategies/package_timing_clean.py +68 -0
  72. exonware/xwlazy/package/strategies/package_timing_full.py +67 -0
  73. exonware/xwlazy/package/strategies/package_timing_smart.py +69 -0
  74. exonware/xwlazy/package/strategies/package_timing_temporary.py +67 -0
  75. exonware/xwlazy/runtime/__init__.py +18 -0
  76. exonware/xwlazy/runtime/adaptive_learner.py +131 -0
  77. exonware/xwlazy/runtime/base.py +276 -0
  78. exonware/xwlazy/runtime/facade.py +95 -0
  79. exonware/xwlazy/runtime/intelligent_selector.py +173 -0
  80. exonware/xwlazy/runtime/metrics.py +64 -0
  81. exonware/xwlazy/runtime/performance.py +39 -0
  82. exonware/xwlazy/version.py +2 -2
  83. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/METADATA +86 -10
  84. exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
  85. exonware_xwlazy-0.1.0.11.dist-info/RECORD +0 -20
  86. xwlazy/__init__.py +0 -34
  87. xwlazy/lazy/__init__.py +0 -301
  88. xwlazy/lazy/bootstrap.py +0 -106
  89. xwlazy/lazy/lazy_base.py +0 -465
  90. xwlazy/lazy/lazy_contracts.py +0 -290
  91. xwlazy/lazy/lazy_core.py +0 -3727
  92. xwlazy/lazy/logging_utils.py +0 -194
  93. xwlazy/version.py +0 -77
  94. /xwlazy/lazy/lazy_state.py → /exonware/xwlazy/common/services/state_manager.py +0 -0
  95. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
  96. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,18 @@
1
+ """
2
+ Module Operations Module
3
+
4
+ This module provides concrete implementations for module operations.
5
+ Main facade: XWModuleHelper extends AModuleHelper
6
+ """
7
+
8
+ # Lazy imports to avoid circular dependencies
9
+ def __getattr__(name: str):
10
+ if name == 'XWModuleHelper':
11
+ from .facade import XWModuleHelper
12
+ return XWModuleHelper
13
+ if name == 'XWModule': # Backward compatibility alias
14
+ from .facade import XWModuleHelper
15
+ return XWModuleHelper
16
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
17
+
18
+ __all__ = ['XWModuleHelper', 'XWModule'] # XWModule is backward compatibility alias
@@ -0,0 +1,569 @@
1
+ """
2
+ #exonware/xwlazy/src/exonware/xwlazy/module/base.py
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 10-Oct-2025
9
+
10
+ Abstract Base Class for Module Operations
11
+
12
+ This module defines the abstract base class for module operations.
13
+ """
14
+
15
+ import sys
16
+ import threading
17
+ from abc import ABC, abstractmethod
18
+ from typing import Dict, List, Optional, Any, Tuple
19
+ from types import ModuleType
20
+
21
+ from ..contracts import (
22
+ IModuleHelper,
23
+ IModuleHelperStrategy,
24
+ IModuleManagerStrategy,
25
+ )
26
+ from ..package.base import APackageHelper
27
+
28
+
29
+ # =============================================================================
30
+ # ABSTRACT MODULE (Unified - Merges AImportHook + ALazyLoader + AModuleHelper)
31
+ # =============================================================================
32
+
33
+ class AModuleHelper(IModuleHelper, ABC):
34
+ """
35
+ Unified abstract base for module operations.
36
+
37
+ Merges functionality from AImportHook, ALazyLoader, and AModuleHelper.
38
+ Provides comprehensive module operations: installation, hooks, finding, interception, loading, importing, registry, and bytecode caching.
39
+
40
+ This abstract class combines:
41
+ - Module installation (installing and importing modules)
42
+ - Import hooks (intercepting import failures)
43
+ - Meta path finding (sys.meta_path hook for lazy installation)
44
+ - Import interception (high-level import interception)
45
+ - Lazy loading (deferred module loading)
46
+ - Lazy importing (lazy module loading with strategies)
47
+ - Watched registry (tracking watched module prefixes)
48
+ - Bytecode caching (caching compiled Python bytecode)
49
+ """
50
+
51
+ __slots__ = (
52
+ # From AImportHook
53
+ '_package_name', '_enabled',
54
+ # From ALazyLoader
55
+ '_module_path', '_cached_module', '_loading',
56
+ # From AModuleHelper
57
+ '_package_helper', '_dependency_mapper',
58
+ # Common
59
+ '_lock'
60
+ )
61
+
62
+ def __init__(self, package_name: str = 'default', package_helper: Optional[APackageHelper] = None):
63
+ """
64
+ Initialize unified module operations.
65
+
66
+ Args:
67
+ package_name: Package this instance is for
68
+ package_helper: Optional package helper instance. If None, creates default.
69
+ """
70
+ # From AImportHook
71
+ self._package_name = package_name
72
+ self._enabled = True
73
+
74
+ # From ALazyLoader
75
+ self._module_path: Optional[str] = None
76
+ self._cached_module: Optional[ModuleType] = None
77
+ self._loading = False
78
+
79
+ # From AModuleHelper
80
+ self._package_helper = package_helper
81
+ self._dependency_mapper = None # Lazy init to avoid circular imports
82
+
83
+ # Common
84
+ self._lock = threading.RLock()
85
+
86
+ # ========================================================================
87
+ # Import Hook Methods (from AImportHook)
88
+ # ========================================================================
89
+
90
+ def enable(self) -> None:
91
+ """Enable the import hook."""
92
+ self._enabled = True
93
+
94
+ def disable(self) -> None:
95
+ """Disable the import hook."""
96
+ self._enabled = False
97
+
98
+ def is_enabled(self) -> bool:
99
+ """Check if hook is enabled."""
100
+ return self._enabled
101
+
102
+ @abstractmethod
103
+ def install_hook(self) -> None:
104
+ """Install the import hook into sys.meta_path (abstract method)."""
105
+ pass
106
+
107
+ @abstractmethod
108
+ def uninstall_hook(self) -> None:
109
+ """Uninstall the import hook from sys.meta_path (abstract method)."""
110
+ pass
111
+
112
+ @abstractmethod
113
+ def handle_import_error(self, module_name: str) -> Optional[Any]:
114
+ """
115
+ Handle ImportError by attempting to install and re-import (abstract method).
116
+
117
+ Args:
118
+ module_name: Name of module that failed to import
119
+
120
+ Returns:
121
+ Imported module if successful, None otherwise
122
+ """
123
+ pass
124
+
125
+ # ========================================================================
126
+ # Lazy Loader Methods (from ALazyLoader)
127
+ # ========================================================================
128
+
129
+ @abstractmethod
130
+ def load_module(self, module_path: str) -> ModuleType:
131
+ """
132
+ Load a module lazily (abstract method).
133
+
134
+ Args:
135
+ module_path: Full module path to load
136
+
137
+ Returns:
138
+ Loaded module
139
+ """
140
+ pass
141
+
142
+ def is_loaded(self, module_path: str = None) -> bool:
143
+ """
144
+ Check if module is already loaded.
145
+
146
+ Args:
147
+ module_path: Module path to check (uses self._module_path if None)
148
+
149
+ Returns:
150
+ True if loaded, False otherwise
151
+ """
152
+ return self._cached_module is not None
153
+
154
+ @abstractmethod
155
+ def unload_module(self, module_path: str) -> None:
156
+ """
157
+ Unload a module from cache (abstract method).
158
+
159
+ Args:
160
+ module_path: Module path to unload
161
+ """
162
+ pass
163
+
164
+ # ========================================================================
165
+ # Module Helper Methods (from AModuleHelper)
166
+ # ========================================================================
167
+
168
+ def _get_package_helper(self) -> APackageHelper:
169
+ """Get package helper instance (creates if needed)."""
170
+ if self._package_helper is None:
171
+ self._package_helper = self._create_package_helper()
172
+ return self._package_helper
173
+
174
+ def _get_dependency_mapper(self):
175
+ """Get dependency mapper instance (lazy init)."""
176
+ if self._dependency_mapper is None:
177
+ from ...common.services.dependency_mapper import DependencyMapper
178
+ self._dependency_mapper = DependencyMapper()
179
+ return self._dependency_mapper
180
+
181
+ def to_package(self, module_name: str) -> Optional[str]:
182
+ """
183
+ Map module name to package name.
184
+
185
+ Args:
186
+ module_name: Module name (e.g., 'bson', 'msgpack')
187
+
188
+ Returns:
189
+ Package name (e.g., 'pymongo', 'msgpack') or None if not found
190
+ """
191
+ mapper = self._get_dependency_mapper()
192
+ return mapper.get_package_name(module_name)
193
+
194
+ def installed(self, module_name: str) -> bool:
195
+ """
196
+ Check if a module is installed.
197
+
198
+ Uses cache first to avoid expensive operations.
199
+ Maps module to package and checks if package is installed.
200
+
201
+ Args:
202
+ module_name: Module name to check (e.g., 'bson')
203
+
204
+ Returns:
205
+ True if module is installed, False otherwise
206
+ """
207
+ # Map module name to package name
208
+ package_name = self.to_package(module_name)
209
+ if package_name:
210
+ # Check if the package is installed
211
+ return self._get_package_helper().installed(package_name)
212
+
213
+ # If no mapping found, check if the module itself is importable (abstract method)
214
+ return self._check_module_importability(module_name)
215
+
216
+ def uninstalled(self, module_name: str) -> bool:
217
+ """
218
+ Check if a module is uninstalled.
219
+
220
+ Uses cache first to avoid expensive operations.
221
+ Maps module to package and checks if package is uninstalled.
222
+
223
+ Args:
224
+ module_name: Module name to check (e.g., 'bson')
225
+
226
+ Returns:
227
+ True if module is uninstalled, False otherwise
228
+ """
229
+ return not self.installed(module_name)
230
+
231
+ def install(self, *module_names: str) -> None:
232
+ """
233
+ Install one or more modules by mapping to packages first.
234
+
235
+ First deduplicates modules, then maps to packages, then installs packages.
236
+ Skips modules that are already installed (using cache).
237
+
238
+ Args:
239
+ *module_names: One or more module names to install (e.g., 'bson', 'msgpack')
240
+
241
+ Raises:
242
+ subprocess.CalledProcessError: If installation fails
243
+ """
244
+ if not module_names:
245
+ return
246
+
247
+ # First deduplicate modules (preserves order)
248
+ unique_modules = list(dict.fromkeys(module_names))
249
+
250
+ # Map all module names to package names
251
+ package_names = []
252
+ for name in unique_modules:
253
+ package_name = self.to_package(name)
254
+ if package_name:
255
+ package_names.append(package_name)
256
+ else:
257
+ # If no mapping found, use the name as-is
258
+ package_names.append(name)
259
+
260
+ # Install the packages (package_helper handles deduplication and caching)
261
+ self._get_package_helper().install(*package_names)
262
+
263
+ def uninstall(self, *module_names: str) -> None:
264
+ """
265
+ Uninstall one or more modules by mapping to packages first.
266
+
267
+ First deduplicates modules, then maps to packages, then uninstalls packages.
268
+ Skips modules that are already uninstalled (using cache).
269
+
270
+ Args:
271
+ *module_names: One or more module names to uninstall (e.g., 'bson', 'msgpack')
272
+
273
+ Raises:
274
+ subprocess.CalledProcessError: If uninstallation fails
275
+ """
276
+ if not module_names:
277
+ return
278
+
279
+ # First deduplicate modules (preserves order)
280
+ unique_modules = list(dict.fromkeys(module_names))
281
+
282
+ # Map all module names to package names
283
+ package_names = []
284
+ for name in unique_modules:
285
+ package_name = self.to_package(name)
286
+ if package_name:
287
+ package_names.append(package_name)
288
+ else:
289
+ # If no mapping found, use the name as-is
290
+ package_names.append(name)
291
+
292
+ # Uninstall the packages (package_helper handles deduplication and caching)
293
+ self._get_package_helper().uninstall(*package_names)
294
+
295
+ def load(self, *module_names: str) -> List[ModuleType]:
296
+ """
297
+ Load one or more modules into memory.
298
+
299
+ Imports modules and returns them. Uses lazy loading if enabled.
300
+
301
+ Args:
302
+ *module_names: One or more module names to load (e.g., 'bson', 'msgpack')
303
+
304
+ Returns:
305
+ List of loaded module objects
306
+
307
+ Raises:
308
+ ImportError: If module cannot be loaded
309
+ """
310
+ loaded_modules = []
311
+ for name in module_names:
312
+ try:
313
+ module = self._import_module(name)
314
+ loaded_modules.append(module)
315
+ except ImportError as e:
316
+ # Try to install if not found
317
+ if not self.installed(name):
318
+ self.install(name)
319
+ # Try importing again
320
+ module = self._import_module(name)
321
+ loaded_modules.append(module)
322
+ else:
323
+ raise
324
+ return loaded_modules
325
+
326
+ def unload(self, *module_names: str) -> None:
327
+ """
328
+ Unload one or more modules from memory.
329
+
330
+ Removes modules from sys.modules and clears caches.
331
+ Useful for freeing memory or forcing reload.
332
+
333
+ Args:
334
+ *module_names: One or more module names to unload (e.g., 'bson', 'msgpack')
335
+ """
336
+ with self._lock:
337
+ for name in module_names:
338
+ # Remove from sys.modules
339
+ if name in sys.modules:
340
+ del sys.modules[name]
341
+
342
+ # Remove submodules too (e.g., 'bson.codec' if 'bson' is unloaded)
343
+ to_remove = [mod for mod in sys.modules.keys() if mod.startswith(name + '.')]
344
+ for mod in to_remove:
345
+ del sys.modules[mod]
346
+
347
+ # Clear import caches (abstract method)
348
+ self._invalidate_import_caches()
349
+
350
+ @abstractmethod
351
+ def _check_module_importability(self, module_name: str) -> bool:
352
+ """
353
+ Check if module is importable (abstract method).
354
+
355
+ Concrete implementations should use importlib.util.find_spec or similar.
356
+
357
+ Args:
358
+ module_name: Module name to check
359
+
360
+ Returns:
361
+ True if importable, False otherwise
362
+ """
363
+ pass
364
+
365
+ @abstractmethod
366
+ def _import_module(self, module_name: str) -> ModuleType:
367
+ """
368
+ Import a module (abstract method).
369
+
370
+ Concrete implementations should use importlib.import_module.
371
+
372
+ Args:
373
+ module_name: Module name to import
374
+
375
+ Returns:
376
+ Imported module
377
+
378
+ Raises:
379
+ ImportError: If module cannot be imported
380
+ """
381
+ pass
382
+
383
+ @abstractmethod
384
+ def _invalidate_import_caches(self) -> None:
385
+ """
386
+ Invalidate import caches (abstract method).
387
+
388
+ Concrete implementations should use importlib.invalidate_caches().
389
+ """
390
+ pass
391
+
392
+ @abstractmethod
393
+ def _create_package_helper(self) -> APackageHelper:
394
+ """
395
+ Create a package helper instance (abstract method).
396
+
397
+ Returns:
398
+ APackageHelper instance
399
+ """
400
+ pass
401
+
402
+ # ========================================================================
403
+ # IModuleHelper Interface Methods (stubs - to be implemented by subclasses)
404
+ # ========================================================================
405
+
406
+ # Note: Many methods from IModuleHelper are already implemented above.
407
+ # The following are stubs that need concrete implementations:
408
+
409
+ def install_and_import(self, module_name: str, package_name: Optional[str] = None) -> Tuple[Optional[ModuleType], bool]:
410
+ """Install package and import module (from IModuleInstaller)."""
411
+ raise NotImplementedError("Subclasses must implement install_and_import")
412
+
413
+ def is_package_installed(self, package_name: str) -> bool:
414
+ """Check if package is installed (from IModuleInstaller)."""
415
+ raise NotImplementedError("Subclasses must implement is_package_installed")
416
+
417
+ def mark_installed(self, package_name: str, version: Optional[str] = None) -> None:
418
+ """Mark package as installed in persistent cache (from IModuleInstaller)."""
419
+ raise NotImplementedError("Subclasses must implement mark_installed")
420
+
421
+ def is_hook_installed(self) -> bool:
422
+ """Check if hook is installed (from IImportHook)."""
423
+ raise NotImplementedError("Subclasses must implement is_hook_installed")
424
+
425
+ def find_spec(self, fullname: str, path: Optional[str] = None, target=None) -> Optional[Any]:
426
+ """Find module spec (from IMetaPathFinder)."""
427
+ raise NotImplementedError("Subclasses must implement find_spec")
428
+
429
+ def should_intercept(self, fullname: str) -> bool:
430
+ """Determine if a module should be intercepted (from IMetaPathFinder)."""
431
+ raise NotImplementedError("Subclasses must implement should_intercept")
432
+
433
+ def is_module_installed(self, fullname: str) -> bool:
434
+ """Check if module is already installed (from IMetaPathFinder)."""
435
+ raise NotImplementedError("Subclasses must implement is_module_installed")
436
+
437
+ def intercept_missing_import(self, module_name: str) -> Optional[ModuleType]:
438
+ """Intercept a missing import (from IImportInterceptor)."""
439
+ raise NotImplementedError("Subclasses must implement intercept_missing_import")
440
+
441
+ def should_intercept_module(self, module_name: str) -> bool:
442
+ """Determine if a module should be intercepted (from IImportInterceptor)."""
443
+ raise NotImplementedError("Subclasses must implement should_intercept_module")
444
+
445
+ def prevent_recursion(self, module_name: str) -> bool:
446
+ """Check if we should prevent recursion (from IImportInterceptor)."""
447
+ raise NotImplementedError("Subclasses must implement prevent_recursion")
448
+
449
+ def import_module(self, module_name: str, package_name: Optional[str] = None) -> Any:
450
+ """Import a module with lazy loading (from ILazyImporter)."""
451
+ raise NotImplementedError("Subclasses must implement import_module")
452
+
453
+ def enable_lazy_loading(self, load_mode: Any) -> None:
454
+ """Enable lazy loading with a mode (from ILazyImporter)."""
455
+ raise NotImplementedError("Subclasses must implement enable_lazy_loading")
456
+
457
+ def disable_lazy_loading(self) -> None:
458
+ """Disable lazy loading (from ILazyImporter)."""
459
+ raise NotImplementedError("Subclasses must implement disable_lazy_loading")
460
+
461
+ def is_lazy_loading_enabled(self) -> bool:
462
+ """Check if lazy loading is enabled (from ILazyImporter)."""
463
+ raise NotImplementedError("Subclasses must implement is_lazy_loading_enabled")
464
+
465
+ def has_root(self, root_name: str) -> bool:
466
+ """Check if a root module name is being watched (from IWatchedRegistry)."""
467
+ raise NotImplementedError("Subclasses must implement has_root")
468
+
469
+ def get_matching_prefixes(self, fullname: str) -> Tuple[str, ...]:
470
+ """Get all watched prefixes that match a module name (from IWatchedRegistry)."""
471
+ raise NotImplementedError("Subclasses must implement get_matching_prefixes")
472
+
473
+ def is_prefix_owned_by(self, prefix: str, package_name: str) -> bool:
474
+ """Check if a prefix is owned by a package (from IWatchedRegistry)."""
475
+ raise NotImplementedError("Subclasses must implement is_prefix_owned_by")
476
+
477
+ def is_watched_registry_empty(self) -> bool:
478
+ """Check if registry is empty (from IWatchedRegistry)."""
479
+ raise NotImplementedError("Subclasses must implement is_watched_registry_empty")
480
+
481
+ def get_bytecode(self, module_path: str, source_code: str) -> Optional[bytes]:
482
+ """Get cached bytecode for module (from IBytecodeCache)."""
483
+ raise NotImplementedError("Subclasses must implement get_bytecode")
484
+
485
+ def cache_bytecode(self, module_path: str, source_code: str, bytecode: bytes) -> None:
486
+ """Cache bytecode for module (from IBytecodeCache)."""
487
+ raise NotImplementedError("Subclasses must implement cache_bytecode")
488
+
489
+ def clear_bytecode_cache(self) -> None:
490
+ """Clear bytecode cache (from IBytecodeCache)."""
491
+ raise NotImplementedError("Subclasses must implement clear_bytecode_cache")
492
+
493
+
494
+ # =============================================================================
495
+ # ABSTRACT MODULE HELPER STRATEGY
496
+ # =============================================================================
497
+
498
+ class AModuleHelperStrategy(IModuleHelperStrategy, ABC):
499
+ """
500
+ Abstract base class for module helper strategies.
501
+
502
+ Operations on a single module (loading, unloading, checking).
503
+ All module helper strategies must extend this class.
504
+ """
505
+
506
+ @abstractmethod
507
+ def load(self, module_path: str, package_helper: Any) -> ModuleType:
508
+ """Load the module."""
509
+ ...
510
+
511
+ @abstractmethod
512
+ def unload(self, module_path: str) -> None:
513
+ """Unload the module."""
514
+ ...
515
+
516
+ @abstractmethod
517
+ def check_importability(self, path: str) -> bool:
518
+ """Check if module is importable."""
519
+ ...
520
+
521
+
522
+ # =============================================================================
523
+ # ABSTRACT MODULE MANAGER STRATEGY
524
+ # =============================================================================
525
+
526
+ class AModuleManagerStrategy(IModuleManagerStrategy, ABC):
527
+ """
528
+ Abstract base class for module manager strategies.
529
+
530
+ Orchestrates multiple modules (loading, hooks, error handling).
531
+ All module manager strategies must extend this class.
532
+ """
533
+
534
+ @abstractmethod
535
+ def load_module(self, module_path: str) -> ModuleType:
536
+ """Load a module."""
537
+ ...
538
+
539
+ @abstractmethod
540
+ def unload_module(self, module_path: str) -> None:
541
+ """Unload a module."""
542
+ ...
543
+
544
+ @abstractmethod
545
+ def install_hook(self) -> None:
546
+ """Install import hook."""
547
+ ...
548
+
549
+ @abstractmethod
550
+ def uninstall_hook(self) -> None:
551
+ """Uninstall import hook."""
552
+ ...
553
+
554
+ @abstractmethod
555
+ def handle_import_error(self, module_name: str) -> Optional[ModuleType]:
556
+ """Handle import error."""
557
+ ...
558
+
559
+
560
+ # =============================================================================
561
+ # EXPORT ALL
562
+ # =============================================================================
563
+
564
+ __all__ = [
565
+ 'AModuleHelper',
566
+ 'AModuleHelperStrategy',
567
+ 'AModuleManagerStrategy',
568
+ ]
569
+
@@ -0,0 +1,17 @@
1
+ """
2
+ Module Data - Immutable data structure for modules.
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Re-export ModuleData from defs.py for backward compatibility.
11
+ """
12
+
13
+ # Re-export from defs.py
14
+ from ..defs import ModuleData
15
+
16
+ __all__ = ['ModuleData']
17
+