exonware-xwlazy 0.1.0.21__py3-none-any.whl → 0.1.0.23__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 (71) hide show
  1. exonware/__init__.py +22 -6
  2. exonware/xwlazy/__init__.py +14 -2
  3. exonware/xwlazy/common/__init__.py +8 -0
  4. exonware/xwlazy/common/base.py +11 -2
  5. exonware/xwlazy/common/cache.py +5 -5
  6. exonware/xwlazy/common/logger.py +5 -5
  7. exonware/xwlazy/common/services/dependency_mapper.py +31 -13
  8. exonware/xwlazy/common/services/install_async_utils.py +5 -0
  9. exonware/xwlazy/common/services/install_cache_utils.py +4 -4
  10. exonware/xwlazy/common/services/spec_cache.py +2 -2
  11. exonware/xwlazy/common/services/state_manager.py +4 -4
  12. exonware/xwlazy/common/strategies/caching_dict.py +2 -2
  13. exonware/xwlazy/common/strategies/caching_lfu.py +2 -2
  14. exonware/xwlazy/common/strategies/caching_ttl.py +2 -2
  15. exonware/xwlazy/common/utils.py +142 -0
  16. exonware/xwlazy/config.py +1 -1
  17. exonware/xwlazy/contracts.py +162 -25
  18. exonware/xwlazy/defs.py +15 -15
  19. exonware/xwlazy/facade.py +175 -29
  20. exonware/xwlazy/host/__init__.py +8 -0
  21. exonware/xwlazy/host/conf.py +16 -0
  22. exonware/xwlazy/module/base.py +61 -4
  23. exonware/xwlazy/module/facade.py +1 -1
  24. exonware/xwlazy/module/importer_engine.py +1017 -170
  25. exonware/xwlazy/module/partial_module_detector.py +275 -0
  26. exonware/xwlazy/module/strategies/module_helper_lazy.py +3 -3
  27. exonware/xwlazy/package/base.py +106 -41
  28. exonware/xwlazy/package/conf.py +6 -6
  29. exonware/xwlazy/package/services/config_manager.py +20 -16
  30. exonware/xwlazy/package/services/discovery.py +81 -16
  31. exonware/xwlazy/package/services/host_packages.py +41 -6
  32. exonware/xwlazy/package/services/install_async.py +16 -2
  33. exonware/xwlazy/package/services/install_cache.py +4 -4
  34. exonware/xwlazy/package/services/install_policy.py +14 -14
  35. exonware/xwlazy/package/services/install_registry.py +3 -3
  36. exonware/xwlazy/package/services/install_sbom.py +1 -1
  37. exonware/xwlazy/package/services/installer_engine.py +3 -3
  38. exonware/xwlazy/package/services/lazy_installer.py +102 -17
  39. exonware/xwlazy/package/services/manifest.py +43 -36
  40. exonware/xwlazy/package/services/strategy_registry.py +150 -12
  41. exonware/xwlazy/package/strategies/package_discovery_file.py +2 -2
  42. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +2 -2
  43. exonware/xwlazy/package/strategies/package_discovery_manifest.py +2 -2
  44. exonware/xwlazy/package/strategies/package_execution_async.py +3 -3
  45. exonware/xwlazy/package/strategies/package_execution_cached.py +2 -2
  46. exonware/xwlazy/package/strategies/package_execution_pip.py +2 -2
  47. exonware/xwlazy/package/strategies/package_execution_wheel.py +2 -2
  48. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +2 -2
  49. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +2 -2
  50. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +2 -2
  51. exonware/xwlazy/package/strategies/package_policy_allow_list.py +4 -4
  52. exonware/xwlazy/package/strategies/package_policy_deny_list.py +4 -4
  53. exonware/xwlazy/package/strategies/package_policy_permissive.py +3 -3
  54. exonware/xwlazy/package/strategies/package_timing_clean.py +2 -2
  55. exonware/xwlazy/package/strategies/package_timing_full.py +2 -2
  56. exonware/xwlazy/package/strategies/package_timing_smart.py +2 -2
  57. exonware/xwlazy/package/strategies/package_timing_temporary.py +2 -2
  58. exonware/xwlazy/runtime/adaptive_learner.py +7 -7
  59. exonware/xwlazy/runtime/base.py +14 -14
  60. exonware/xwlazy/runtime/facade.py +7 -7
  61. exonware/xwlazy/runtime/intelligent_selector.py +6 -6
  62. exonware/xwlazy/runtime/metrics.py +6 -6
  63. exonware/xwlazy/runtime/performance.py +5 -5
  64. exonware/xwlazy/version.py +2 -2
  65. {exonware_xwlazy-0.1.0.21.dist-info → exonware_xwlazy-0.1.0.23.dist-info}/METADATA +2 -6
  66. exonware_xwlazy-0.1.0.23.dist-info/RECORD +93 -0
  67. xwlazy/__init__.py +14 -0
  68. xwlazy/lazy.py +30 -0
  69. exonware_xwlazy-0.1.0.21.dist-info/RECORD +0 -87
  70. {exonware_xwlazy-0.1.0.21.dist-info → exonware_xwlazy-0.1.0.23.dist-info}/WHEEL +0 -0
  71. {exonware_xwlazy-0.1.0.21.dist-info → exonware_xwlazy-0.1.0.23.dist-info}/licenses/LICENSE +0 -0
