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.
Files changed (89) 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/services/state_manager.py +86 -0
  14. exonware/xwlazy/common/strategies/__init__.py +28 -0
  15. exonware/xwlazy/common/strategies/caching_dict.py +45 -0
  16. exonware/xwlazy/common/strategies/caching_installation.py +89 -0
  17. exonware/xwlazy/common/strategies/caching_lfu.py +67 -0
  18. exonware/xwlazy/common/strategies/caching_lru.py +64 -0
  19. exonware/xwlazy/common/strategies/caching_multitier.py +60 -0
  20. exonware/xwlazy/common/strategies/caching_ttl.py +60 -0
  21. exonware/xwlazy/config.py +195 -0
  22. exonware/xwlazy/contracts.py +1410 -0
  23. exonware/xwlazy/defs.py +397 -0
  24. exonware/xwlazy/errors.py +284 -0
  25. exonware/xwlazy/facade.py +1049 -0
  26. exonware/xwlazy/module/__init__.py +18 -0
  27. exonware/xwlazy/module/base.py +569 -0
  28. exonware/xwlazy/module/data.py +17 -0
  29. exonware/xwlazy/module/facade.py +247 -0
  30. exonware/xwlazy/module/importer_engine.py +2161 -0
  31. exonware/xwlazy/module/strategies/__init__.py +22 -0
  32. exonware/xwlazy/module/strategies/module_helper_lazy.py +94 -0
  33. exonware/xwlazy/module/strategies/module_helper_simple.py +66 -0
  34. exonware/xwlazy/module/strategies/module_manager_advanced.py +112 -0
  35. exonware/xwlazy/module/strategies/module_manager_simple.py +96 -0
  36. exonware/xwlazy/package/__init__.py +18 -0
  37. exonware/xwlazy/package/base.py +807 -0
  38. exonware/xwlazy/package/conf.py +331 -0
  39. exonware/xwlazy/package/data.py +17 -0
  40. exonware/xwlazy/package/facade.py +481 -0
  41. exonware/xwlazy/package/services/__init__.py +84 -0
  42. exonware/xwlazy/package/services/async_install_handle.py +89 -0
  43. exonware/xwlazy/package/services/config_manager.py +246 -0
  44. exonware/xwlazy/package/services/discovery.py +374 -0
  45. exonware/xwlazy/package/services/host_packages.py +149 -0
  46. exonware/xwlazy/package/services/install_async.py +278 -0
  47. exonware/xwlazy/package/services/install_cache.py +146 -0
  48. exonware/xwlazy/package/services/install_interactive.py +60 -0
  49. exonware/xwlazy/package/services/install_policy.py +158 -0
  50. exonware/xwlazy/package/services/install_registry.py +56 -0
  51. exonware/xwlazy/package/services/install_result.py +17 -0
  52. exonware/xwlazy/package/services/install_sbom.py +154 -0
  53. exonware/xwlazy/package/services/install_utils.py +83 -0
  54. exonware/xwlazy/package/services/installer_engine.py +408 -0
  55. exonware/xwlazy/package/services/lazy_installer.py +720 -0
  56. exonware/xwlazy/package/services/manifest.py +506 -0
  57. exonware/xwlazy/package/services/strategy_registry.py +188 -0
  58. exonware/xwlazy/package/strategies/__init__.py +57 -0
  59. exonware/xwlazy/package/strategies/package_discovery_file.py +130 -0
  60. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +85 -0
  61. exonware/xwlazy/package/strategies/package_discovery_manifest.py +102 -0
  62. exonware/xwlazy/package/strategies/package_execution_async.py +114 -0
  63. exonware/xwlazy/package/strategies/package_execution_cached.py +91 -0
  64. exonware/xwlazy/package/strategies/package_execution_pip.py +100 -0
  65. exonware/xwlazy/package/strategies/package_execution_wheel.py +107 -0
  66. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +101 -0
  67. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +106 -0
  68. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +101 -0
  69. exonware/xwlazy/package/strategies/package_policy_allow_list.py +58 -0
  70. exonware/xwlazy/package/strategies/package_policy_deny_list.py +58 -0
  71. exonware/xwlazy/package/strategies/package_policy_permissive.py +47 -0
  72. exonware/xwlazy/package/strategies/package_timing_clean.py +68 -0
  73. exonware/xwlazy/package/strategies/package_timing_full.py +67 -0
  74. exonware/xwlazy/package/strategies/package_timing_smart.py +69 -0
  75. exonware/xwlazy/package/strategies/package_timing_temporary.py +67 -0
  76. exonware/xwlazy/runtime/__init__.py +18 -0
  77. exonware/xwlazy/runtime/adaptive_learner.py +131 -0
  78. exonware/xwlazy/runtime/base.py +276 -0
  79. exonware/xwlazy/runtime/facade.py +95 -0
  80. exonware/xwlazy/runtime/intelligent_selector.py +173 -0
  81. exonware/xwlazy/runtime/metrics.py +64 -0
  82. exonware/xwlazy/runtime/performance.py +39 -0
  83. exonware/xwlazy/version.py +2 -2
  84. exonware_xwlazy-0.1.0.19.dist-info/METADATA +456 -0
  85. exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
  86. exonware_xwlazy-0.1.0.10.dist-info/METADATA +0 -0
  87. exonware_xwlazy-0.1.0.10.dist-info/RECORD +0 -6
  88. {exonware_xwlazy-0.1.0.10.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
  89. {exonware_xwlazy-0.1.0.10.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,107 @@
1
+ """
2
+ Wheel Execution 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
+ Wheel execution strategy - install from wheel files.
11
+ Uses shared utilities from common/services/install_cache_utils.
12
+ """
13
+
14
+ import sys
15
+ import subprocess
16
+ from pathlib import Path
17
+ from typing import List, Any, Optional
18
+ from ...package.base import AInstallExecutionStrategy
19
+ from ...package.services.install_result import InstallResult, InstallStatus
20
+ from ...common.services.install_cache_utils import (
21
+ get_wheel_path,
22
+ ensure_cached_wheel,
23
+ pip_install_from_path,
24
+ )
25
+
26
+
27
+ class WheelExecution(AInstallExecutionStrategy):
28
+ """
29
+ Wheel execution strategy - installs packages from wheel files.
30
+
31
+ Downloads wheel first, then installs from local wheel file.
32
+ Uses shared utilities from common/services/install_cache_utils.
33
+ """
34
+
35
+ def __init__(self, cache_dir: Optional[Path] = None):
36
+ """
37
+ Initialize wheel execution strategy.
38
+
39
+ Args:
40
+ cache_dir: Optional cache directory for wheels
41
+ """
42
+ self._cache_dir = cache_dir
43
+
44
+ def execute_install(self, package_name: str, policy_args: List[str]) -> Any:
45
+ """
46
+ Execute installation from wheel file.
47
+
48
+ Args:
49
+ package_name: Package name to install
50
+ policy_args: Policy arguments
51
+
52
+ Returns:
53
+ InstallResult with success status
54
+ """
55
+ # Check if wheel already exists
56
+ wheel_path = get_wheel_path(package_name, self._cache_dir)
57
+
58
+ if not wheel_path.exists():
59
+ # Download wheel first
60
+ wheel_path = ensure_cached_wheel(package_name, policy_args, self._cache_dir)
61
+ if wheel_path is None:
62
+ return InstallResult(
63
+ package_name=package_name,
64
+ success=False,
65
+ status=InstallStatus.FAILED,
66
+ error="Failed to download wheel"
67
+ )
68
+
69
+ # Install from wheel
70
+ success = pip_install_from_path(wheel_path, policy_args)
71
+
72
+ if success:
73
+ return InstallResult(
74
+ package_name=package_name,
75
+ success=True,
76
+ status=InstallStatus.SUCCESS,
77
+ source="wheel"
78
+ )
79
+ else:
80
+ return InstallResult(
81
+ package_name=package_name,
82
+ success=False,
83
+ status=InstallStatus.FAILED,
84
+ error="Failed to install from wheel"
85
+ )
86
+
87
+ def execute_uninstall(self, package_name: str) -> bool:
88
+ """
89
+ Execute uninstallation using pip.
90
+
91
+ Args:
92
+ package_name: Package name to uninstall
93
+
94
+ Returns:
95
+ True if successful, False otherwise
96
+ """
97
+ try:
98
+ result = subprocess.run(
99
+ [sys.executable, '-m', 'pip', 'uninstall', '-y', package_name],
100
+ capture_output=True,
101
+ text=True,
102
+ check=True
103
+ )
104
+ return result.returncode == 0
105
+ except Exception:
106
+ return False
107
+
@@ -0,0 +1,101 @@
1
+ """
2
+ Discovery-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
+ Discovery-first mapping strategy - discovery takes precedence over manifest.
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 DiscoveryFirstMapping(AMappingStrategy):
19
+ """
20
+ Discovery-first mapping strategy.
21
+
22
+ Priority order:
23
+ 1. Discovery mappings (automatic discovery - highest priority)
24
+ 2. Manifest dependencies (explicit user configuration)
25
+ 3. Common mappings (fallback)
26
+ """
27
+
28
+ def __init__(self, package_name: str = 'default'):
29
+ """
30
+ Initialize discovery-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: Discovery > Manifest > 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 discovery mappings FIRST
58
+ discovery = self._get_discovery()
59
+ discovery_mapping = discovery.get_import_package_mapping()
60
+ package = discovery_mapping.get(import_name)
61
+ if package:
62
+ return package
63
+
64
+ # Check manifest (fallback)
65
+ loader = get_manifest_loader()
66
+ manifest = loader.get_manifest(self._package_name)
67
+ if manifest:
68
+ package = manifest.get_dependency(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 FIRST
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,106 @@
1
+ """
2
+ Hybrid 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
+ Hybrid mapping strategy - tries both manifest and discovery, uses best match.
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 HybridMapping(AMappingStrategy):
19
+ """
20
+ Hybrid mapping strategy.
21
+
22
+ Tries both manifest and discovery, uses the best match.
23
+ If both exist, prefers the one that's more specific.
24
+ """
25
+
26
+ def __init__(self, package_name: str = 'default'):
27
+ """
28
+ Initialize hybrid mapping strategy.
29
+
30
+ Args:
31
+ package_name: Package name for isolation
32
+ """
33
+ self._package_name = package_name
34
+ self._discovery = None # Lazy init
35
+
36
+ def _get_discovery(self):
37
+ """Get discovery instance (lazy init)."""
38
+ if self._discovery is None:
39
+ from ...package.services.discovery import LazyDiscovery
40
+ self._discovery = LazyDiscovery(self._package_name)
41
+ return self._discovery
42
+
43
+ def map_import_to_package(self, import_name: str) -> Optional[str]:
44
+ """
45
+ Map import name to package name.
46
+
47
+ Tries both manifest and discovery, prefers more specific match.
48
+
49
+ Args:
50
+ import_name: Import name (e.g., 'cv2')
51
+
52
+ Returns:
53
+ Package name (e.g., 'opencv-python') or None
54
+ """
55
+ # Try manifest first
56
+ loader = get_manifest_loader()
57
+ manifest = loader.get_manifest(self._package_name)
58
+ manifest_package = None
59
+ if manifest:
60
+ manifest_package = manifest.get_dependency(import_name)
61
+
62
+ # Try discovery
63
+ discovery = self._get_discovery()
64
+ discovery_mapping = discovery.get_import_package_mapping()
65
+ discovery_package = discovery_mapping.get(import_name)
66
+
67
+ # Prefer manifest if both exist (more explicit)
68
+ if manifest_package:
69
+ return manifest_package
70
+
71
+ if discovery_package:
72
+ return discovery_package
73
+
74
+ # Fallback to common mappings
75
+ common_mappings = discovery.COMMON_MAPPINGS
76
+ return common_mappings.get(import_name)
77
+
78
+ def map_package_to_imports(self, package_name: str) -> List[str]:
79
+ """
80
+ Map package name to possible import names.
81
+
82
+ Args:
83
+ package_name: Package name (e.g., 'opencv-python')
84
+
85
+ Returns:
86
+ List of possible import names (e.g., ['cv2'])
87
+ """
88
+ imports = []
89
+
90
+ # Check discovery mappings
91
+ discovery = self._get_discovery()
92
+ package_mapping = discovery.get_package_import_mapping()
93
+ discovery_imports = package_mapping.get(package_name, [])
94
+ imports.extend(discovery_imports)
95
+
96
+ # Check manifest (reverse lookup)
97
+ loader = get_manifest_loader()
98
+ manifest = loader.get_manifest(self._package_name)
99
+ if manifest:
100
+ for import_name, pkg in manifest.dependencies.items():
101
+ if pkg.lower() == package_name.lower():
102
+ if import_name not in imports:
103
+ imports.append(import_name)
104
+
105
+ return imports if imports else [package_name]
106
+
@@ -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
+