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.
- 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/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
- {xwlazy/lazy → exonware/xwlazy}/config.py +52 -20
- exonware/xwlazy/contracts.py +1410 -0
- exonware/xwlazy/defs.py +397 -0
- xwlazy/lazy/lazy_errors.py → exonware/xwlazy/errors.py +21 -8
- 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
- xwlazy/lazy/host_conf.py → exonware/xwlazy/package/conf.py +62 -10
- 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
- {xwlazy/lazy → exonware/xwlazy/package/services}/host_packages.py +43 -16
- 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
- {xwlazy/lazy → exonware/xwlazy/package/services}/manifest.py +42 -25
- 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.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/METADATA +86 -10
- exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
- exonware_xwlazy-0.1.0.11.dist-info/RECORD +0 -20
- xwlazy/__init__.py +0 -34
- xwlazy/lazy/__init__.py +0 -301
- xwlazy/lazy/bootstrap.py +0 -106
- xwlazy/lazy/lazy_base.py +0 -465
- xwlazy/lazy/lazy_contracts.py +0 -290
- xwlazy/lazy/lazy_core.py +0 -3727
- xwlazy/lazy/logging_utils.py +0 -194
- xwlazy/version.py +0 -77
- /xwlazy/lazy/lazy_state.py → /exonware/xwlazy/common/services/state_manager.py +0 -0
- {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manifest-First Mapping Strategy
|
|
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
|
+
Manifest-first mapping strategy - manifest takes precedence over discovery.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional, List
|
|
14
|
+
from ...package.base import AMappingStrategy
|
|
15
|
+
from ...package.services.manifest import get_manifest_loader
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ManifestFirstMapping(AMappingStrategy):
|
|
19
|
+
"""
|
|
20
|
+
Manifest-first mapping strategy.
|
|
21
|
+
|
|
22
|
+
Priority order:
|
|
23
|
+
1. Manifest dependencies (explicit user configuration - highest priority)
|
|
24
|
+
2. Discovery mappings (automatic discovery)
|
|
25
|
+
3. Common mappings (fallback)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, package_name: str = 'default'):
|
|
29
|
+
"""
|
|
30
|
+
Initialize manifest-first mapping strategy.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
package_name: Package name for isolation
|
|
34
|
+
"""
|
|
35
|
+
self._package_name = package_name
|
|
36
|
+
self._discovery = None # Lazy init
|
|
37
|
+
|
|
38
|
+
def _get_discovery(self):
|
|
39
|
+
"""Get discovery instance (lazy init)."""
|
|
40
|
+
if self._discovery is None:
|
|
41
|
+
from ...package.services.discovery import LazyDiscovery
|
|
42
|
+
self._discovery = LazyDiscovery(self._package_name)
|
|
43
|
+
return self._discovery
|
|
44
|
+
|
|
45
|
+
def map_import_to_package(self, import_name: str) -> Optional[str]:
|
|
46
|
+
"""
|
|
47
|
+
Map import name to package name.
|
|
48
|
+
|
|
49
|
+
Priority: Manifest > Discovery > Common mappings
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
import_name: Import name (e.g., 'cv2')
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Package name (e.g., 'opencv-python') or None
|
|
56
|
+
"""
|
|
57
|
+
# Check manifest FIRST - explicit user configuration takes precedence
|
|
58
|
+
loader = get_manifest_loader()
|
|
59
|
+
manifest = loader.get_manifest(self._package_name)
|
|
60
|
+
if manifest:
|
|
61
|
+
package = manifest.get_dependency(import_name)
|
|
62
|
+
if package:
|
|
63
|
+
return package
|
|
64
|
+
|
|
65
|
+
# Check discovery mappings
|
|
66
|
+
discovery = self._get_discovery()
|
|
67
|
+
discovery_mapping = discovery.get_import_package_mapping()
|
|
68
|
+
package = discovery_mapping.get(import_name)
|
|
69
|
+
if package:
|
|
70
|
+
return package
|
|
71
|
+
|
|
72
|
+
# Fallback to common mappings
|
|
73
|
+
common_mappings = discovery.COMMON_MAPPINGS
|
|
74
|
+
return common_mappings.get(import_name)
|
|
75
|
+
|
|
76
|
+
def map_package_to_imports(self, package_name: str) -> List[str]:
|
|
77
|
+
"""
|
|
78
|
+
Map package name to possible import names.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
package_name: Package name (e.g., 'opencv-python')
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of possible import names (e.g., ['cv2'])
|
|
85
|
+
"""
|
|
86
|
+
# Check discovery mappings
|
|
87
|
+
discovery = self._get_discovery()
|
|
88
|
+
package_mapping = discovery.get_package_import_mapping()
|
|
89
|
+
imports = package_mapping.get(package_name, [])
|
|
90
|
+
|
|
91
|
+
# Also check manifest (reverse lookup)
|
|
92
|
+
loader = get_manifest_loader()
|
|
93
|
+
manifest = loader.get_manifest(self._package_name)
|
|
94
|
+
if manifest:
|
|
95
|
+
for import_name, pkg in manifest.dependencies.items():
|
|
96
|
+
if pkg.lower() == package_name.lower():
|
|
97
|
+
if import_name not in imports:
|
|
98
|
+
imports.append(import_name)
|
|
99
|
+
|
|
100
|
+
return imports if imports else [package_name]
|
|
101
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Allow List Policy Strategy
|
|
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
|
+
Allow list policy - only allows packages in the allow list.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Tuple, List, Set
|
|
14
|
+
from ...package.base import APolicyStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AllowListPolicy(APolicyStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Allow list policy strategy - only allows packages in the allow list.
|
|
20
|
+
|
|
21
|
+
Only packages explicitly in the allow list can be installed.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, allowed_packages: Set[str]):
|
|
25
|
+
"""
|
|
26
|
+
Initialize allow list policy.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
allowed_packages: Set of allowed package names
|
|
30
|
+
"""
|
|
31
|
+
self._allowed = {pkg.lower() for pkg in allowed_packages}
|
|
32
|
+
|
|
33
|
+
def is_allowed(self, package_name: str) -> Tuple[bool, str]:
|
|
34
|
+
"""
|
|
35
|
+
Check if package is allowed to be installed.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
package_name: Package name to check
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Tuple of (allowed: bool, reason: str)
|
|
42
|
+
"""
|
|
43
|
+
if package_name.lower() in self._allowed:
|
|
44
|
+
return (True, f"Package '{package_name}' is in allow list")
|
|
45
|
+
return (False, f"Package '{package_name}' is not in allow list")
|
|
46
|
+
|
|
47
|
+
def get_pip_args(self, package_name: str) -> List[str]:
|
|
48
|
+
"""
|
|
49
|
+
Get pip arguments based on policy.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
package_name: Package name
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of pip arguments (empty for allow list policy)
|
|
56
|
+
"""
|
|
57
|
+
return []
|
|
58
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deny List Policy Strategy
|
|
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
|
+
Deny list policy - blocks packages in the deny list.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Tuple, List, Set
|
|
14
|
+
from ...package.base import APolicyStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DenyListPolicy(APolicyStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Deny list policy strategy - blocks packages in the deny list.
|
|
20
|
+
|
|
21
|
+
Packages in the deny list cannot be installed.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, denied_packages: Set[str]):
|
|
25
|
+
"""
|
|
26
|
+
Initialize deny list policy.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
denied_packages: Set of denied package names
|
|
30
|
+
"""
|
|
31
|
+
self._denied = {pkg.lower() for pkg in denied_packages}
|
|
32
|
+
|
|
33
|
+
def is_allowed(self, package_name: str) -> Tuple[bool, str]:
|
|
34
|
+
"""
|
|
35
|
+
Check if package is allowed to be installed.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
package_name: Package name to check
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Tuple of (allowed: bool, reason: str)
|
|
42
|
+
"""
|
|
43
|
+
if package_name.lower() in self._denied:
|
|
44
|
+
return (False, f"Package '{package_name}' is in deny list")
|
|
45
|
+
return (True, f"Package '{package_name}' is not in deny list")
|
|
46
|
+
|
|
47
|
+
def get_pip_args(self, package_name: str) -> List[str]:
|
|
48
|
+
"""
|
|
49
|
+
Get pip arguments based on policy.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
package_name: Package name
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of pip arguments (empty for deny list policy)
|
|
56
|
+
"""
|
|
57
|
+
return []
|
|
58
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Permissive Policy Strategy
|
|
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
|
+
Permissive policy - allows all packages (default).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Tuple, List
|
|
14
|
+
from ...package.base import APolicyStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PermissivePolicy(APolicyStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Permissive policy strategy - allows all packages.
|
|
20
|
+
|
|
21
|
+
This is the default policy that doesn't restrict any packages.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def is_allowed(self, package_name: str) -> Tuple[bool, str]:
|
|
25
|
+
"""
|
|
26
|
+
Check if package is allowed to be installed.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
package_name: Package name to check
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Tuple of (allowed: bool, reason: str)
|
|
33
|
+
"""
|
|
34
|
+
return (True, "Permissive policy allows all packages")
|
|
35
|
+
|
|
36
|
+
def get_pip_args(self, package_name: str) -> List[str]:
|
|
37
|
+
"""
|
|
38
|
+
Get pip arguments based on policy.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
package_name: Package name
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
List of pip arguments (empty for permissive policy)
|
|
45
|
+
"""
|
|
46
|
+
return []
|
|
47
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clean Timing Strategy
|
|
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
|
+
Clean timing - install on usage + uninstall after completion.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import List, Any
|
|
14
|
+
from ...package.base import AInstallTimingStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CleanTiming(AInstallTimingStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Clean timing strategy - installs on usage + uninstalls after completion (LazyInstallMode.CLEAN).
|
|
20
|
+
|
|
21
|
+
Installs package when needed, then uninstalls after use to keep environment clean.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def should_install_now(self, package_name: str, context: Any) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Determine if package should be installed now.
|
|
27
|
+
|
|
28
|
+
Clean mode: Install when first needed.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
package_name: Package name to check
|
|
32
|
+
context: Context information (e.g., import error)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
True if should install now
|
|
36
|
+
"""
|
|
37
|
+
# In clean mode, install when first needed
|
|
38
|
+
return context is not None
|
|
39
|
+
|
|
40
|
+
def should_uninstall_after(self, package_name: str, context: Any) -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Determine if package should be uninstalled after use.
|
|
43
|
+
|
|
44
|
+
Clean mode: Uninstall after use.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
package_name: Package name to check
|
|
48
|
+
context: Context information
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True (uninstall after use)
|
|
52
|
+
"""
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def get_install_priority(self, packages: List[str]) -> List[str]:
|
|
56
|
+
"""
|
|
57
|
+
Get priority order for installing packages.
|
|
58
|
+
|
|
59
|
+
Clean mode: Install in order requested.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
packages: List of package names
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Priority-ordered list
|
|
66
|
+
"""
|
|
67
|
+
return packages
|
|
68
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Full Timing Strategy
|
|
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
|
+
Full timing - install all dependencies upfront.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import List, Any
|
|
14
|
+
from ...package.base import AInstallTimingStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FullTiming(AInstallTimingStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Full timing strategy - installs all packages upfront (LazyInstallMode.FULL).
|
|
20
|
+
|
|
21
|
+
Batch installs all dependencies in parallel on initialization.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def should_install_now(self, package_name: str, context: Any) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Determine if package should be installed now.
|
|
27
|
+
|
|
28
|
+
Full mode: Install all upfront.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
package_name: Package name to check
|
|
32
|
+
context: Context information (ignored in full mode)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
True (always install upfront)
|
|
36
|
+
"""
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
def should_uninstall_after(self, package_name: str, context: Any) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Determine if package should be uninstalled after use.
|
|
42
|
+
|
|
43
|
+
Full mode: Keep installed after use.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
package_name: Package name to check
|
|
47
|
+
context: Context information
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
False (keep installed)
|
|
51
|
+
"""
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
def get_install_priority(self, packages: List[str]) -> List[str]:
|
|
55
|
+
"""
|
|
56
|
+
Get priority order for installing packages.
|
|
57
|
+
|
|
58
|
+
Full mode: Install all in parallel (no specific order).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
packages: List of package names
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Priority-ordered list (original order)
|
|
65
|
+
"""
|
|
66
|
+
return packages
|
|
67
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Smart Timing Strategy
|
|
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
|
+
Smart timing - install on first usage (on-demand).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import List, Any
|
|
14
|
+
from ...package.base import AInstallTimingStrategy
|
|
15
|
+
from ...defs import LazyInstallMode
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SmartTiming(AInstallTimingStrategy):
|
|
19
|
+
"""
|
|
20
|
+
Smart timing strategy - installs packages on-demand (LazyInstallMode.SMART).
|
|
21
|
+
|
|
22
|
+
Installs package when first needed, then caches the result.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def should_install_now(self, package_name: str, context: Any) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Determine if package should be installed now.
|
|
28
|
+
|
|
29
|
+
Smart mode: Install when first needed.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
package_name: Package name to check
|
|
33
|
+
context: Context information (e.g., import error)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if should install now
|
|
37
|
+
"""
|
|
38
|
+
# In smart mode, install when first needed (context indicates need)
|
|
39
|
+
return context is not None
|
|
40
|
+
|
|
41
|
+
def should_uninstall_after(self, package_name: str, context: Any) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Determine if package should be uninstalled after use.
|
|
44
|
+
|
|
45
|
+
Smart mode: Keep installed after use.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
package_name: Package name to check
|
|
49
|
+
context: Context information
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
False (keep installed)
|
|
53
|
+
"""
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def get_install_priority(self, packages: List[str]) -> List[str]:
|
|
57
|
+
"""
|
|
58
|
+
Get priority order for installing packages.
|
|
59
|
+
|
|
60
|
+
Smart mode: Install in order requested.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
packages: List of package names
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Priority-ordered list
|
|
67
|
+
"""
|
|
68
|
+
return packages
|
|
69
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Temporary Timing Strategy
|
|
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
|
+
Temporary timing - always uninstall after use (more aggressive than CLEAN).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import List, Any
|
|
14
|
+
from ...package.base import AInstallTimingStrategy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TemporaryTiming(AInstallTimingStrategy):
|
|
18
|
+
"""
|
|
19
|
+
Temporary timing strategy - always uninstalls after use (LazyInstallMode.TEMPORARY).
|
|
20
|
+
|
|
21
|
+
More aggressive than CLEAN - always uninstalls packages after use.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def should_install_now(self, package_name: str, context: Any) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Determine if package should be installed now.
|
|
27
|
+
|
|
28
|
+
Temporary mode: Install when first needed.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
package_name: Package name to check
|
|
32
|
+
context: Context information (e.g., import error)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
True if should install now
|
|
36
|
+
"""
|
|
37
|
+
return context is not None
|
|
38
|
+
|
|
39
|
+
def should_uninstall_after(self, package_name: str, context: Any) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Determine if package should be uninstalled after use.
|
|
42
|
+
|
|
43
|
+
Temporary mode: Always uninstall after use.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
package_name: Package name to check
|
|
47
|
+
context: Context information (ignored)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True (always uninstall)
|
|
51
|
+
"""
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
def get_install_priority(self, packages: List[str]) -> List[str]:
|
|
55
|
+
"""
|
|
56
|
+
Get priority order for installing packages.
|
|
57
|
+
|
|
58
|
+
Temporary mode: Install in order requested.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
packages: List of package names
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Priority-ordered list
|
|
65
|
+
"""
|
|
66
|
+
return packages
|
|
67
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Runtime Services Module
|
|
3
|
+
|
|
4
|
+
This module provides concrete implementations for runtime services.
|
|
5
|
+
Main facade: XWRuntimeHelper extends ARuntimeHelper
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Lazy imports to avoid circular dependencies
|
|
9
|
+
def __getattr__(name: str):
|
|
10
|
+
if name == 'XWRuntimeHelper':
|
|
11
|
+
from .facade import XWRuntimeHelper
|
|
12
|
+
return XWRuntimeHelper
|
|
13
|
+
if name == 'XWRuntime': # Backward compatibility alias
|
|
14
|
+
from .facade import XWRuntimeHelper
|
|
15
|
+
return XWRuntimeHelper
|
|
16
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
17
|
+
|
|
18
|
+
__all__ = ['XWRuntimeHelper', 'XWRuntime'] # XWRuntime is backward compatibility alias
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
#exonware/xwlazy/src/exonware/xwlazy/loading/adaptive_utils.py
|
|
3
|
+
|
|
4
|
+
Adaptive learning utilities for pattern-based optimization.
|
|
5
|
+
|
|
6
|
+
Company: eXonware.com
|
|
7
|
+
Author: Eng. Muhammad AlShehri
|
|
8
|
+
Email: connect@exonware.com
|
|
9
|
+
Version: 0.1.0.19
|
|
10
|
+
Generation Date: 19-Nov-2025
|
|
11
|
+
|
|
12
|
+
This module provides adaptive learning for ADAPTIVE mode.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
import threading
|
|
17
|
+
from typing import Dict, List, Tuple, Optional
|
|
18
|
+
from collections import defaultdict, deque
|
|
19
|
+
|
|
20
|
+
# Logger not used in this module, removed to avoid circular dependency
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AdaptiveLearner:
|
|
24
|
+
"""Learns import patterns and optimizes loading strategy."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, learning_window: int = 100, prediction_depth: int = 3):
|
|
27
|
+
"""
|
|
28
|
+
Initialize adaptive learner.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
learning_window: Number of imports to track for learning
|
|
32
|
+
prediction_depth: Depth of sequence predictions
|
|
33
|
+
"""
|
|
34
|
+
self._learning_window = learning_window
|
|
35
|
+
self._prediction_depth = prediction_depth
|
|
36
|
+
self._import_sequences: deque = deque(maxlen=learning_window)
|
|
37
|
+
self._access_times: Dict[str, List[float]] = defaultdict(list)
|
|
38
|
+
self._import_chains: Dict[str, Dict[str, int]] = defaultdict(lambda: defaultdict(int))
|
|
39
|
+
self._module_scores: Dict[str, float] = {}
|
|
40
|
+
self._lock = threading.RLock()
|
|
41
|
+
|
|
42
|
+
def record_import(self, module_name: str, import_time: float) -> None:
|
|
43
|
+
"""Record an import event."""
|
|
44
|
+
current_time = time.time()
|
|
45
|
+
|
|
46
|
+
# Lock-free append to deque (thread-safe for appends)
|
|
47
|
+
self._import_sequences.append((module_name, current_time, import_time))
|
|
48
|
+
|
|
49
|
+
with self._lock:
|
|
50
|
+
self._access_times[module_name].append(current_time)
|
|
51
|
+
|
|
52
|
+
# Update import chains (what imports after what)
|
|
53
|
+
if len(self._import_sequences) > 1:
|
|
54
|
+
prev_name, _, _ = self._import_sequences[-2]
|
|
55
|
+
self._import_chains[prev_name][module_name] += 1
|
|
56
|
+
|
|
57
|
+
# Update module score (frequency * recency) - defer heavy computation
|
|
58
|
+
if len(self._access_times[module_name]) % 5 == 0: # Update every 5th access
|
|
59
|
+
self._update_module_score(module_name)
|
|
60
|
+
|
|
61
|
+
def _update_module_score(self, module_name: str) -> None:
|
|
62
|
+
"""Update module priority score."""
|
|
63
|
+
with self._lock:
|
|
64
|
+
accesses = self._access_times[module_name]
|
|
65
|
+
if not accesses:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Frequency component
|
|
69
|
+
recent_accesses = [t for t in accesses if time.time() - t < 3600] # Last hour
|
|
70
|
+
frequency = len(recent_accesses)
|
|
71
|
+
|
|
72
|
+
# Recency component (more recent = higher score)
|
|
73
|
+
if accesses:
|
|
74
|
+
last_access = accesses[-1]
|
|
75
|
+
recency = 1.0 / (time.time() - last_access + 1.0)
|
|
76
|
+
else:
|
|
77
|
+
recency = 0.0
|
|
78
|
+
|
|
79
|
+
# Chain component (if often imported after another module)
|
|
80
|
+
chain_weight = sum(self._import_chains.get(prev, {}).get(module_name, 0)
|
|
81
|
+
for prev in self._access_times.keys()) / max(len(self._import_sequences), 1)
|
|
82
|
+
|
|
83
|
+
# Combined score
|
|
84
|
+
self._module_scores[module_name] = frequency * 0.4 + recency * 1000 * 0.4 + chain_weight * 0.2
|
|
85
|
+
|
|
86
|
+
def predict_next_imports(self, current_module: Optional[str] = None, limit: int = 5) -> List[str]:
|
|
87
|
+
"""Predict likely next imports based on patterns."""
|
|
88
|
+
# Lock-free check first
|
|
89
|
+
if not self._import_sequences:
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
with self._lock:
|
|
93
|
+
candidates: Dict[str, float] = {}
|
|
94
|
+
|
|
95
|
+
# Predict based on current module chain (lock-free read of dict)
|
|
96
|
+
if current_module:
|
|
97
|
+
chain_candidates = self._import_chains.get(current_module, {})
|
|
98
|
+
for module, count in chain_candidates.items():
|
|
99
|
+
candidates[module] = candidates.get(module, 0.0) + count * 2.0
|
|
100
|
+
|
|
101
|
+
# Add high-scoring modules (lock-free read of dict)
|
|
102
|
+
for module, score in self._module_scores.items():
|
|
103
|
+
candidates[module] = candidates.get(module, 0.0) + score * 0.5
|
|
104
|
+
|
|
105
|
+
# Sort by score
|
|
106
|
+
sorted_candidates = sorted(candidates.items(), key=lambda x: x[1], reverse=True)
|
|
107
|
+
return [module for module, _ in sorted_candidates[:limit]]
|
|
108
|
+
|
|
109
|
+
def get_priority_modules(self, limit: int = 10) -> List[str]:
|
|
110
|
+
"""Get modules that should be preloaded based on scores."""
|
|
111
|
+
with self._lock:
|
|
112
|
+
sorted_modules = sorted(
|
|
113
|
+
self._module_scores.items(),
|
|
114
|
+
key=lambda x: x[1],
|
|
115
|
+
reverse=True
|
|
116
|
+
)
|
|
117
|
+
return [module for module, _ in sorted_modules[:limit]]
|
|
118
|
+
|
|
119
|
+
def get_stats(self) -> Dict:
|
|
120
|
+
"""Get learning statistics."""
|
|
121
|
+
with self._lock:
|
|
122
|
+
return {
|
|
123
|
+
'sequences_tracked': len(self._import_sequences),
|
|
124
|
+
'unique_modules': len(self._access_times),
|
|
125
|
+
'chains_tracked': sum(len(chains) for chains in self._import_chains.values()),
|
|
126
|
+
'top_modules': self.get_priority_modules(5),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = ['AdaptiveLearner']
|
|
131
|
+
|