exonware/xwlazy/facade.py CHANGED
@@ -19,11 +19,12 @@ Design Pattern: Facade Pattern
19
19
  - Centralizes public API
20
20
  """
21
21
 
22
+ import os
22
23
  import sys
23
24
  import subprocess
24
25
  import importlib
25
26
  import importlib.util
26
- from typing import Dict, List, Optional, Tuple, Any
27
+ from typing import Optional, Any
27
28
  from types import ModuleType
28
29
 
29
30
  # Import from contracts for types
@@ -70,6 +71,9 @@ from .module.importer_engine import (
70
71
  is_import_hook_installed as _is_import_hook_installed,
71
72
  register_lazy_module_prefix as _register_lazy_module_prefix,
72
73
  register_lazy_module_methods as _register_lazy_module_methods,
74
+ register_lazy_package as _register_lazy_package,
75
+ install_global_import_hook as _install_global_import_hook,
76
+ is_global_import_hook_installed as _is_global_import_hook_installed,
73
77
  LazyImporter,
74
78
  LazyModuleRegistry,
75
79
  )
@@ -94,7 +98,7 @@ class LazyModeFacade:
94
98
  def __init__(self):
95
99
  self._enabled = False
96
100
  self._strategy = "on_demand"
97
- self._configs: Dict[str, Any] = {}
101
+ self._configs: dict[str, Any] = {}
98
102
 
99
103
  def enable(self, strategy: str = "on_demand", **kwargs) -> None:
100
104
  """Enable lazy mode with specified strategy."""
@@ -112,7 +116,7 @@ class LazyModeFacade:
112
116
  """Check if lazy mode is currently enabled."""
113
117
  return self._enabled
114
118
 
115
- def get_stats(self) -> Dict[str, Any]:
119
+ def get_stats(self) -> dict[str, Any]:
116
120
  """Get lazy mode performance statistics."""
117
121
  return {
118
122
  "enabled": self._enabled,
@@ -139,16 +143,17 @@ def is_lazy_mode_enabled() -> bool:
139
143
  """Check if lazy mode is currently enabled."""
140
144
  return _lazy_facade.is_enabled()
141
145
 
142
- def get_lazy_mode_stats() -> Dict[str, Any]:
146
+ def get_lazy_mode_stats() -> dict[str, Any]:
143
147
  """Get lazy mode performance statistics."""
144
148
  return _lazy_facade.get_stats()
145
149
 
146
150
  def configure_lazy_mode(package_name: str, config: LazyModeConfig) -> None:
147
151
  """Configure lazy mode for a specific package."""
148
- LazyInstallConfig.set_mode_config(package_name, config)
152
+ # Use set() method with mode_config parameter
153
+ LazyInstallConfig.set(package_name, True, mode_config=config)
149
154
  logger.info(f"Configured lazy mode for {package_name}")
150
155
 
151
- def preload_modules(package_name: str, modules: List[str]) -> None:
156
+ def preload_modules(package_name: str, modules: list[str]) -> None:
152
157
  """Preload specified modules for a package."""
153
158
  for module_name in modules:
154
159
  _lazy_importer.preload_module(module_name)
@@ -159,6 +164,134 @@ def optimize_lazy_mode(package_name: str) -> None:
159
164
  _lazy_module_registry.preload_frequently_used()
160
165
  logger.info(f"Optimization completed for {package_name}")
161
166
 
167
+ # =============================================================================
168
+ # ONE-LINE ACTIVATION API
169
+ # =============================================================================
170
+
171
+ def auto_enable_lazy(package_name: Optional[str] = None, mode: str = "smart") -> bool:
172
+ """
173
+ Auto-enable lazy mode for a package - ONE LINE ACTIVATION!
174
+
175
+ Usage in any library's __init__.py:
176
+ from exonware.xwlazy import auto_enable_lazy
177
+ auto_enable_lazy(__package__)
178
+
179
+ Args:
180
+ package_name: Package name (auto-detected if None)
181
+ mode: Lazy mode ("smart", "lite", "full", "clean", "temporary")
182
+
183
+ Returns:
184
+ True if enabled, False otherwise
185
+ """
186
+ import inspect
187
+
188
+ # Auto-detect package name from caller
189
+ if package_name is None:
190
+ try:
191
+ frame = inspect.currentframe().f_back
192
+ package_name = (frame.f_globals.get('__package__') or
193
+ frame.f_globals.get('__name__', '').split('.')[0])
194
+ except Exception:
195
+ logger.warning("Could not auto-detect package name")
196
+ return False
197
+
198
+ if not package_name:
199
+ logger.warning("Package name is required")
200
+ return False
201
+
202
+ try:
203
+ # Get preset mode configuration
204
+ config = get_preset_mode(mode)
205
+ if config is None:
206
+ logger.warning(f"Unknown mode: {mode}, using 'smart'")
207
+ config = get_preset_mode("smart")
208
+
209
+ # Register package for lazy loading/installation
210
+ _register_lazy_package(package_name, config)
211
+
212
+ # Enable lazy install for this package with mode config
213
+ # Pass mode_config through set() method (not a separate set_mode_config method)
214
+ LazyInstallConfig.set(
215
+ package_name,
216
+ True,
217
+ mode=mode,
218
+ mode_config=config
219
+ )
220
+
221
+ # Install global import hook if not already installed
222
+ if not _is_global_import_hook_installed():
223
+ _install_global_import_hook()
224
+
225
+ # Also install meta_path hook for compatibility
226
+ _install_import_hook(package_name)
227
+
228
+ logger.info(f"✅ Auto-enabled lazy mode for package: {package_name} (mode: {mode})")
229
+ return True
230
+ except Exception as e:
231
+ logger.error(f"Failed to auto-enable lazy mode for {package_name}: {e}")
232
+ return False
233
+
234
+ def attach(package_name: str, submodules: Optional[list[str]] = None, submod_attrs: Optional[dict[str, list[str]]] = None):
235
+ """
236
+ Attach lazily loaded submodules and attributes (lazy-loader compatible API).
237
+
238
+ Returns (__getattr__, __dir__, __all__) for lazy loading.
239
+
240
+ Usage:
241
+ __getattr__, __dir__, __all__ = lazy.attach(__name__, ['submodule1'], {'module': ['attr1', 'attr2']})
242
+
243
+ Args:
244
+ package_name: Package name (typically __name__)
245
+ submodules: List of submodule names to attach
246
+ submod_attrs: Dict mapping submodule -> list of attributes/functions
247
+
248
+ Returns:
249
+ Tuple of (__getattr__, __dir__, __all__)
250
+ """
251
+ import importlib
252
+
253
+ if submod_attrs is None:
254
+ submod_attrs = {}
255
+ if submodules is None:
256
+ submodules = []
257
+
258
+ submodules_set = set(submodules)
259
+ attr_to_modules = {
260
+ attr: mod for mod, attrs in submod_attrs.items() for attr in attrs
261
+ }
262
+
263
+ __all__ = sorted(submodules_set | attr_to_modules.keys())
264
+
265
+ def __getattr__(name: str) -> Any:
266
+ """Lazy load submodule or attribute on first access."""
267
+ if name in submodules_set:
268
+ return importlib.import_module(f"{package_name}.{name}")
269
+ elif name in attr_to_modules:
270
+ submod_path = f"{package_name}.{attr_to_modules[name]}"
271
+ submod = importlib.import_module(submod_path)
272
+ attr = getattr(submod, name)
273
+
274
+ # If attribute lives in a file with same name as attribute,
275
+ # ensure attribute (not module) is accessible
276
+ if name == attr_to_modules[name]:
277
+ pkg = sys.modules[package_name]
278
+ pkg.__dict__[name] = attr
279
+
280
+ return attr
281
+ else:
282
+ raise AttributeError(f"module {package_name!r} has no attribute {name!r}")
283
+
284
+ def __dir__() -> list[str]:
285
+ """Return list of available attributes."""
286
+ return __all__.copy()
287
+
288
+ # Eager import if EAGER_IMPORT env var is set (for debugging)
289
+ if os.environ.get("EAGER_IMPORT", ""):
290
+ for attr in set(attr_to_modules.keys()) | submodules_set:
291
+ __getattr__(attr)
292
+
293
+ return __getattr__, __dir__, __all__.copy()
294
+
162
295
  # =============================================================================
163
296
  # PUBLIC API FUNCTIONS - Installation
164
297
  # =============================================================================
@@ -195,7 +328,7 @@ def install_missing_package(package_name: str, module_name: str, installer_packa
195
328
  logger.error(f"Failed to install package {package_name} for {installer_package}: {e}")
196
329
  return False
197
330
 
198
- def install_and_import(module_name: str, package_name: str = None, installer_package: str = 'default') -> Tuple[Optional[ModuleType], bool]:
331
+ def install_and_import(module_name: str, package_name: str = None, installer_package: str = 'default') -> tuple[Optional[ModuleType], bool]:
199
332
  """Install package and import module."""
200
333
  try:
201
334
  installer = LazyInstallerRegistry.get_instance(installer_package)
@@ -207,7 +340,7 @@ def install_and_import(module_name: str, package_name: str = None, installer_pac
207
340
  logger.error(f"Failed to install and import {module_name} for {installer_package}: {e}")
208
341
  return None, False
209
342
 
210
- def get_lazy_install_stats(package_name: str) -> Dict[str, Any]:
343
+ def get_lazy_install_stats(package_name: str) -> dict[str, Any]:
211
344
  """Get installation statistics for a package."""
212
345
  try:
213
346
  installer = LazyInstallerRegistry.get_instance(package_name)
@@ -224,7 +357,7 @@ def get_lazy_install_stats(package_name: str) -> Dict[str, Any]:
224
357
  'total_failed': 0,
225
358
  }
226
359
 
227
- def get_all_lazy_install_stats() -> Dict[str, Dict[str, Any]]:
360
+ def get_all_lazy_install_stats() -> dict[str, dict[str, Any]]:
228
361
  """Get installation statistics for all packages."""
229
362
  try:
230
363
  all_instances = LazyInstallerRegistry.get_all_instances()
@@ -233,7 +366,7 @@ def get_all_lazy_install_stats() -> Dict[str, Dict[str, Any]]:
233
366
  logger.error(f"Failed to get all stats: {e}")
234
367
  return {}
235
368
 
236
- def lazy_import_with_install(module_name: str, package_name: str = None, installer_package: str = 'default') -> Tuple[Optional[ModuleType], bool]:
369
+ def lazy_import_with_install(module_name: str, package_name: str = None, installer_package: str = 'default') -> tuple[Optional[ModuleType], bool]:
237
370
  """Lazy import with automatic installation."""
238
371
  try:
239
372
  installer = LazyInstallerRegistry.get_instance(installer_package)
@@ -260,7 +393,7 @@ def install_import_hook(package_name: str = 'default') -> None:
260
393
  """Install performant import hook for automatic lazy installation."""
261
394
  try:
262
395
  _install_import_hook(package_name)
263
- logger.info(f"Import hook installed for {package_name}")
396
+ logger.debug(f"Import hook installed for {package_name}")
264
397
  except Exception as e:
265
398
  logger.error(f"Failed to install import hook for {package_name}: {e}")
266
399
  raise
@@ -269,7 +402,7 @@ def uninstall_import_hook(package_name: str = 'default') -> None:
269
402
  """Uninstall import hook for a package."""
270
403
  try:
271
404
  _uninstall_import_hook(package_name)
272
- logger.info(f"Import hook uninstalled for {package_name}")
405
+ logger.debug(f"Import hook uninstalled for {package_name}")
273
406
  except Exception as e:
274
407
  logger.error(f"Failed to uninstall import hook for {package_name}: {e}")
275
408
  raise
@@ -298,13 +431,11 @@ def enable_lazy_imports(mode: LazyLoadMode = LazyLoadMode.AUTO, package_name: Op
298
431
  """
