exonware-xwlazy 0.1.0.10__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.
- exonware/__init__.py +22 -0
- exonware/xwlazy/__init__.py +0 -0
- exonware/xwlazy/common/__init__.py +47 -0
- exonware/xwlazy/common/base.py +58 -0
- exonware/xwlazy/common/cache.py +506 -0
- exonware/xwlazy/common/logger.py +268 -0
- exonware/xwlazy/common/services/__init__.py +72 -0
- exonware/xwlazy/common/services/dependency_mapper.py +234 -0
- exonware/xwlazy/common/services/install_async_utils.py +169 -0
- exonware/xwlazy/common/services/install_cache_utils.py +257 -0
- exonware/xwlazy/common/services/keyword_detection.py +292 -0
- exonware/xwlazy/common/services/spec_cache.py +173 -0
- exonware/xwlazy/common/services/state_manager.py +86 -0
- exonware/xwlazy/common/strategies/__init__.py +28 -0
- exonware/xwlazy/common/strategies/caching_dict.py +45 -0
- exonware/xwlazy/common/strategies/caching_installation.py +89 -0
- exonware/xwlazy/common/strategies/caching_lfu.py +67 -0
- exonware/xwlazy/common/strategies/caching_lru.py +64 -0
- exonware/xwlazy/common/strategies/caching_multitier.py +60 -0
- exonware/xwlazy/common/strategies/caching_ttl.py +60 -0
- exonware/xwlazy/config.py +195 -0
- exonware/xwlazy/contracts.py +1410 -0
- exonware/xwlazy/defs.py +397 -0
- exonware/xwlazy/errors.py +284 -0
- exonware/xwlazy/facade.py +1049 -0
- exonware/xwlazy/module/__init__.py +18 -0
- exonware/xwlazy/module/base.py +569 -0
- exonware/xwlazy/module/data.py +17 -0
- exonware/xwlazy/module/facade.py +247 -0
- exonware/xwlazy/module/importer_engine.py +2161 -0
- exonware/xwlazy/module/strategies/__init__.py +22 -0
- exonware/xwlazy/module/strategies/module_helper_lazy.py +94 -0
- exonware/xwlazy/module/strategies/module_helper_simple.py +66 -0
- exonware/xwlazy/module/strategies/module_manager_advanced.py +112 -0
- exonware/xwlazy/module/strategies/module_manager_simple.py +96 -0
- exonware/xwlazy/package/__init__.py +18 -0
- exonware/xwlazy/package/base.py +807 -0
- exonware/xwlazy/package/conf.py +331 -0
- exonware/xwlazy/package/data.py +17 -0
- exonware/xwlazy/package/facade.py +481 -0
- exonware/xwlazy/package/services/__init__.py +84 -0
- exonware/xwlazy/package/services/async_install_handle.py +89 -0
- exonware/xwlazy/package/services/config_manager.py +246 -0
- exonware/xwlazy/package/services/discovery.py +374 -0
- exonware/xwlazy/package/services/host_packages.py +149 -0
- exonware/xwlazy/package/services/install_async.py +278 -0
- exonware/xwlazy/package/services/install_cache.py +146 -0
- exonware/xwlazy/package/services/install_interactive.py +60 -0
- exonware/xwlazy/package/services/install_policy.py +158 -0
- exonware/xwlazy/package/services/install_registry.py +56 -0
- exonware/xwlazy/package/services/install_result.py +17 -0
- exonware/xwlazy/package/services/install_sbom.py +154 -0
- exonware/xwlazy/package/services/install_utils.py +83 -0
- exonware/xwlazy/package/services/installer_engine.py +408 -0
- exonware/xwlazy/package/services/lazy_installer.py +720 -0
- exonware/xwlazy/package/services/manifest.py +506 -0
- exonware/xwlazy/package/services/strategy_registry.py +188 -0
- exonware/xwlazy/package/strategies/__init__.py +57 -0
- exonware/xwlazy/package/strategies/package_discovery_file.py +130 -0
- exonware/xwlazy/package/strategies/package_discovery_hybrid.py +85 -0
- exonware/xwlazy/package/strategies/package_discovery_manifest.py +102 -0
- exonware/xwlazy/package/strategies/package_execution_async.py +114 -0
- exonware/xwlazy/package/strategies/package_execution_cached.py +91 -0
- exonware/xwlazy/package/strategies/package_execution_pip.py +100 -0
- exonware/xwlazy/package/strategies/package_execution_wheel.py +107 -0
- exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +101 -0
- exonware/xwlazy/package/strategies/package_mapping_hybrid.py +106 -0
- exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +101 -0
- exonware/xwlazy/package/strategies/package_policy_allow_list.py +58 -0
- exonware/xwlazy/package/strategies/package_policy_deny_list.py +58 -0
- exonware/xwlazy/package/strategies/package_policy_permissive.py +47 -0
- exonware/xwlazy/package/strategies/package_timing_clean.py +68 -0
- exonware/xwlazy/package/strategies/package_timing_full.py +67 -0
- exonware/xwlazy/package/strategies/package_timing_smart.py +69 -0
- exonware/xwlazy/package/strategies/package_timing_temporary.py +67 -0
- exonware/xwlazy/runtime/__init__.py +18 -0
- exonware/xwlazy/runtime/adaptive_learner.py +131 -0
- exonware/xwlazy/runtime/base.py +276 -0
- exonware/xwlazy/runtime/facade.py +95 -0
- exonware/xwlazy/runtime/intelligent_selector.py +173 -0
- exonware/xwlazy/runtime/metrics.py +64 -0
- exonware/xwlazy/runtime/performance.py +39 -0
- exonware/xwlazy/version.py +2 -2
- exonware_xwlazy-0.1.0.19.dist-info/METADATA +456 -0
- exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
- exonware_xwlazy-0.1.0.10.dist-info/METADATA +0 -0
- exonware_xwlazy-0.1.0.10.dist-info/RECORD +0 -6
- {exonware_xwlazy-0.1.0.10.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.10.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Installation Cache Strategy - Wrapper for existing InstallationCache.
|
|
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
|
+
Wraps existing InstallationCache to implement ICachingStrategy interface.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional, Any
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from ..cache import InstallationCache
|
|
16
|
+
from ..base import ACachingStrategy
|
|
17
|
+
from ...package.data import PackageData
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InstallationCacheWrapper(ACachingStrategy):
|
|
21
|
+
"""
|
|
22
|
+
Installation cache strategy wrapper.
|
|
23
|
+
|
|
24
|
+
Wraps existing InstallationCache to implement ICachingStrategy interface.
|
|
25
|
+
Used for package installation status caching.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, cache_file: Optional[Path] = None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize installation cache wrapper.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
cache_file: Optional path to cache file
|
|
34
|
+
"""
|
|
35
|
+
self._cache = InstallationCache(cache_file)
|
|
36
|
+
|
|
37
|
+
def get(self, key: str) -> Optional[Any]:
|
|
38
|
+
"""
|
|
39
|
+
Get package from cache.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
key: Package name
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
PackageData if found, None otherwise
|
|
46
|
+
"""
|
|
47
|
+
if self._cache.is_installed(key):
|
|
48
|
+
version = self._cache.get_version(key)
|
|
49
|
+
return PackageData(
|
|
50
|
+
name=key,
|
|
51
|
+
installed=True,
|
|
52
|
+
version=version
|
|
53
|
+
)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def set(self, key: str, value: Any) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Cache a package.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
key: Package name
|
|
62
|
+
value: PackageData or dict with installed/version info
|
|
63
|
+
"""
|
|
64
|
+
if isinstance(value, PackageData):
|
|
65
|
+
if value.installed:
|
|
66
|
+
self._cache.mark_installed(key, value.version)
|
|
67
|
+
else:
|
|
68
|
+
self._cache.mark_uninstalled(key)
|
|
69
|
+
elif isinstance(value, dict):
|
|
70
|
+
if value.get('installed', False):
|
|
71
|
+
self._cache.mark_installed(key, value.get('version'))
|
|
72
|
+
else:
|
|
73
|
+
self._cache.mark_uninstalled(key)
|
|
74
|
+
|
|
75
|
+
def invalidate(self, key: str) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Invalidate cached package.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
key: Package name
|
|
81
|
+
"""
|
|
82
|
+
self._cache.mark_uninstalled(key)
|
|
83
|
+
|
|
84
|
+
def clear(self) -> None:
|
|
85
|
+
"""Clear all cached packages."""
|
|
86
|
+
# InstallationCache doesn't have clear, so we'd need to extend it
|
|
87
|
+
# For now, just mark all as uninstalled (would need cache iteration)
|
|
88
|
+
pass
|
|
89
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LFU Cache Strategy - Least Frequently Used eviction.
|
|
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
|
+
LFU cache implementation with size limit.
|
|
11
|
+
Works with ANY data type (modules, packages, etc.).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Dict, Optional, Any
|
|
15
|
+
from collections import Counter
|
|
16
|
+
from ...common.base import ACachingStrategy
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LFUCache(ACachingStrategy):
|
|
20
|
+
"""
|
|
21
|
+
LFU (Least Frequently Used) cache with size limit.
|
|
22
|
+
|
|
23
|
+
Evicts least frequently accessed items when cache is full.
|
|
24
|
+
Good for access pattern-based caching.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, max_size: int = 1000):
|
|
28
|
+
"""
|
|
29
|
+
Initialize LFU cache.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
max_size: Maximum number of items in cache
|
|
33
|
+
"""
|
|
34
|
+
self._cache: Dict[str, Any] = {}
|
|
35
|
+
self._freq: Counter[str] = Counter()
|
|
36
|
+
self._max_size = max_size
|
|
37
|
+
|
|
38
|
+
def get(self, key: str) -> Optional[Any]:
|
|
39
|
+
"""Get value from cache (increments frequency)."""
|
|
40
|
+
if key in self._cache:
|
|
41
|
+
self._freq[key] += 1
|
|
42
|
+
return self._cache[key]
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def set(self, key: str, value: Any) -> None:
|
|
46
|
+
"""Set value in cache (evicts least frequent if full)."""
|
|
47
|
+
if key not in self._cache and len(self._cache) >= self._max_size:
|
|
48
|
+
# Evict least frequent
|
|
49
|
+
if self._freq:
|
|
50
|
+
least_frequent = min(self._freq.items(), key=lambda x: x[1])[0]
|
|
51
|
+
self._cache.pop(least_frequent, None)
|
|
52
|
+
self._freq.pop(least_frequent, None)
|
|
53
|
+
|
|
54
|
+
self._cache[key] = value
|
|
55
|
+
if key not in self._freq:
|
|
56
|
+
self._freq[key] = 0
|
|
57
|
+
|
|
58
|
+
def invalidate(self, key: str) -> None:
|
|
59
|
+
"""Invalidate cached value."""
|
|
60
|
+
self._cache.pop(key, None)
|
|
61
|
+
self._freq.pop(key, None)
|
|
62
|
+
|
|
63
|
+
def clear(self) -> None:
|
|
64
|
+
"""Clear all cached values."""
|
|
65
|
+
self._cache.clear()
|
|
66
|
+
self._freq.clear()
|
|
67
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LRU Cache Strategy - Least Recently Used eviction.
|
|
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
|
+
LRU cache implementation with size limit.
|
|
11
|
+
Works with ANY data type (modules, packages, etc.).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Optional, Any
|
|
15
|
+
from collections import OrderedDict
|
|
16
|
+
from ...common.base import ACachingStrategy
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LRUCache(ACachingStrategy):
|
|
20
|
+
"""
|
|
21
|
+
LRU (Least Recently Used) cache with size limit.
|
|
22
|
+
|
|
23
|
+
Evicts least recently used items when cache is full.
|
|
24
|
+
Good for general-purpose caching.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, max_size: int = 1000):
|
|
28
|
+
"""
|
|
29
|
+
Initialize LRU cache.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
max_size: Maximum number of items in cache
|
|
33
|
+
"""
|
|
34
|
+
self._cache: OrderedDict[str, Any] = OrderedDict()
|
|
35
|
+
self._max_size = max_size
|
|
36
|
+
|
|
37
|
+
def get(self, key: str) -> Optional[Any]:
|
|
38
|
+
"""Get value from cache (moves to end for LRU)."""
|
|
39
|
+
if key in self._cache:
|
|
40
|
+
# Move to end (most recently used)
|
|
41
|
+
self._cache.move_to_end(key)
|
|
42
|
+
return self._cache[key]
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def set(self, key: str, value: Any) -> None:
|
|
46
|
+
"""Set value in cache (evicts oldest if full)."""
|
|
47
|
+
if key in self._cache:
|
|
48
|
+
# Update existing - move to end
|
|
49
|
+
self._cache.move_to_end(key)
|
|
50
|
+
else:
|
|
51
|
+
# New entry - check size limit
|
|
52
|
+
if len(self._cache) >= self._max_size:
|
|
53
|
+
# Remove oldest (first item)
|
|
54
|
+
self._cache.popitem(last=False)
|
|
55
|
+
self._cache[key] = value
|
|
56
|
+
|
|
57
|
+
def invalidate(self, key: str) -> None:
|
|
58
|
+
"""Invalidate cached value."""
|
|
59
|
+
self._cache.pop(key, None)
|
|
60
|
+
|
|
61
|
+
def clear(self) -> None:
|
|
62
|
+
"""Clear all cached values."""
|
|
63
|
+
self._cache.clear()
|
|
64
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Tier Cache Strategy - Wrapper for existing MultiTierCache.
|
|
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
|
+
Wraps existing MultiTierCache to implement ICaching interface.
|
|
11
|
+
Works with ANY data type (modules, packages, etc.).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Optional, Any
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from ...common.cache import MultiTierCache
|
|
17
|
+
from ...common.base import ACachingStrategy
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MultiTierCacheStrategy(ACachingStrategy):
|
|
21
|
+
"""
|
|
22
|
+
Multi-tier cache strategy (L1 memory + L2 disk + L3 predictive).
|
|
23
|
+
|
|
24
|
+
Wraps existing MultiTierCache to implement ICachingStrategy interface.
|
|
25
|
+
High-performance caching with multiple tiers.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, l1_size: int = 1000, l2_dir: Optional[Path] = None, enable_l3: bool = True):
|
|
29
|
+
"""
|
|
30
|
+
Initialize multi-tier cache.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
l1_size: Maximum size of L1 (memory) cache
|
|
34
|
+
l2_dir: Directory for L2 (disk) cache
|
|
35
|
+
enable_l3: Enable L3 (predictive) cache
|
|
36
|
+
"""
|
|
37
|
+
self._cache = MultiTierCache(l1_size=l1_size, l2_dir=l2_dir, enable_l3=enable_l3)
|
|
38
|
+
|
|
39
|
+
def get(self, key: str) -> Optional[Any]:
|
|
40
|
+
"""Get value from cache (L1 -> L2 -> L3)."""
|
|
41
|
+
return self._cache.get(key)
|
|
42
|
+
|
|
43
|
+
def set(self, key: str, value: Any) -> None:
|
|
44
|
+
"""Set value in cache (L1 + L2 batched)."""
|
|
45
|
+
self._cache.set(key, value)
|
|
46
|
+
|
|
47
|
+
def invalidate(self, key: str) -> None:
|
|
48
|
+
"""Invalidate cached value."""
|
|
49
|
+
# MultiTierCache doesn't have invalidate, so we set to None
|
|
50
|
+
# Or we could extend MultiTierCache to add invalidate
|
|
51
|
+
self._cache.set(key, None)
|
|
52
|
+
|
|
53
|
+
def clear(self) -> None:
|
|
54
|
+
"""Clear all cached values."""
|
|
55
|
+
self._cache.clear()
|
|
56
|
+
|
|
57
|
+
def shutdown(self) -> None:
|
|
58
|
+
"""Shutdown cache (flush L2, cleanup threads)."""
|
|
59
|
+
self._cache.shutdown()
|
|
60
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TTL Cache Strategy - Time-To-Live expiration.
|
|
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
|
+
TTL cache implementation with expiration.
|
|
11
|
+
Works with ANY data type (modules, packages, etc.).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import time
|
|
15
|
+
from typing import Dict, Optional, Any, Tuple
|
|
16
|
+
from ...common.base import ACachingStrategy
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TTLCache(ACachingStrategy):
|
|
20
|
+
"""
|
|
21
|
+
TTL (Time-To-Live) cache with expiration.
|
|
22
|
+
|
|
23
|
+
Automatically expires entries after TTL seconds.
|
|
24
|
+
Good for time-sensitive data.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, ttl_seconds: float = 3600.0):
|
|
28
|
+
"""
|
|
29
|
+
Initialize TTL cache.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
ttl_seconds: Time-to-live in seconds (default: 1 hour)
|
|
33
|
+
"""
|
|
34
|
+
self._cache: Dict[str, Tuple[Any, float]] = {} # (value, expiry_time)
|
|
35
|
+
self._ttl = ttl_seconds
|
|
36
|
+
|
|
37
|
+
def get(self, key: str) -> Optional[Any]:
|
|
38
|
+
"""Get value from cache (returns None if expired)."""
|
|
39
|
+
if key in self._cache:
|
|
40
|
+
value, expiry = self._cache[key]
|
|
41
|
+
if time.time() < expiry:
|
|
42
|
+
return value
|
|
43
|
+
else:
|
|
44
|
+
# Expired - remove
|
|
45
|
+
del self._cache[key]
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def set(self, key: str, value: Any) -> None:
|
|
49
|
+
"""Set value in cache with TTL."""
|
|
50
|
+
expiry = time.time() + self._ttl
|
|
51
|
+
self._cache[key] = (value, expiry)
|
|
52
|
+
|
|
53
|
+
def invalidate(self, key: str) -> None:
|
|
54
|
+
"""Invalidate cached value."""
|
|
55
|
+
self._cache.pop(key, None)
|
|
56
|
+
|
|
57
|
+
def clear(self) -> None:
|
|
58
|
+
"""Clear all cached values."""
|
|
59
|
+
self._cache.clear()
|
|
60
|
+
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
Version: 0.1.0.19
|
|
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, Dict
|
|
19
|
+
|
|
20
|
+
# Import LazyConfig dataclass from defs.py
|
|
21
|
+
from .defs import LazyConfig as _LazyConfigBase
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Extend LazyConfig with methods (dataclass is in defs.py)
|
|
25
|
+
class LazyConfig(_LazyConfigBase):
|
|
26
|
+
"""Bridge configuration settings with the lazy package implementation."""
|
|
27
|
+
|
|
28
|
+
def __post_init__(self) -> None:
|
|
29
|
+
"""Normalize package names."""
|
|
30
|
+
super().__post_init__()
|
|
31
|
+
|
|
32
|
+
# High-level API -----------------------------------------------------
|
|
33
|
+
@property
|
|
34
|
+
def lazy_import(self) -> bool:
|
|
35
|
+
"""Return whether lazy mode is currently active."""
|
|
36
|
+
# Import from facade
|
|
37
|
+
from .facade import is_lazy_mode_enabled
|
|
38
|
+
return is_lazy_mode_enabled()
|
|
39
|
+
|
|
40
|
+
@lazy_import.setter
|
|
41
|
+
def lazy_import(self, value: bool) -> None:
|
|
42
|
+
self.set_lazy_import(bool(value))
|
|
43
|
+
|
|
44
|
+
def set_lazy_import(
|
|
45
|
+
self,
|
|
46
|
+
enabled: bool,
|
|
47
|
+
*,
|
|
48
|
+
lazy_imports: bool = True,
|
|
49
|
+
lazy_install: bool = True,
|
|
50
|
+
install_hook: bool = True,
|
|
51
|
+
mode: str = "auto",
|
|
52
|
+
) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Toggle lazy mode with optional fine-grained controls.
|
|
55
|
+
|
|
56
|
+
Includes re-hooking support: If lazy is enabled and install_hook is True,
|
|
57
|
+
ensures the import hook is installed even if it wasn't installed initially.
|
|
58
|
+
"""
|
|
59
|
+
# Import from facade
|
|
60
|
+
from .facade import (
|
|
61
|
+
config_package_lazy_install_enabled,
|
|
62
|
+
disable_lazy_mode,
|
|
63
|
+
enable_lazy_mode,
|
|
64
|
+
is_import_hook_installed,
|
|
65
|
+
install_import_hook,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if enabled:
|
|
69
|
+
self._configure_packages(True, mode=mode, install_hook=install_hook)
|
|
70
|
+
enable_lazy_mode(
|
|
71
|
+
package_name=self.packages[0],
|
|
72
|
+
enable_lazy_imports=lazy_imports,
|
|
73
|
+
enable_lazy_install=lazy_install,
|
|
74
|
+
install_hook=install_hook,
|
|
75
|
+
lazy_install_mode=mode,
|
|
76
|
+
)
|
|
77
|
+
# Re-hook: Install hook if lazy is enabled and hook not already installed
|
|
78
|
+
# Root cause: Hook not installed when lazy enabled after package load
|
|
79
|
+
# Priority impact: Usability (#2) - Users expect lazy to work when enabled
|
|
80
|
+
if install_hook:
|
|
81
|
+
self._ensure_hook_installed()
|
|
82
|
+
else:
|
|
83
|
+
disable_lazy_mode()
|
|
84
|
+
self._configure_packages(False, install_hook=False)
|
|
85
|
+
|
|
86
|
+
def enable(
|
|
87
|
+
self,
|
|
88
|
+
*,
|
|
89
|
+
lazy_imports: bool = True,
|
|
90
|
+
lazy_install: bool = True,
|
|
91
|
+
install_hook: bool = True,
|
|
92
|
+
mode: str = "auto",
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Enable lazy mode using the provided options."""
|
|
95
|
+
self.set_lazy_import(
|
|
96
|
+
True,
|
|
97
|
+
lazy_imports=lazy_imports,
|
|
98
|
+
lazy_install=lazy_install,
|
|
99
|
+
install_hook=install_hook,
|
|
100
|
+
mode=mode,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def disable(self) -> None:
|
|
104
|
+
"""Disable lazy mode entirely."""
|
|
105
|
+
self.set_lazy_import(False)
|
|
106
|
+
|
|
107
|
+
# DX: Status check methods -------------------------------------------
|
|
108
|
+
def get_lazy_status(self) -> dict:
|
|
109
|
+
"""
|
|
110
|
+
Get detailed lazy installation status (DX enhancement).
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with lazy mode status information
|
|
114
|
+
"""
|
|
115
|
+
# Import from facade
|
|
116
|
+
from .facade import (
|
|
117
|
+
is_import_hook_installed,
|
|
118
|
+
is_lazy_install_enabled,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
123
|
+
return {
|
|
124
|
+
'enabled': self.lazy_import,
|
|
125
|
+
'hook_installed': is_import_hook_installed(primary_package),
|
|
126
|
+
'lazy_install_enabled': is_lazy_install_enabled(primary_package),
|
|
127
|
+
'active': self.lazy_import and is_import_hook_installed(primary_package)
|
|
128
|
+
}
|
|
129
|
+
except Exception:
|
|
130
|
+
return {
|
|
131
|
+
'enabled': self.lazy_import,
|
|
132
|
+
'hook_installed': False,
|
|
133
|
+
'lazy_install_enabled': False,
|
|
134
|
+
'active': False,
|
|
135
|
+
'error': 'Could not check hook status'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
def is_lazy_active(self) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Check if lazy mode is active (DX enhancement).
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if lazy mode is enabled and hook is installed
|
|
144
|
+
"""
|
|
145
|
+
# Import from facade
|
|
146
|
+
from .facade import is_import_hook_installed
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
150
|
+
return self.lazy_import and is_import_hook_installed(primary_package)
|
|
151
|
+
except Exception:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
# Internal helpers ---------------------------------------------------
|
|
155
|
+
def _configure_packages(
|
|
156
|
+
self,
|
|
157
|
+
enabled: bool,
|
|
158
|
+
*,
|
|
159
|
+
mode: str = "auto",
|
|
160
|
+
install_hook: bool = True,
|
|
161
|
+
) -> None:
|
|
162
|
+
# Import from facade
|
|
163
|
+
from .facade import config_package_lazy_install_enabled
|
|
164
|
+
|
|
165
|
+
for package in self.packages:
|
|
166
|
+
config_package_lazy_install_enabled(
|
|
167
|
+
package,
|
|
168
|
+
enabled,
|
|
169
|
+
mode,
|
|
170
|
+
install_hook=install_hook,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def _ensure_hook_installed(self) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Ensure import hook is installed for primary package.
|
|
176
|
+
|
|
177
|
+
Re-hooking support: Install hook if not already installed.
|
|
178
|
+
"""
|
|
179
|
+
# Import from facade
|
|
180
|
+
from .facade import (
|
|
181
|
+
is_import_hook_installed,
|
|
182
|
+
install_import_hook,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
187
|
+
if not is_import_hook_installed(primary_package):
|
|
188
|
+
install_import_hook(primary_package)
|
|
189
|
+
except Exception:
|
|
190
|
+
# Fail silently - hook installation failure shouldn't break package
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
DEFAULT_LAZY_CONFIG = LazyConfig()
|
|
195
|
+
|