exonware-xwlazy 0.1.0.11__py3-none-any.whl → 0.1.0.20__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 +26 -0
  2. exonware/xwlazy/__init__.py +0 -0
  3. exonware/xwlazy/common/__init__.py +47 -0
  4. exonware/xwlazy/common/base.py +56 -0
  5. exonware/xwlazy/common/cache.py +504 -0
  6. exonware/xwlazy/common/logger.py +257 -0
  7. exonware/xwlazy/common/services/__init__.py +72 -0
  8. exonware/xwlazy/common/services/dependency_mapper.py +232 -0
  9. exonware/xwlazy/common/services/install_async_utils.py +165 -0
  10. exonware/xwlazy/common/services/install_cache_utils.py +245 -0
  11. exonware/xwlazy/common/services/keyword_detection.py +283 -0
  12. exonware/xwlazy/common/services/spec_cache.py +165 -0
  13. xwlazy/lazy/lazy_state.py → exonware/xwlazy/common/services/state_manager.py +0 -2
  14. exonware/xwlazy/common/strategies/__init__.py +28 -0
  15. exonware/xwlazy/common/strategies/caching_dict.py +44 -0
  16. exonware/xwlazy/common/strategies/caching_installation.py +88 -0
  17. exonware/xwlazy/common/strategies/caching_lfu.py +66 -0
  18. exonware/xwlazy/common/strategies/caching_lru.py +63 -0
  19. exonware/xwlazy/common/strategies/caching_multitier.py +59 -0
  20. exonware/xwlazy/common/strategies/caching_ttl.py +59 -0
  21. {xwlazy/lazy → exonware/xwlazy}/config.py +51 -21
  22. exonware/xwlazy/contracts.py +1396 -0
  23. exonware/xwlazy/defs.py +378 -0
  24. xwlazy/lazy/lazy_errors.py → exonware/xwlazy/errors.py +21 -16
  25. exonware/xwlazy/facade.py +991 -0
  26. exonware/xwlazy/module/__init__.py +18 -0
  27. exonware/xwlazy/module/base.py +565 -0
  28. exonware/xwlazy/module/data.py +17 -0
  29. exonware/xwlazy/module/facade.py +246 -0
  30. exonware/xwlazy/module/importer_engine.py +2117 -0
  31. exonware/xwlazy/module/strategies/__init__.py +22 -0
  32. exonware/xwlazy/module/strategies/module_helper_lazy.py +93 -0
  33. exonware/xwlazy/module/strategies/module_helper_simple.py +65 -0
  34. exonware/xwlazy/module/strategies/module_manager_advanced.py +111 -0
  35. exonware/xwlazy/module/strategies/module_manager_simple.py +95 -0
  36. exonware/xwlazy/package/__init__.py +18 -0
  37. exonware/xwlazy/package/base.py +798 -0
  38. xwlazy/lazy/host_conf.py → exonware/xwlazy/package/conf.py +61 -16
  39. exonware/xwlazy/package/data.py +17 -0
  40. exonware/xwlazy/package/facade.py +480 -0
  41. exonware/xwlazy/package/services/__init__.py +84 -0
  42. exonware/xwlazy/package/services/async_install_handle.py +87 -0
  43. exonware/xwlazy/package/services/config_manager.py +245 -0
  44. exonware/xwlazy/package/services/discovery.py +370 -0
  45. {xwlazy/lazy → exonware/xwlazy/package/services}/host_packages.py +43 -20
  46. exonware/xwlazy/package/services/install_async.py +277 -0
  47. exonware/xwlazy/package/services/install_cache.py +145 -0
  48. exonware/xwlazy/package/services/install_interactive.py +59 -0
  49. exonware/xwlazy/package/services/install_policy.py +156 -0
  50. exonware/xwlazy/package/services/install_registry.py +54 -0
  51. exonware/xwlazy/package/services/install_result.py +17 -0
  52. exonware/xwlazy/package/services/install_sbom.py +153 -0
  53. exonware/xwlazy/package/services/install_utils.py +79 -0
  54. exonware/xwlazy/package/services/installer_engine.py +406 -0
  55. exonware/xwlazy/package/services/lazy_installer.py +718 -0
  56. {xwlazy/lazy → exonware/xwlazy/package/services}/manifest.py +40 -33
  57. exonware/xwlazy/package/services/strategy_registry.py +186 -0
  58. exonware/xwlazy/package/strategies/__init__.py +57 -0
  59. exonware/xwlazy/package/strategies/package_discovery_file.py +129 -0
  60. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +84 -0
  61. exonware/xwlazy/package/strategies/package_discovery_manifest.py +101 -0
  62. exonware/xwlazy/package/strategies/package_execution_async.py +113 -0
  63. exonware/xwlazy/package/strategies/package_execution_cached.py +90 -0
  64. exonware/xwlazy/package/strategies/package_execution_pip.py +99 -0
  65. exonware/xwlazy/package/strategies/package_execution_wheel.py +106 -0
  66. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +100 -0
  67. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +105 -0
  68. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +100 -0
  69. exonware/xwlazy/package/strategies/package_policy_allow_list.py +57 -0
  70. exonware/xwlazy/package/strategies/package_policy_deny_list.py +57 -0
  71. exonware/xwlazy/package/strategies/package_policy_permissive.py +46 -0
  72. exonware/xwlazy/package/strategies/package_timing_clean.py +67 -0
  73. exonware/xwlazy/package/strategies/package_timing_full.py +66 -0
  74. exonware/xwlazy/package/strategies/package_timing_smart.py +68 -0
  75. exonware/xwlazy/package/strategies/package_timing_temporary.py +66 -0
  76. exonware/xwlazy/runtime/__init__.py +18 -0
  77. exonware/xwlazy/runtime/adaptive_learner.py +129 -0
  78. exonware/xwlazy/runtime/base.py +274 -0
  79. exonware/xwlazy/runtime/facade.py +94 -0
  80. exonware/xwlazy/runtime/intelligent_selector.py +170 -0
  81. exonware/xwlazy/runtime/metrics.py +60 -0
  82. exonware/xwlazy/runtime/performance.py +37 -0
  83. exonware/xwlazy/version.py +2 -2
  84. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.20.dist-info}/METADATA +89 -11
  85. exonware_xwlazy-0.1.0.20.dist-info/RECORD +87 -0
  86. exonware_xwlazy-0.1.0.11.dist-info/RECORD +0 -20
  87. xwlazy/__init__.py +0 -34
  88. xwlazy/lazy/__init__.py +0 -301
  89. xwlazy/lazy/bootstrap.py +0 -106
  90. xwlazy/lazy/lazy_base.py +0 -465
  91. xwlazy/lazy/lazy_contracts.py +0 -290
  92. xwlazy/lazy/lazy_core.py +0 -3727
  93. xwlazy/lazy/logging_utils.py +0 -194
  94. xwlazy/version.py +0 -77
  95. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.20.dist-info}/WHEEL +0 -0
  96. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.20.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,84 @@
