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,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,89 @@
1
+ """
2
+ Async Install Handle
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
+ Lightweight handle for background installation jobs.
11
+ """
12
+
13
+ import asyncio
14
+ from typing import Optional, Any
15
+
16
+
17
+ class AsyncInstallHandle:
18
+ """Lightweight handle for background installation jobs."""
19
+
20
+ __slots__ = ("_task_or_future", "module_name", "package_name", "installer_package")
21
+
22
+ def __init__(
23
+ self,
24
+ task_or_future: Any, # Can be Future or asyncio.Task
25
+ module_name: str,
26
+ package_name: str,
27
+ installer_package: str,
28
+ ) -> None:
29
+ self._task_or_future = task_or_future
30
+ self.module_name = module_name
31
+ self.package_name = package_name
32
+ self.installer_package = installer_package
33
+
34
+ def wait(self, timeout: Optional[float] = None) -> bool:
35
+ """
36
+ Wait for installation to complete.
37
+
38
+ Args:
39
+ timeout: Optional timeout in seconds
40
+
41
+ Returns:
42
+ True if installation succeeded, False otherwise
43
+ """
44
+ try:
45
+ # Handle concurrent.futures.Future (from asyncio.run_coroutine_threadsafe)
46
+ if hasattr(self._task_or_future, 'result'):
47
+ result = self._task_or_future.result(timeout=timeout)
48
+ return bool(result)
49
+ # Handle asyncio.Task
50
+ elif hasattr(self._task_or_future, 'done'):
51
+ if timeout is None:
52
+ # Use asyncio.wait_for if we have a loop
53
+ try:
54
+ loop = asyncio.get_event_loop()
55
+ if loop.is_running():
56
+ # Can't wait in running loop, return False
57
+ return False
58
+ except RuntimeError:
59
+ pass
60
+ # Create new event loop to wait
61
+ return asyncio.run(self._wait_task())
62
+ else:
63
+ return asyncio.run(asyncio.wait_for(self._wait_task(), timeout=timeout))
64
+ return False
65
+ except Exception:
66
+ return False
67
+
68
+ async def _wait_task(self) -> bool:
69
+ """Async helper to wait for task."""
70
+ if hasattr(self._task_or_future, 'done'):
71
+ await self._task_or_future
72
+ return bool(self._task_or_future.result() if hasattr(self._task_or_future, 'result') else True)
73
+ return False
74
+
75
+ @property
76
+ def done(self) -> bool:
77
+ """
78
+ Check if installation is complete.
79
+
80
+ Returns:
81
+ True if installation is complete
82
+ """
83
+ if hasattr(self._task_or_future, 'done'):
84
+ return self._task_or_future.done()
85
+ return False
86
+
87
+
88
+ __all__ = ['AsyncInstallHandle']
89
+
@@ -0,0 +1,246 @@
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
+
49
+ class LazyInstallConfig:
50
+ """Global configuration for lazy installation per package."""
51
+ _configs: Dict[str, bool] = {}
52
+ _modes: Dict[str, str] = {}
53
+ _load_modes: Dict[str, LazyLoadMode] = {}
54
+ _install_modes: Dict[str, LazyInstallMode] = {}
55
+ _mode_configs: Dict[str, LazyModeConfig] = {}
56
+ _initialized: Dict[str, bool] = {}
57
+ _manual_overrides: Dict[str, bool] = {}
58
+
59
+ @classmethod
60
+ def set(
61
+ cls,
62
+ package_name: str,
63
+ enabled: bool,
64
+ mode: str = "auto",
65
+ install_hook: bool = True,
66
+ manual: bool = False,
67
+ load_mode: Optional[LazyLoadMode] = None,
68
+ install_mode: Optional[LazyInstallMode] = None,
69
+ mode_config: Optional[LazyModeConfig] = None,
70
+ ) -> None:
71
+ """Enable or disable lazy installation for a specific package."""
72
+ package_key = package_name.lower()
73
+ state_manager = LazyStateManager(package_name)
74
+
75
+ if manual:
76
+ cls._manual_overrides[package_key] = True
77
+ state_manager.set_manual_state(enabled)
78
+ elif cls._manual_overrides.get(package_key):
79
+ global logger
80
+ if logger is None:
81
+ logger = _get_logger()
82
+ logger.debug(
83
+ f"Lazy install config for {package_key} already overridden manually; skipping auto configuration."
84
+ )
85
+ return
86
+ else:
87
+ state_manager.set_manual_state(None)
88
+
89
+ cls._configs[package_key] = enabled
90
+ cls._modes[package_key] = mode
91
+
92
+ # Handle two-dimensional mode configuration
93
+ if mode_config:
94
+ cls._mode_configs[package_key] = mode_config
95
+ cls._load_modes[package_key] = mode_config.load_mode
96
+ cls._install_modes[package_key] = mode_config.install_mode
97
+ elif load_mode is not None or install_mode is not None:
98
+ # Explicit mode specification
99
+ if load_mode is None:
100
+ load_mode = LazyLoadMode.AUTO # Default
101
+ if install_mode is None:
102
+ install_mode = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
103
+ cls._load_modes[package_key] = load_mode
104
+ cls._install_modes[package_key] = install_mode
105
+ cls._mode_configs[package_key] = LazyModeConfig(
106
+ load_mode=load_mode,
107
+ install_mode=install_mode
108
+ )
109
+ else:
110
+ # Legacy mode string - try to resolve to preset or default
111
+ preset = get_preset_mode(mode)
112
+ if preset:
113
+ cls._mode_configs[package_key] = preset
114
+ cls._load_modes[package_key] = preset.load_mode
115
+ cls._install_modes[package_key] = preset.install_mode
116
+ else:
117
+ # Fallback to legacy behavior
118
+ install_mode_enum = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
119
+ cls._load_modes[package_key] = LazyLoadMode.AUTO
120
+ cls._install_modes[package_key] = install_mode_enum
121
+ cls._mode_configs[package_key] = LazyModeConfig(
122
+ load_mode=LazyLoadMode.AUTO,
123
+ install_mode=install_mode_enum
124
+ )
125
+
126
+ cls._initialize_package(package_key, enabled, mode, install_hook=install_hook)
127
+
128
+ @classmethod
129
+ def _initialize_package(cls, package_key: str, enabled: bool, mode: str, install_hook: bool = True) -> None:
130
+ """Initialize lazy installation for a specific package."""
131
+ global logger, _log
132
+ if logger is None:
133
+ logger = _get_logger()
134
+ if _log is None:
135
+ _log = _get_log_event()
136
+
137
+ # Deferred imports to avoid circular dependency
138
+ from .install_registry import LazyInstallerRegistry
139
+ from ...facade import (
140
+ enable_lazy_install,
141
+ disable_lazy_install,
142
+ set_lazy_install_mode,
143
+ enable_lazy_imports,
144
+ install_import_hook,
145
+ uninstall_import_hook,
146
+ is_import_hook_installed,
147
+ sync_manifest_configuration,
148
+ )
149
+ import asyncio
150
+
151
+ if enabled:
152
+ try:
153
+ # Don't call enable_lazy_install() here - it would create infinite recursion
154
+ # The config is already set by LazyInstallConfig.set() above
155
+
156
+ # Use explicitly set install_mode from config, or derive from mode string
157
+ # Check if install_mode was explicitly set by checking if package_key exists in _install_modes
158
+ if package_key in cls._install_modes:
159
+ # install_mode was explicitly set in set() method, don't override it
160
+ mode_enum = cls._install_modes[package_key]
161
+ else:
162
+ # Not explicitly set, derive from mode string
163
+ mode_enum = _MODE_ENUM_MAP.get(mode.lower(), LazyInstallMode.SMART)
164
+ set_lazy_install_mode(package_key, mode_enum)
165
+
166
+ # Get load mode from config
167
+ load_mode = cls.get_load_mode(package_key)
168
+
169
+ # Enable lazy imports with appropriate load mode (skip if NONE mode)
170
+ if load_mode != LazyLoadMode.NONE:
171
+ enable_lazy_imports(load_mode, package_name=package_key)
172
+
173
+ # Enable async for modes that support it
174
+ installer = LazyInstallerRegistry.get_instance(package_key)
175
+ if installer and mode_enum in (LazyInstallMode.SMART, LazyInstallMode.FULL, LazyInstallMode.CLEAN, LazyInstallMode.TEMPORARY):
176
+ installer._async_enabled = True
177
+ installer._ensure_async_loop()
178
+
179
+ # For FULL mode, install all dependencies on start
180
+ if mode_enum == LazyInstallMode.FULL:
181
+ loop = installer._async_loop
182
+ if loop:
183
+ asyncio.run_coroutine_threadsafe(installer.install_all_dependencies(), loop)
184
+
185
+ if install_hook:
186
+ if not is_import_hook_installed(package_key):
187
+ install_import_hook(package_key)
188
+ _log("config", logger.info, f"✅ Lazy installation initialized for {package_key} (install_mode: {mode}, load_mode: {load_mode.value}, hook: installed)")
189
+ else:
190
+ uninstall_import_hook(package_key)
191
+ _log("config", logger.info, f"✅ Lazy installation initialized for {package_key} (install_mode: {mode}, load_mode: {load_mode.value}, hook: disabled)")
192
+
193
+ cls._initialized[package_key] = True
194
+ sync_manifest_configuration(package_key)
195
+ except ImportError as e:
196
+ if logger is None:
197
+ logger = _get_logger()
198
+ logger.warning(f"⚠️ Could not enable lazy install for {package_key}: {e}")
199
+ else:
200
+ try:
201
+ disable_lazy_install(package_key)
202
+ except ImportError:
203
+ pass
204
+ uninstall_import_hook(package_key)
205
+ cls._initialized[package_key] = False
206
+ _log("config", logger.info, f"❌ Lazy installation disabled for {package_key}")
207
+ sync_manifest_configuration(package_key)
208
+
209
+ @classmethod
210
+ def is_enabled(cls, package_name: str) -> bool:
211
+ """Check if lazy installation is enabled for a package."""
212
+ return cls._configs.get(package_name.lower(), False)
213
+
214
+ @classmethod
215
+ def get_mode(cls, package_name: str) -> str:
216
+ """Get the lazy installation mode for a package."""
217
+ return cls._modes.get(package_name.lower(), "auto")
218
+
219
+ @classmethod
220
+ def get_mode_config(cls, package_name: str) -> Optional[LazyModeConfig]:
221
+ """Get the full mode configuration for a package."""
222
+ return cls._mode_configs.get(package_name.lower())
223
+
224
+ @classmethod
225
+ def get_load_mode(cls, package_name: str) -> LazyLoadMode:
226
+ """Get the load mode for a package."""
227
+ return cls._load_modes.get(package_name.lower(), LazyLoadMode.NONE)
228
+
229
+ @classmethod
230
+ def get_install_mode(cls, package_name: str) -> LazyInstallMode:
231
+ """Get the install mode for a package."""
232
+ return cls._install_modes.get(package_name.lower(), LazyInstallMode.NONE)
233
+
234
+ @classmethod
235
+ def set_install_mode(cls, package_name: str, mode: LazyInstallMode) -> None:
236
+ """Set the install mode for a package."""
237
+ package_key = package_name.lower()
238
+ cls._install_modes[package_key] = mode
239
+ # Update mode config if it exists
240
+ if package_key in cls._mode_configs:
241
+ mode_config = cls._mode_configs[package_key]
242
+ cls._mode_configs[package_key] = LazyModeConfig(
243
+ load_mode=mode_config.load_mode,
244
+ install_mode=mode
245
+ )
246
+