exonware-xwlazy 0.1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/__init__.py +42 -0
- exonware/xwlazy/__init__.py +379 -0
- exonware/xwlazy/common/__init__.py +55 -0
- exonware/xwlazy/common/base.py +65 -0
- exonware/xwlazy/common/cache.py +504 -0
- exonware/xwlazy/common/logger.py +257 -0
- exonware/xwlazy/common/services/__init__.py +72 -0
- exonware/xwlazy/common/services/dependency_mapper.py +250 -0
- exonware/xwlazy/common/services/install_async_utils.py +170 -0
- exonware/xwlazy/common/services/install_cache_utils.py +245 -0
- exonware/xwlazy/common/services/keyword_detection.py +283 -0
- exonware/xwlazy/common/services/spec_cache.py +165 -0
- exonware/xwlazy/common/services/state_manager.py +84 -0
- exonware/xwlazy/common/strategies/__init__.py +28 -0
- exonware/xwlazy/common/strategies/caching_dict.py +44 -0
- exonware/xwlazy/common/strategies/caching_installation.py +88 -0
- exonware/xwlazy/common/strategies/caching_lfu.py +66 -0
- exonware/xwlazy/common/strategies/caching_lru.py +63 -0
- exonware/xwlazy/common/strategies/caching_multitier.py +59 -0
- exonware/xwlazy/common/strategies/caching_ttl.py +59 -0
- exonware/xwlazy/common/utils.py +142 -0
- exonware/xwlazy/config.py +193 -0
- exonware/xwlazy/contracts.py +1533 -0
- exonware/xwlazy/defs.py +378 -0
- exonware/xwlazy/errors.py +276 -0
- exonware/xwlazy/facade.py +1137 -0
- exonware/xwlazy/host/__init__.py +8 -0
- exonware/xwlazy/host/conf.py +16 -0
- exonware/xwlazy/module/__init__.py +18 -0
- exonware/xwlazy/module/base.py +643 -0
- exonware/xwlazy/module/data.py +17 -0
- exonware/xwlazy/module/facade.py +246 -0
- exonware/xwlazy/module/importer_engine.py +2964 -0
- exonware/xwlazy/module/partial_module_detector.py +275 -0
- exonware/xwlazy/module/strategies/__init__.py +22 -0
- exonware/xwlazy/module/strategies/module_helper_lazy.py +93 -0
- exonware/xwlazy/module/strategies/module_helper_simple.py +65 -0
- exonware/xwlazy/module/strategies/module_manager_advanced.py +111 -0
- exonware/xwlazy/module/strategies/module_manager_simple.py +95 -0
- exonware/xwlazy/package/__init__.py +18 -0
- exonware/xwlazy/package/base.py +877 -0
- exonware/xwlazy/package/conf.py +324 -0
- exonware/xwlazy/package/data.py +17 -0
- exonware/xwlazy/package/facade.py +480 -0
- exonware/xwlazy/package/services/__init__.py +84 -0
- exonware/xwlazy/package/services/async_install_handle.py +87 -0
- exonware/xwlazy/package/services/config_manager.py +249 -0
- exonware/xwlazy/package/services/discovery.py +435 -0
- exonware/xwlazy/package/services/host_packages.py +180 -0
- exonware/xwlazy/package/services/install_async.py +291 -0
- exonware/xwlazy/package/services/install_cache.py +145 -0
- exonware/xwlazy/package/services/install_interactive.py +59 -0
- exonware/xwlazy/package/services/install_policy.py +156 -0
- exonware/xwlazy/package/services/install_registry.py +54 -0
- exonware/xwlazy/package/services/install_result.py +17 -0
- exonware/xwlazy/package/services/install_sbom.py +153 -0
- exonware/xwlazy/package/services/install_utils.py +79 -0
- exonware/xwlazy/package/services/installer_engine.py +406 -0
- exonware/xwlazy/package/services/lazy_installer.py +803 -0
- exonware/xwlazy/package/services/manifest.py +503 -0
- exonware/xwlazy/package/services/strategy_registry.py +324 -0
- exonware/xwlazy/package/strategies/__init__.py +57 -0
- exonware/xwlazy/package/strategies/package_discovery_file.py +129 -0
- exonware/xwlazy/package/strategies/package_discovery_hybrid.py +84 -0
- exonware/xwlazy/package/strategies/package_discovery_manifest.py +101 -0
- exonware/xwlazy/package/strategies/package_execution_async.py +113 -0
- exonware/xwlazy/package/strategies/package_execution_cached.py +90 -0
- exonware/xwlazy/package/strategies/package_execution_pip.py +99 -0
- exonware/xwlazy/package/strategies/package_execution_wheel.py +106 -0
- exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +100 -0
- exonware/xwlazy/package/strategies/package_mapping_hybrid.py +105 -0
- exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +100 -0
- exonware/xwlazy/package/strategies/package_policy_allow_list.py +57 -0
- exonware/xwlazy/package/strategies/package_policy_deny_list.py +57 -0
- exonware/xwlazy/package/strategies/package_policy_permissive.py +46 -0
- exonware/xwlazy/package/strategies/package_timing_clean.py +67 -0
- exonware/xwlazy/package/strategies/package_timing_full.py +66 -0
- exonware/xwlazy/package/strategies/package_timing_smart.py +68 -0
- exonware/xwlazy/package/strategies/package_timing_temporary.py +66 -0
- exonware/xwlazy/runtime/__init__.py +18 -0
- exonware/xwlazy/runtime/adaptive_learner.py +129 -0
- exonware/xwlazy/runtime/base.py +274 -0
- exonware/xwlazy/runtime/facade.py +94 -0
- exonware/xwlazy/runtime/intelligent_selector.py +170 -0
- exonware/xwlazy/runtime/metrics.py +60 -0
- exonware/xwlazy/runtime/performance.py +37 -0
- exonware/xwlazy/version.py +77 -0
- exonware_xwlazy-0.1.0.1.dist-info/METADATA +454 -0
- exonware_xwlazy-0.1.0.1.dist-info/RECORD +93 -0
- exonware_xwlazy-0.1.0.1.dist-info/WHEEL +4 -0
- exonware_xwlazy-0.1.0.1.dist-info/licenses/LICENSE +21 -0
- xwlazy/__init__.py +14 -0
- xwlazy/lazy.py +30 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common utility functions for xwlazy.
|
|
3
|
+
|
|
4
|
+
Company: eXonware.com
|
|
5
|
+
Author: Eng. Muhammad AlShehri
|
|
6
|
+
Email: connect@exonware.com
|
|
7
|
+
|
|
8
|
+
This module provides shared utility functions used across xwlazy,
|
|
9
|
+
including path operations and project root detection.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def find_project_root(start_path: Optional[Path] = None) -> Path:
|
|
18
|
+
"""
|
|
19
|
+
Find the project root directory by looking for markers.
|
|
20
|
+
|
|
21
|
+
Package-agnostic: Finds root from running script location, not xwlazy's location.
|
|
22
|
+
This function is designed to work with any project that uses xwlazy.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
start_path: Optional starting path. If None, attempts to find from:
|
|
26
|
+
1. __main__ module location
|
|
27
|
+
2. sys.path[0] (script directory)
|
|
28
|
+
3. Current working directory
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Path to the project root directory
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
>>> root = find_project_root()
|
|
35
|
+
>>> # Finds pyproject.toml or setup.py from running script
|
|
36
|
+
|
|
37
|
+
>>> root = find_project_root(Path(__file__).parent)
|
|
38
|
+
>>> # Finds from specific starting path
|
|
39
|
+
"""
|
|
40
|
+
start_paths = []
|
|
41
|
+
|
|
42
|
+
# If explicit start path provided, use it first
|
|
43
|
+
if start_path:
|
|
44
|
+
try:
|
|
45
|
+
start_paths.append(start_path.resolve())
|
|
46
|
+
except (ValueError, OSError):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# Option 1: Use __main__ module location if available
|
|
50
|
+
if '__main__' in sys.modules:
|
|
51
|
+
main_module = sys.modules['__main__']
|
|
52
|
+
if hasattr(main_module, '__file__') and main_module.__file__:
|
|
53
|
+
try:
|
|
54
|
+
main_path = Path(main_module.__file__).resolve().parent
|
|
55
|
+
start_paths.append(main_path)
|
|
56
|
+
except (ValueError, OSError):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
# Option 2: Use sys.path[0] (script directory)
|
|
60
|
+
if sys.path and sys.path[0]:
|
|
61
|
+
try:
|
|
62
|
+
script_path = Path(sys.path[0]).resolve()
|
|
63
|
+
if script_path.exists():
|
|
64
|
+
start_paths.append(script_path)
|
|
65
|
+
except (ValueError, OSError):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
# Option 3: Use current working directory
|
|
69
|
+
try:
|
|
70
|
+
cwd = Path.cwd().resolve()
|
|
71
|
+
if cwd.exists():
|
|
72
|
+
start_paths.append(cwd)
|
|
73
|
+
except (OSError, ValueError):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# Option 4: Fallback to xwlazy location (for backwards compatibility)
|
|
77
|
+
try:
|
|
78
|
+
xwlazy_path = Path(__file__).parent.parent.parent.parent
|
|
79
|
+
if xwlazy_path.exists():
|
|
80
|
+
start_paths.append(xwlazy_path)
|
|
81
|
+
except (ValueError, OSError):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
# Try each starting path
|
|
85
|
+
for start_path_item in start_paths:
|
|
86
|
+
current = start_path_item
|
|
87
|
+
max_levels = 20 # Prevent infinite loops
|
|
88
|
+
levels = 0
|
|
89
|
+
|
|
90
|
+
while current != current.parent and levels < max_levels:
|
|
91
|
+
# Check for project markers
|
|
92
|
+
markers = ['pyproject.toml', 'setup.py', 'requirements.txt', '.git']
|
|
93
|
+
if any((current / marker).exists() for marker in markers):
|
|
94
|
+
return current
|
|
95
|
+
current = current.parent
|
|
96
|
+
levels += 1
|
|
97
|
+
|
|
98
|
+
# Final fallback: current working directory
|
|
99
|
+
try:
|
|
100
|
+
return Path.cwd().resolve()
|
|
101
|
+
except (OSError, ValueError):
|
|
102
|
+
# Ultimate fallback: return the xwlazy package directory
|
|
103
|
+
return Path(__file__).parent.parent.parent.parent
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def find_config_file(filename: str, start_path: Optional[Path] = None) -> Optional[Path]:
|
|
107
|
+
"""
|
|
108
|
+
Find a configuration file by walking up from the start path.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
filename: Name of the config file to find (e.g., 'pyproject.toml', 'requirements.txt')
|
|
112
|
+
start_path: Optional starting path. If None, uses find_project_root()
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Path to the config file if found, None otherwise
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
>>> pyproject = find_config_file('pyproject.toml')
|
|
119
|
+
>>> requirements = find_config_file('requirements.txt')
|
|
120
|
+
"""
|
|
121
|
+
if start_path is None:
|
|
122
|
+
root = find_project_root()
|
|
123
|
+
else:
|
|
124
|
+
root = start_path
|
|
125
|
+
|
|
126
|
+
# Check in root and walk up
|
|
127
|
+
current = root
|
|
128
|
+
max_levels = 10
|
|
129
|
+
levels = 0
|
|
130
|
+
|
|
131
|
+
while current != current.parent and levels < max_levels:
|
|
132
|
+
config_path = current / filename
|
|
133
|
+
if config_path.exists():
|
|
134
|
+
return config_path
|
|
135
|
+
current = current.parent
|
|
136
|
+
levels += 1
|
|
137
|
+
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = ['find_project_root', 'find_config_file']
|
|
142
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""
|
|
2
|
+
#exonware/xwlazy/src/exonware/xwlazy/config.py
|
|
3
|
+
|
|
4
|
+
Company: eXonware.com
|
|
5
|
+
Author: Eng. Muhammad AlShehri
|
|
6
|
+
Email: connect@exonware.com
|
|
7
|
+
|
|
8
|
+
Generation Date: 10-Oct-2025
|
|
9
|
+
|
|
10
|
+
Configuration for Lazy Loading System
|
|
11
|
+
|
|
12
|
+
This module defines configuration classes for the lazy loading system
|
|
13
|
+
following GUIDE_ARCH.md structure.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Optional, Any
|
|
19
|
+
|
|
20
|
+
# Import LazyConfig dataclass from defs.py
|
|
21
|
+
from .defs import LazyConfig as _LazyConfigBase
|
|
22
|
+
|
|
23
|
+
# Extend LazyConfig with methods (dataclass is in defs.py)
|
|
24
|
+
class LazyConfig(_LazyConfigBase):
|
|
25
|
+
"""Bridge configuration settings with the lazy package implementation."""
|
|
26
|
+
|
|
27
|
+
def __post_init__(self) -> None:
|
|
28
|
+
"""Normalize package names."""
|
|
29
|
+
super().__post_init__()
|
|
30
|
+
|
|
31
|
+
# High-level API -----------------------------------------------------
|
|
32
|
+
@property
|
|
33
|
+
def lazy_import(self) -> bool:
|
|
34
|
+
"""Return whether lazy mode is currently active."""
|
|
35
|
+
# Import from facade
|
|
36
|
+
from .facade import is_lazy_mode_enabled
|
|
37
|
+
return is_lazy_mode_enabled()
|
|
38
|
+
|
|
39
|
+
@lazy_import.setter
|
|
40
|
+
def lazy_import(self, value: bool) -> None:
|
|
41
|
+
self.set_lazy_import(bool(value))
|
|
42
|
+
|
|
43
|
+
def set_lazy_import(
|
|
44
|
+
self,
|
|
45
|
+
enabled: bool,
|
|
46
|
+
*,
|
|
47
|
+
lazy_imports: bool = True,
|
|
48
|
+
lazy_install: bool = True,
|
|
49
|
+
install_hook: bool = True,
|
|
50
|
+
mode: str = "auto",
|
|
51
|
+
) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Toggle lazy mode with optional fine-grained controls.
|
|
54
|
+
|
|
55
|
+
Includes re-hooking support: If lazy is enabled and install_hook is True,
|
|
56
|
+
ensures the import hook is installed even if it wasn't installed initially.
|
|
57
|
+
"""
|
|
58
|
+
# Import from facade
|
|
59
|
+
from .facade import (
|
|
60
|
+
config_package_lazy_install_enabled,
|
|
61
|
+
disable_lazy_mode,
|
|
62
|
+
enable_lazy_mode,
|
|
63
|
+
is_import_hook_installed,
|
|
64
|
+
install_import_hook,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if enabled:
|
|
68
|
+
self._configure_packages(True, mode=mode, install_hook=install_hook)
|
|
69
|
+
enable_lazy_mode(
|
|
70
|
+
package_name=self.packages[0],
|
|
71
|
+
enable_lazy_imports=lazy_imports,
|
|
72
|
+
enable_lazy_install=lazy_install,
|
|
73
|
+
install_hook=install_hook,
|
|
74
|
+
lazy_install_mode=mode,
|
|
75
|
+
)
|
|
76
|
+
# Re-hook: Install hook if lazy is enabled and hook not already installed
|
|
77
|
+
# Root cause: Hook not installed when lazy enabled after package load
|
|
78
|
+
# Priority impact: Usability (#2) - Users expect lazy to work when enabled
|
|
79
|
+
if install_hook:
|
|
80
|
+
self._ensure_hook_installed()
|
|
81
|
+
else:
|
|
82
|
+
disable_lazy_mode()
|
|
83
|
+
self._configure_packages(False, install_hook=False)
|
|
84
|
+
|
|
85
|
+
def enable(
|
|
86
|
+
self,
|
|
87
|
+
*,
|
|
88
|
+
lazy_imports: bool = True,
|
|
89
|
+
lazy_install: bool = True,
|
|
90
|
+
install_hook: bool = True,
|
|
91
|
+
mode: str = "auto",
|
|
92
|
+
) -> None:
|
|
93
|
+
"""Enable lazy mode using the provided options."""
|
|
94
|
+
self.set_lazy_import(
|
|
95
|
+
True,
|
|
96
|
+
lazy_imports=lazy_imports,
|
|
97
|
+
lazy_install=lazy_install,
|
|
98
|
+
install_hook=install_hook,
|
|
99
|
+
mode=mode,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def disable(self) -> None:
|
|
103
|
+
"""Disable lazy mode entirely."""
|
|
104
|
+
self.set_lazy_import(False)
|
|
105
|
+
|
|
106
|
+
# DX: Status check methods -------------------------------------------
|
|
107
|
+
def get_lazy_status(self) -> dict:
|
|
108
|
+
"""
|
|
109
|
+
Get detailed lazy installation status (DX enhancement).
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dictionary with lazy mode status information
|
|
113
|
+
"""
|
|
114
|
+
# Import from facade
|
|
115
|
+
from .facade import (
|
|
116
|
+
is_import_hook_installed,
|
|
117
|
+
is_lazy_install_enabled,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
122
|
+
return {
|
|
123
|
+
'enabled': self.lazy_import,
|
|
124
|
+
'hook_installed': is_import_hook_installed(primary_package),
|
|
125
|
+
'lazy_install_enabled': is_lazy_install_enabled(primary_package),
|
|
126
|
+
'active': self.lazy_import and is_import_hook_installed(primary_package)
|
|
127
|
+
}
|
|
128
|
+
except Exception:
|
|
129
|
+
return {
|
|
130
|
+
'enabled': self.lazy_import,
|
|
131
|
+
'hook_installed': False,
|
|
132
|
+
'lazy_install_enabled': False,
|
|
133
|
+
'active': False,
|
|
134
|
+
'error': 'Could not check hook status'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def is_lazy_active(self) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Check if lazy mode is active (DX enhancement).
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
True if lazy mode is enabled and hook is installed
|
|
143
|
+
"""
|
|
144
|
+
# Import from facade
|
|
145
|
+
from .facade import is_import_hook_installed
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
149
|
+
return self.lazy_import and is_import_hook_installed(primary_package)
|
|
150
|
+
except Exception:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
# Internal helpers ---------------------------------------------------
|
|
154
|
+
def _configure_packages(
|
|
155
|
+
self,
|
|
156
|
+
enabled: bool,
|
|
157
|
+
*,
|
|
158
|
+
mode: str = "auto",
|
|
159
|
+
install_hook: bool = True,
|
|
160
|
+
) -> None:
|
|
161
|
+
# Import from facade
|
|
162
|
+
from .facade import config_package_lazy_install_enabled
|
|
163
|
+
|
|
164
|
+
for package in self.packages:
|
|
165
|
+
config_package_lazy_install_enabled(
|
|
166
|
+
package,
|
|
167
|
+
enabled,
|
|
168
|
+
mode,
|
|
169
|
+
install_hook=install_hook,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def _ensure_hook_installed(self) -> None:
|
|
173
|
+
"""
|
|
174
|
+
Ensure import hook is installed for primary package.
|
|
175
|
+
|
|
176
|
+
Re-hooking support: Install hook if not already installed.
|
|
177
|
+
"""
|
|
178
|
+
# Import from facade
|
|
179
|
+
from .facade import (
|
|
180
|
+
is_import_hook_installed,
|
|
181
|
+
install_import_hook,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
186
|
+
if not is_import_hook_installed(primary_package):
|
|
187
|
+
install_import_hook(primary_package)
|
|
188
|
+
except Exception:
|
|
189
|
+
# Fail silently - hook installation failure shouldn't break package
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
DEFAULT_LAZY_CONFIG = LazyConfig()
|
|
193
|
+
|