1
+ """
2
+ Package Services
3
+
4
+ Supporting services for package operations (not strategies).
5
+ """
6
+
7
+ from .install_result import InstallStatus, InstallResult
8
+ from .install_policy import LazyInstallPolicy
9
+ from .install_utils import get_trigger_file, is_externally_managed, check_pip_audit_available
10
+ from .install_registry import LazyInstallerRegistry
11
+ from .installer_engine import InstallerEngine
12
+ from .async_install_handle import AsyncInstallHandle
13
+ from .lazy_installer import LazyInstaller
14
+ from .strategy_registry import StrategyRegistry
15
+
16
+ # Config and manifest services
17
+ from .config_manager import LazyInstallConfig
18
+ from .manifest import (
19
+ PackageManifest,
20
+ LazyManifestLoader,
21
+ get_manifest_loader,
22
+ refresh_manifest_cache,
23
+ sync_manifest_configuration,
24
+ _normalize_prefix,
25
+ )
26
+
27
+ # Discovery service
28
+ from .discovery import LazyDiscovery, get_lazy_discovery
29
+
30
+ # State management and keyword detection (moved to common/services)
31
+ from ...common.services import (
32
+ LazyStateManager,
33
+ enable_keyword_detection,
34
+ is_keyword_detection_enabled,
35
+ get_keyword_detection_keyword,
36
+ check_package_keywords,
37
+ _detect_lazy_installation,
38
+ _detect_meta_info_mode,
39
+ )
40
+
41
+ # Host package registration
42
+ from .host_packages import (
43
+ register_host_package,
44
+ refresh_host_package,
45
+ )
46
+
47
+ __all__ = [
48
+ # Install services
49
+ 'InstallStatus',
50
+ 'InstallResult',
51
+ 'LazyInstallPolicy',
52
+ 'LazyInstallerRegistry',
53
+ 'InstallerEngine',
54
+ 'AsyncInstallHandle',
55
+ 'LazyInstaller',
56
+ 'StrategyRegistry',
57
+ 'get_trigger_file',
58
+ 'is_externally_managed',
59
+ 'check_pip_audit_available',
60
+ # Config and manifest
61
+ 'LazyInstallConfig',
62
+ 'PackageManifest',
63
+ 'LazyManifestLoader',
64
+ 'get_manifest_loader',
65
+ 'refresh_manifest_cache',
66
+ 'sync_manifest_configuration',
67
+ '_normalize_prefix',
68
+ # Discovery
69
+ 'LazyDiscovery',
70
+ 'get_lazy_discovery',
71
+ # State management
72
+ 'LazyStateManager',
73
+ # Keyword detection
74
+ 'enable_keyword_detection',
75
+ 'is_keyword_detection_enabled',
76
+ 'get_keyword_detection_keyword',
77
+ 'check_package_keywords',
78
+ '_detect_lazy_installation',
79
+ '_detect_meta_info_mode',
80
+ # Host package registration
81
+ 'register_host_package',
82
+ 'refresh_host_package',
83
+ ]
84
+
@@ -0,0 +1,87 @@
1
+ """
2
+ Async Install Handle
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Lightweight handle for background installation jobs.
11
+ """
12
+
13
+ import asyncio
14
+ from typing import Optional, Any
15
+
16
+ class AsyncInstallHandle:
17
+ """Lightweight handle for background installation jobs."""
18
+
19
+ __slots__ = ("_task_or_future", "module_name", "package_name", "installer_package")
20
+
21
+ def __init__(
22
+ self,
23
+ task_or_future: Any, # Can be Future or asyncio.Task
24
+ module_name: str,
25
+ package_name: str,
26
+ installer_package: str,
27
+ ) -> None:
28
+ self._task_or_future = task_or_future
29
+ self.module_name = module_name
30
+ self.package_name = package_name
31
+ self.installer_package = installer_package
32
+
33
+ def wait(self, timeout: Optional[float] = None) -> bool:
34
+ """
35
+ Wait for installation to complete.
36
+
37
+ Args:
38
+ timeout: Optional timeout in seconds
39
+
40
+ Returns:
41
+ True if installation succeeded, False otherwise
42
+ """
43
+ try:
44
+ # Handle concurrent.futures.Future (from asyncio.run_coroutine_threadsafe)
45
+ if hasattr(self._task_or_future, 'result'):
46
+ result = self._task_or_future.result(timeout=timeout)
47
+ return bool(result)
48
+ # Handle asyncio.Task
49
+ elif hasattr(self._task_or_future, 'done'):
50
+ if timeout is None:
51
+ # Use asyncio.wait_for if we have a loop
52
+ try:
53
+ loop = asyncio.get_event_loop()
54
+ if loop.is_running():
55
+ # Can't wait in running loop, return False
56
+ return False
57
+ except RuntimeError:
58
+ pass
59
+ # Create new event loop to wait
60
+ return asyncio.run(self._wait_task())
61
+ else:
62
+ return asyncio.run(asyncio.wait_for(self._wait_task(), timeout=timeout))
63
+ return False
64
+ except Exception:
65
+ return False
66
+
67
+ async def _wait_task(self) -> bool:
68
+ """Async helper to wait for task."""
69
+ if hasattr(self._task_or_future, 'done'):
70
+ await self._task_or_future
71
+ return bool(self._task_or_future.result() if hasattr(self._task_or_future, 'result') else True)
72
+ return False
73
+
74
+ @property
75
+ def done(self) -> bool:
76
+ """
77
+ Check if installation is complete.
78
+
79
+ Returns:
80
+ True if installation is complete
81
+ """
82
+ if hasattr(self._task_or_future, 'done'):
83
+ return self._task_or_future.done()
84
+ return False
85
+
86
+ __all__ = ['AsyncInstallHandle']
87
+
@@ -0,0 +1,245 @@
1
+ """
2
+ Configuration management for lazy loading system.
3
+
4
+ This module contains LazyInstallConfig which manages per-package lazy installation
5
+ configuration. Extracted from lazy_core.py Section 5.
6
+ """
7
+
8
+ from typing import Dict, Optional
9
+ from ...common.services import LazyStateManager
10
+ from ...defs import LazyLoadMode, LazyInstallMode, LazyModeConfig
11
+ from ...defs import get_preset_mode
12
+
13
+ # Lazy import to avoid circular dependency
14
+ def _get_logger():
15
+ """Get logger (lazy import to avoid circular dependency)."""
16
+ from ...common.logger import get_logger
17
+ return get_logger("xwlazy.config")
18
+
19
+ def _get_log_event():
20
+ """Get log_event function (lazy import to avoid circular dependency)."""
21
+ from ...common.logger import log_event
22
+ return log_event
23
+
24
+ logger = None # Will be initialized on first use
25
+ _log = None # Will be initialized on first use
26
+
27
+ # Mode enum mapping - extracted from lazy_core.py
28
+ _MODE_ENUM_MAP = {
29
+ # Core v1.0 modes
30
+ "none": LazyInstallMode.NONE,
31
+ "smart": LazyInstallMode.SMART,
32
+ "full": LazyInstallMode.FULL,
33
+ "clean": LazyInstallMode.CLEAN,
34
+ "temporary": LazyInstallMode.TEMPORARY,
35
+ "size_aware": LazyInstallMode.SIZE_AWARE,
36
+ # Special purpose modes
37
+ "interactive": LazyInstallMode.INTERACTIVE,
38
+ "warn": LazyInstallMode.WARN,
39
+ "disabled": LazyInstallMode.DISABLED,
40
+ "dry_run": LazyInstallMode.DRY_RUN,
41
+ # Legacy aliases
42
+ "auto": LazyInstallMode.SMART,
43
+ "on_demand": LazyInstallMode.SMART,
44
+ "on-demand": LazyInstallMode.SMART,
45
+ "lazy": LazyInstallMode.SMART,
46
+ }
47
+
48
+ class LazyInstallConfig:
49
+ """Global configuration for lazy installation per package."""
50
+ _configs: Dict[str, bool] = {}
51
+ _modes: Dict[str, str] = {}
52
+ _load_modes: Dict[str, LazyLoadMode] = {}
53
+ _install_modes: Dict[str, LazyInstallMode] = {}
54
+ _mode_configs: Dict[str, LazyModeConfig] = {}
55
+ _initialized: Dict[str, bool] = {}
56
+ _manual_overrides: Dict[str, bool] = {}
57
+
58
+ @classmethod
59
+ def set(
60
+ cls,
61
+ package_name: str,
62
+ enabled: bool,
63
+ mode: str = "auto",
64
+ install_hook: bool = True,
65
+ manual: bool = False,
66
+ load_mode: Optional[LazyLoadMode] = None,
67
+ install_mode: Optional[LazyInstallMode] = None,
68
+ mode_config: Optional[LazyModeConfig] = None,
69
+ ) -> None:
70
+ """Enable or disable lazy installation for a specific package."""
71
+ package_key = package_name.lower()
72
+ state_manager = LazyStateManager(package_name)
73
+
74
+ if manual:
75
+ cls._manual_overrides[package_key] = True
76
+ state_manager.set_manual_state(enabled)
77
+ elif cls._manual_overrides.get(package_key):
78
+ global logger
79
+ if logger is None:
80
+ logger = _get_logger()
81
+ logger.debug(
82
+ f"Lazy install config for {package_key} already overridden manually; skipping auto configuration."
83
+ )
84
+ return
85
+ else:
86
+ state_manager.set_manual_state(None)
87
+
88
+ cls._configs[package_key] = enabled
89
+ cls._modes[package_key] = mode
90
+
91
+ # Handle two-dimensional mode configuration
92
+ if mode_config:
93
+ cls._mode_configs[package_key] = mode_config
94
+ cls._load_modes[package_key] = mode_config.load_mode
95
+ cls._install_modes[package_key] = mode_config.install_mode
96
+ elif load_mode is not None or install_mode is not None:
97
+ # Explicit mode specification
98
+ if load_mode is None:
99
+ load_mode = LazyLoadMode.AUTO # Default
100
+ if install_mode is None:
101
+ install_mode = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
102
+ cls._load_modes[package_key] = load_mode
103
+ cls._install_modes[package_key] = install_mode
104
+ cls._mode_configs[package_key] = LazyModeConfig(
105
+ load_mode=load_mode,
106
+ install_mode=install_mode
107
+ )
108
+ else:
109
+ # Legacy mode string - try to resolve to preset or default
110
+ preset = get_preset_mode(mode)
111
+ if preset:
112
+ cls._mode_configs[package_key] = preset
113
+ cls._load_modes[package_key] = preset.load_mode
114
+ cls._install_modes[package_key] = preset.install_mode
115
+ else:
116
+ # Fallback to legacy behavior
117
+ install_mode_enum = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
118
+ cls._load_modes[package_key] = LazyLoadMode.AUTO
119
+ cls._install_modes[package_key] = install_mode_enum
120
+ cls._mode_configs[package_key] = LazyModeConfig(
121
+ load_mode=LazyLoadMode.AUTO,
122
+ install_mode=install_mode_enum
123
+ )
124
+
125
+ cls._initialize_package(package_key, enabled, mode, install_hook=install_hook)
126
+
127
+ @classmethod
128
+ def _initialize_package(cls, package_key: str, enabled: bool, mode: str, install_hook: bool = True) -> None:
129
+ """Initialize lazy installation for a specific package."""
130
+ global logger, _log
131
+ if logger is None:
132
+ logger = _get_logger()
133
+ if _log is None:
134
+ _log = _get_log_event()
135
+
136
+ # Deferred imports to avoid circular dependency
137
+ from .install_registry import LazyInstallerRegistry
138
+ from ...facade import (
139
+ enable_lazy_install,
140
+ disable_lazy_install,
141
+ set_lazy_install_mode,
142
+ enable_lazy_imports,
143
+ install_import_hook,
144
+ uninstall_import_hook,
145
+ is_import_hook_installed,
146
+ sync_manifest_configuration,
147
+ )
148
+ import asyncio
149
+
150
+ if enabled:
151
+ try:
152
+ # Don't call enable_lazy_install() here - it would create infinite recursion
153
+ # The config is already set by LazyInstallConfig.set() above
154
+
155
+ # Use explicitly set install_mode from config, or derive from mode string
156
+ # Check if install_mode was explicitly set by checking if package_key exists in _install_modes
157
+ if package_key in cls._install_modes:
158
+ # install_mode was explicitly set in set() method, don't override it
159
+ mode_enum = cls._install_modes[package_key]
160
+ else:
161
+ # Not explicitly set, derive from mode string
162
+ mode_enum = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
163
+ set_lazy_install_mode(package_key, mode_enum)
164
+
165
+ # Get load mode from config
166
+ load_mode = cls.get_load_mode(package_key)
167
+
168
+ # Enable lazy imports with appropriate load mode (skip if NONE mode)
169
+ if load_mode != LazyLoadMode.NONE:
170
+ enable_lazy_imports(load_mode, package_name=package_key)
171
+
172
+ # Enable async for modes that support it
173
+ installer = LazyInstallerRegistry.get_instance(package_key)
174
+ if installer and mode_enum in (LazyInstallMode.SMART, LazyInstallMode.FULL, LazyInstallMode.CLEAN, LazyInstallMode.TEMPORARY):
175
+ installer._async_enabled = True
176
+ installer._ensure_async_loop()
177
+
178
+ # For FULL mode, install all dependencies on start
179
+ if mode_enum == LazyInstallMode.FULL:
180
+ loop = installer._async_loop
181
+ if loop:
182
+ asyncio.run_coroutine_threadsafe(installer.install_all_dependencies(), loop)
183
+
184
+ if install_hook:
185
+ if not is_import_hook_installed(package_key):
186
+ install_import_hook(package_key)
187
+ _log("config", logger.info, f"✅ Lazy installation initialized for {package_key} (install_mode: {mode}, load_mode: {load_mode.value}, hook: installed)")
188
+ else:
189
+ uninstall_import_hook(package_key)
190
+ _log("config", logger.info, f"✅ Lazy installation initialized for {package_key} (install_mode: {mode}, load_mode: {load_mode.value}, hook: disabled)")
191
+
192
+ cls._initialized[package_key] = True
193
+ sync_manifest_configuration(package_key)
194
+ except ImportError as e:
195
+ if logger is None:
196
+ logger = _get_logger()
197
+ logger.warning(f"⚠️ Could not enable lazy install for {package_key}: {e}")
198
+ else:
199
+ try:
200
+ disable_lazy_install(package_key)
201
+ except ImportError:
202
+ pass
203
+ uninstall_import_hook(package_key)
204
+ cls._initialized[package_key] = False
205
+ _log("config", logger.info, f"❌ Lazy installation disabled for {package_key}")
206
+ sync_manifest_configuration(package_key)
207
+
208
+ @classmethod
209
+ def is_enabled(cls, package_name: str) -> bool:
210
+ """Check if lazy installation is enabled for a package."""
211
+ return cls._configs.get(package_name.lower(), False)
212
+
213
+ @classmethod
214
+ def get_mode(cls, package_name: str) -> str:
215
+ """Get the lazy installation mode for a package."""
216
+ return cls._modes.get(package_name.lower(), "auto")
217
+
218
+ @classmethod
219
+ def get_mode_config(cls, package_name: str) -> Optional[LazyModeConfig]:
220
+ """Get the full mode configuration for a package."""
221
+ return cls._mode_configs.get(package_name.lower())
222
+
223
+ @classmethod
224
+ def get_load_mode(cls, package_name: str) -> LazyLoadMode:
225
+ """Get the load mode for a package."""
226
+ return cls._load_modes.get(package_name.lower(), LazyLoadMode.NONE)
227
+
228
+ @classmethod
229
+ def get_install_mode(cls, package_name: str) -> LazyInstallMode:
230
+ """Get the install mode for a package."""
231
+ return cls._install_modes.get(package_name.lower(), LazyInstallMode.NONE)
232
+
233
+ @classmethod
234
+ def set_install_mode(cls, package_name: str, mode: LazyInstallMode) -> None:
235
+ """Set the install mode for a package."""
236
+ package_key = package_name.lower()
237
+ cls._install_modes[package_key] = mode
238
+ # Update mode config if it exists
239
+ if package_key in cls._mode_configs:
240
+ mode_config = cls._mode_configs[package_key]
241
+ cls._mode_configs[package_key] = LazyModeConfig(
242
+ load_mode=mode_config.load_mode,
243
+ install_mode=mode
244
+ )
245
+