299
432
  try:
300
433
  _lazy_importer.enable(mode)
301
- # Also patch import_module to be lazy-aware (from archive)
302
- from .module.importer_engine import _patch_import_module
303
- _patch_import_module()
434
+ # Note: _patch_import_module removed - using sys.meta_path hooks instead
304
435
  if package_name:
305
- logger.info(f"Lazy imports enabled for {package_name} with mode {mode}")
436
+ logger.debug(f"Lazy imports enabled for {package_name} with mode {mode}")
306
437
  else:
307
- logger.info(f"Lazy imports enabled with mode {mode}")
438
+ logger.debug(f"Lazy imports enabled with mode {mode}")
308
439
  except Exception as e:
309
440
  if package_name:
310
441
  logger.error(f"Failed to enable lazy imports for {package_name}: {e}")
@@ -418,7 +549,7 @@ def get_lazy_module(module_name: str, package_name: str = None) -> Optional[Modu
418
549
  import sys
419
550
  return sys.modules.get(module_name)
420
551
 
421
- def get_loading_stats(package_name: str) -> Dict[str, Any]:
552
+ def get_loading_stats(package_name: str) -> dict[str, Any]:
422
553
  """Get loading statistics for a package."""
423
554
  try:
424
555
  return _lazy_module_registry.get_stats()
@@ -440,7 +571,7 @@ def preload_frequently_used(package_name: str) -> None:
440
571
  except Exception as e:
441
572
  logger.error(f"Failed to preload frequently used for {package_name}: {e}")
442
573
 
443
- def get_lazy_import_stats(package_name: str) -> Dict[str, Any]:
574
+ def get_lazy_import_stats(package_name: str) -> dict[str, Any]:
444
575
  """Get lazy import statistics for a package."""
445
576
  try:
446
577
  return _lazy_importer.get_stats()
@@ -535,7 +666,18 @@ def config_package_lazy_install_enabled(
535
666
  mode_config=mode_config,
536
667
  )
537
668
 
538
- # Install hook if requested and enabled
669
+ # Register package for global __import__ hook (for module-level imports)
670
+ if enabled:
671
+ try:
672
+ from .module.importer_engine import register_lazy_package, install_global_import_hook
673
+ # Register package for global hook
674
+ register_lazy_package(package_name, mode_config)
675
+ # Install global hook if not already installed
676
+ install_global_import_hook()
677
+ except Exception as global_hook_error:
678
+ logger.warning(f"Failed to register package for global hook {package_name}: {global_hook_error}")
679
+
680
+ # Install meta_path hook if requested and enabled
539
681
  if install_hook and enabled:
540
682
  try:
541
683
  install_import_hook(package_name)
@@ -543,7 +685,7 @@ def config_package_lazy_install_enabled(
543
685
  logger.warning(f"Failed to install import hook for {package_name}: {hook_error}")
544
686
 
545
687
  result = LazyInstallConfig.is_enabled(package_name)
546
- logger.info(f"Configured lazy installation for {package_name}: enabled={result}, mode={mode}")
688
+ logger.debug(f"Configured lazy installation for {package_name}: enabled={result}, mode={mode}")
547
689
  return result
548
690
  except Exception as e:
549
691
  logger.error(f"Failed to configure lazy installation for {package_name}: {e}")
@@ -626,7 +768,7 @@ def sync_manifest_configuration(package_name: str) -> None:
626
768
  if manifest and manifest.class_wrap_prefixes:
627
769
  _set_package_class_hints(package_key, manifest.class_wrap_prefixes)
628
770
 
629
- logger.info(f"Manifest configuration synced for {package_name}")
771
+ logger.debug(f"Manifest configuration synced for {package_name}")
630
772
  except Exception as e:
631
773
  logger.error(f"Failed to sync manifest configuration for {package_name}: {e}")
632
774
  raise
@@ -644,7 +786,7 @@ def refresh_lazy_manifests() -> None:
644
786
  # SECURITY & POLICY FUNCTIONS
645
787
  # =============================================================================
646
788
 
647
- def set_package_allow_list(package_name: str, allowed_packages: List[str]) -> None:
789
+ def set_package_allow_list(package_name: str, allowed_packages: list[str]) -> None:
648
790
  """Set allow list for a package."""
649
791
  try:
650
792
  LazyInstallPolicy.set_allow_list(package_name, allowed_packages)
@@ -653,7 +795,7 @@ def set_package_allow_list(package_name: str, allowed_packages: List[str]) -> No
653
795
  logger.error(f"Failed to set allow list for {package_name}: {e}")
654
796
  raise
655
797
 
656
- def set_package_deny_list(package_name: str, denied_packages: List[str]) -> None:
798
+ def set_package_deny_list(package_name: str, denied_packages: list[str]) -> None:
657
799
  """Set deny list for a package."""
658
800
  try:
659
801
  LazyInstallPolicy.set_deny_list(package_name, denied_packages)
@@ -689,7 +831,7 @@ def set_package_index_url(package_name: str, index_url: str) -> None:
689
831
  logger.error(f"Failed to set index URL for {package_name}: {e}")
690
832
  raise
691
833
 
692
- def set_package_extra_index_urls(package_name: str, extra_index_urls: List[str]) -> None:
834
+ def set_package_extra_index_urls(package_name: str, extra_index_urls: list[str]) -> None:
693
835
  """Set extra index URLs for a package."""
694
836
  try:
695
837
  LazyInstallPolicy.set_extra_index_urls(package_name, extra_index_urls)
@@ -716,7 +858,7 @@ def set_package_lockfile(package_name: str, lockfile_path: str) -> None:
716
858
  logger.error(f"Failed to set lockfile path for {package_name}: {e}")
717
859
  raise
718
860
 
719
- def generate_package_sbom(package_name: str, output_path: Optional[str] = None) -> Dict[str, Any]:
861
+ def generate_package_sbom(package_name: str, output_path: Optional[str] = None) -> dict[str, Any]:
720
862
  """Generate SBOM for a package."""
721
863
  try:
722
864
  installer = LazyInstallerRegistry.get_instance(package_name)
@@ -752,7 +894,7 @@ def register_lazy_module_prefix(prefix: str) -> None:
752
894
  logger.error(f"Failed to register lazy module prefix {prefix}: {e}")
753
895
  raise
754
896
 
755
- def register_lazy_module_methods(prefix: str, methods: Tuple[str, ...]) -> None:
897
+ def register_lazy_module_methods(prefix: str, methods: tuple[str, ...]) -> None:
756
898
  """Register methods for a lazy module."""
757
899
  try:
758
900
  _register_lazy_module_methods(prefix, methods)
@@ -839,7 +981,7 @@ def get_keyword_detection_keyword(package_name: Optional[str] = None) -> Optiona
839
981
  logger.error(f"Failed to get keyword detection keyword: {e}")
840
982
  return None
841
983
 
842
- def check_package_keywords(package_name: Optional[str] = None, keywords: Optional[List[str]] = None) -> bool:
984
+ def check_package_keywords(package_name: Optional[str] = None, keywords: Optional[list[str]] = None) -> bool:
843
985
  """
844
986
  Check if a package (or any package) has the specified keyword in its metadata.
845
987
 
@@ -872,7 +1014,7 @@ def get_lazy_discovery(package_name: str = 'default') -> Optional[APackageHelper
872
1014
  logger.error(f"Failed to get discovery instance for {package_name}: {e}")
873
1015
  return None
874
1016
 
875
- def discover_dependencies(package_name: str = 'default') -> Dict[str, str]:
1017
+ def discover_dependencies(package_name: str = 'default') -> dict[str, str]:
876
1018
  """Discover dependencies for a package."""
877
1019
  try:
878
1020
  discovery = _get_lazy_discovery()
@@ -916,6 +1058,10 @@ __all__ = [
916
1058
  'configure_lazy_mode',
917
1059
  'preload_modules',
918
1060
  'optimize_lazy_mode',
1061
+ # One-line activation API
1062
+ 'auto_enable_lazy',
1063
+ # Lazy-loader compatible API
1064
+ 'attach',
919
1065
  # Public API functions
920
1066
  'enable_lazy_install',
921
1067
  'disable_lazy_install',
@@ -0,0 +1,8 @@
1
+ """
2
+ Host-facing API for xwlazy.
3
+
4
+ This package provides compatibility interfaces for host packages like xwsystem.
5
+ """
6
+
7
+ __all__ = []
8
+
@@ -0,0 +1,16 @@
1
+ """
2
+ Host-facing configuration module for xwlazy.
3
+
4
+ This module provides the get_conf_module function that host packages
5
+ like xwsystem use to enable lazy mode via exonware.conf.
6
+
7
+ Company: eXonware.com
8
+ Author: Eng. Muhammad AlShehri
9
+ Email: connect@exonware.com
10
+ """
11
+
12
+ # Re-export from the actual implementation location
13
+ from ..package.conf import get_conf_module
14
+
15
+ __all__ = ['get_conf_module']
16
+
@@ -15,13 +15,14 @@ This module defines the abstract base class for module operations.
15
15
  import sys
16
16
  import threading
17
17
  from abc import ABC, abstractmethod
18
- from typing import Dict, List, Optional, Any, Tuple
18
+ from typing import Optional, Any
19
19
  from types import ModuleType
20
20
 
21
21
  from ..contracts import (
22
22
  IModuleHelper,
23
23
  IModuleHelperStrategy,
24
24
  IModuleManagerStrategy,
25
+ ILoadStrategy,
25
26
  )
26
27
  from ..package.base import APackageHelper
27
28
 
@@ -291,7 +292,7 @@ class AModuleHelper(IModuleHelper, ABC):
291
292
  # Uninstall the packages (package_helper handles deduplication and caching)
292
293
  self._get_package_helper().uninstall(*package_names)
293
294
 
294
- def load(self, *module_names: str) -> List[ModuleType]:
295
+ def load(self, *module_names: str) -> list[ModuleType]:
295
296
  """
296
297
  Load one or more modules into memory.
297
298
 
@@ -405,7 +406,7 @@ class AModuleHelper(IModuleHelper, ABC):
405
406
  # Note: Many methods from IModuleHelper are already implemented above.
406
407
  # The following are stubs that need concrete implementations:
407
408
 
408
- def install_and_import(self, module_name: str, package_name: Optional[str] = None) -> Tuple[Optional[ModuleType], bool]:
409
+ def install_and_import(self, module_name: str, package_name: Optional[str] = None) -> tuple[Optional[ModuleType], bool]:
409
410
  """Install package and import module (from IModuleInstaller)."""
410
411
  raise NotImplementedError("Subclasses must implement install_and_import")
411
412
 
@@ -465,7 +466,7 @@ class AModuleHelper(IModuleHelper, ABC):
465
466
  """Check if a root module name is being watched (from IWatchedRegistry)."""
466
467
  raise NotImplementedError("Subclasses must implement has_root")
467
468
 
468
- def get_matching_prefixes(self, fullname: str) -> Tuple[str, ...]:
469
+ def get_matching_prefixes(self, fullname: str) -> tuple[str, ...]:
469
470
  """Get all watched prefixes that match a module name (from IWatchedRegistry)."""
470
471
  raise NotImplementedError("Subclasses must implement get_matching_prefixes")
471
472
 
@@ -557,9 +558,65 @@ class AModuleManagerStrategy(IModuleManagerStrategy, ABC):
557
558
  # EXPORT ALL
558
559
  # =============================================================================
559
560
 
561
+ # =============================================================================
562
+ # ABSTRACT LOADING STRATEGY (Enhanced for Runtime Swapping)
563
+ # =============================================================================
564
+
565
+ class ALoadStrategy(ILoadStrategy, ABC):
566
+ """
567
+ Abstract base class for module loading strategies.
568
+
569
+ Enables runtime strategy swapping for different loading methods
570
+ (lazy, simple, advanced, etc.).
571
+ """
572
+
573
+ @abstractmethod
574
+ def load(self, module_name: str) -> ModuleType:
575
+ """
576
+ Load a module.
577
+
578
+ Args:
579
+ module_name: Module name to load
580
+
581
+ Returns:
582
+ Loaded module
583
+ """
584
+ ...
585
+
586
+ def should_lazy_load(self, module_name: str) -> bool:
587
+ """
588
+ Determine if module should be lazy loaded.
589
+
590
+ Default implementation returns True.
591
+ Override for strategy-specific logic.
592
+
593
+ Args:
594
+ module_name: Module name to check
595
+
596
+ Returns:
597
+ True if should lazy load, False otherwise
598
+ """
599
+ return True
600
+
601
+ @abstractmethod
602
+ def unload(self, module_name: str) -> None:
603
+ """
604
+ Unload a module.
605
+
606
+ Args:
607
+ module_name: Module name to unload
608
+ """
609
+ ...
610
+
611
+ # =============================================================================
612
+ # EXPORT ALL
613
+ # =============================================================================
614
+
560
615
  __all__ = [
561
616
  'AModuleHelper',
562
617
  'AModuleHelperStrategy',
563
618
  'AModuleManagerStrategy',
619
+ # Enhanced Strategy Interfaces for Runtime Swapping
620
+ 'ALoadStrategy',
564
621
  ]
565
622
 
@@ -9,7 +9,7 @@ Uses strategy pattern for caching, helper, and manager strategies.
9
9
  import sys
10
10
  import importlib
11
11
  import importlib.util
12
- from typing import Optional, Dict
12
+ from typing import Optional
13
13
  from types import ModuleType
14
14
 
15
15
  from .base import AModuleHelper, APackageHelper