exonware-xwlazy 0.1.0.23__py3-none-any.whl → 1.0.1.2__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 +85 -34
- exonware/xwlazy/version.py +5 -5
- exonware/xwlazy.py +2546 -0
- exonware/xwlazy_external_libs.toml +716 -0
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/METADATA +5 -1
- exonware_xwlazy-1.0.1.2.dist-info/RECORD +8 -0
- exonware/xwlazy/__init__.py +0 -379
- exonware/xwlazy/common/__init__.py +0 -55
- exonware/xwlazy/common/base.py +0 -65
- exonware/xwlazy/common/cache.py +0 -504
- exonware/xwlazy/common/logger.py +0 -257
- exonware/xwlazy/common/services/__init__.py +0 -72
- exonware/xwlazy/common/services/dependency_mapper.py +0 -250
- exonware/xwlazy/common/services/install_async_utils.py +0 -170
- exonware/xwlazy/common/services/install_cache_utils.py +0 -245
- exonware/xwlazy/common/services/keyword_detection.py +0 -283
- exonware/xwlazy/common/services/spec_cache.py +0 -165
- exonware/xwlazy/common/services/state_manager.py +0 -84
- exonware/xwlazy/common/strategies/__init__.py +0 -28
- exonware/xwlazy/common/strategies/caching_dict.py +0 -44
- exonware/xwlazy/common/strategies/caching_installation.py +0 -88
- exonware/xwlazy/common/strategies/caching_lfu.py +0 -66
- exonware/xwlazy/common/strategies/caching_lru.py +0 -63
- exonware/xwlazy/common/strategies/caching_multitier.py +0 -59
- exonware/xwlazy/common/strategies/caching_ttl.py +0 -59
- exonware/xwlazy/common/utils.py +0 -142
- exonware/xwlazy/config.py +0 -193
- exonware/xwlazy/contracts.py +0 -1533
- exonware/xwlazy/defs.py +0 -378
- exonware/xwlazy/errors.py +0 -276
- exonware/xwlazy/facade.py +0 -1137
- exonware/xwlazy/host/__init__.py +0 -8
- exonware/xwlazy/host/conf.py +0 -16
- exonware/xwlazy/module/__init__.py +0 -18
- exonware/xwlazy/module/base.py +0 -622
- exonware/xwlazy/module/data.py +0 -17
- exonware/xwlazy/module/facade.py +0 -246
- exonware/xwlazy/module/importer_engine.py +0 -2964
- exonware/xwlazy/module/partial_module_detector.py +0 -275
- exonware/xwlazy/module/strategies/__init__.py +0 -22
- exonware/xwlazy/module/strategies/module_helper_lazy.py +0 -93
- exonware/xwlazy/module/strategies/module_helper_simple.py +0 -65
- exonware/xwlazy/module/strategies/module_manager_advanced.py +0 -111
- exonware/xwlazy/module/strategies/module_manager_simple.py +0 -95
- exonware/xwlazy/package/__init__.py +0 -18
- exonware/xwlazy/package/base.py +0 -863
- exonware/xwlazy/package/conf.py +0 -324
- exonware/xwlazy/package/data.py +0 -17
- exonware/xwlazy/package/facade.py +0 -480
- exonware/xwlazy/package/services/__init__.py +0 -84
- exonware/xwlazy/package/services/async_install_handle.py +0 -87
- exonware/xwlazy/package/services/config_manager.py +0 -249
- exonware/xwlazy/package/services/discovery.py +0 -435
- exonware/xwlazy/package/services/host_packages.py +0 -180
- exonware/xwlazy/package/services/install_async.py +0 -291
- exonware/xwlazy/package/services/install_cache.py +0 -145
- exonware/xwlazy/package/services/install_interactive.py +0 -59
- exonware/xwlazy/package/services/install_policy.py +0 -156
- exonware/xwlazy/package/services/install_registry.py +0 -54
- exonware/xwlazy/package/services/install_result.py +0 -17
- exonware/xwlazy/package/services/install_sbom.py +0 -153
- exonware/xwlazy/package/services/install_utils.py +0 -79
- exonware/xwlazy/package/services/installer_engine.py +0 -406
- exonware/xwlazy/package/services/lazy_installer.py +0 -803
- exonware/xwlazy/package/services/manifest.py +0 -503
- exonware/xwlazy/package/services/strategy_registry.py +0 -324
- exonware/xwlazy/package/strategies/__init__.py +0 -57
- exonware/xwlazy/package/strategies/package_discovery_file.py +0 -129
- exonware/xwlazy/package/strategies/package_discovery_hybrid.py +0 -84
- exonware/xwlazy/package/strategies/package_discovery_manifest.py +0 -101
- exonware/xwlazy/package/strategies/package_execution_async.py +0 -113
- exonware/xwlazy/package/strategies/package_execution_cached.py +0 -90
- exonware/xwlazy/package/strategies/package_execution_pip.py +0 -99
- exonware/xwlazy/package/strategies/package_execution_wheel.py +0 -106
- exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +0 -100
- exonware/xwlazy/package/strategies/package_mapping_hybrid.py +0 -105
- exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +0 -100
- exonware/xwlazy/package/strategies/package_policy_allow_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_deny_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_permissive.py +0 -46
- exonware/xwlazy/package/strategies/package_timing_clean.py +0 -67
- exonware/xwlazy/package/strategies/package_timing_full.py +0 -66
- exonware/xwlazy/package/strategies/package_timing_smart.py +0 -68
- exonware/xwlazy/package/strategies/package_timing_temporary.py +0 -66
- exonware/xwlazy/runtime/__init__.py +0 -18
- exonware/xwlazy/runtime/adaptive_learner.py +0 -129
- exonware/xwlazy/runtime/base.py +0 -274
- exonware/xwlazy/runtime/facade.py +0 -94
- exonware/xwlazy/runtime/intelligent_selector.py +0 -170
- exonware/xwlazy/runtime/metrics.py +0 -60
- exonware/xwlazy/runtime/performance.py +0 -37
- exonware_xwlazy-0.1.0.23.dist-info/RECORD +0 -93
- xwlazy/__init__.py +0 -14
- xwlazy/lazy.py +0 -30
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.23.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Strategy Registry
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Registry to store custom strategies per package for both package and module operations.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import threading
|
|
14
|
-
from typing import Optional, Any, TYPE_CHECKING
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from ...contracts import (
|
|
18
|
-
IInstallExecutionStrategy,
|
|
19
|
-
IInstallTimingStrategy,
|
|
20
|
-
IDiscoveryStrategy,
|
|
21
|
-
IPolicyStrategy,
|
|
22
|
-
IMappingStrategy,
|
|
23
|
-
IModuleHelperStrategy,
|
|
24
|
-
IModuleManagerStrategy,
|
|
25
|
-
ICachingStrategy,
|
|
26
|
-
IInstallStrategy,
|
|
27
|
-
ILoadStrategy,
|
|
28
|
-
ICacheStrategy,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
class StrategyRegistry:
|
|
32
|
-
"""
|
|
33
|
-
Registry to store custom strategies per package and enable runtime strategy swapping.
|
|
34
|
-
|
|
35
|
-
Supports both:
|
|
36
|
-
- Per-package strategies (different strategies for different packages)
|
|
37
|
-
- Global strategy swapping (change default strategies used by all packages)
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
# Package strategies (per-package)
|
|
41
|
-
_package_execution_strategies: dict[str, 'IInstallExecutionStrategy'] = {}
|
|
42
|
-
_package_timing_strategies: dict[str, 'IInstallTimingStrategy'] = {}
|
|
43
|
-
_package_discovery_strategies: dict[str, 'IDiscoveryStrategy'] = {}
|
|
44
|
-
_package_policy_strategies: dict[str, 'IPolicyStrategy'] = {}
|
|
45
|
-
_package_mapping_strategies: dict[str, 'IMappingStrategy'] = {}
|
|
46
|
-
|
|
47
|
-
# Module strategies (per-package)
|
|
48
|
-
_module_helper_strategies: dict[str, 'IModuleHelperStrategy'] = {}
|
|
49
|
-
_module_manager_strategies: dict[str, 'IModuleManagerStrategy'] = {}
|
|
50
|
-
_module_caching_strategies: dict[str, 'ICachingStrategy'] = {}
|
|
51
|
-
|
|
52
|
-
# Global strategies (for runtime swapping)
|
|
53
|
-
_global_install_strategies: dict[str, 'IInstallStrategy'] = {}
|
|
54
|
-
_global_load_strategies: dict[str, 'ILoadStrategy'] = {}
|
|
55
|
-
_global_cache_strategies: dict[str, 'ICacheStrategy'] = {}
|
|
56
|
-
_default_install_strategy: Optional[str] = None
|
|
57
|
-
_default_load_strategy: Optional[str] = None
|
|
58
|
-
_default_cache_strategy: Optional[str] = None
|
|
59
|
-
|
|
60
|
-
_lock = threading.RLock()
|
|
61
|
-
|
|
62
|
-
@classmethod
|
|
63
|
-
def set_package_strategy(
|
|
64
|
-
cls,
|
|
65
|
-
package_name: str,
|
|
66
|
-
strategy_type: str,
|
|
67
|
-
strategy: Any,
|
|
68
|
-
) -> None:
|
|
69
|
-
"""
|
|
70
|
-
Set a package strategy for a package.
|
|
71
|
-
|
|
72
|
-
Args:
|
|
73
|
-
package_name: Package name
|
|
74
|
-
strategy_type: One of 'execution', 'timing', 'discovery', 'policy', 'mapping'
|
|
75
|
-
strategy: Strategy instance
|
|
76
|
-
"""
|
|
77
|
-
package_key = package_name.lower()
|
|
78
|
-
with cls._lock:
|
|
79
|
-
if strategy_type == 'execution':
|
|
80
|
-
cls._package_execution_strategies[package_key] = strategy
|
|
81
|
-
elif strategy_type == 'timing':
|
|
82
|
-
cls._package_timing_strategies[package_key] = strategy
|
|
83
|
-
elif strategy_type == 'discovery':
|
|
84
|
-
cls._package_discovery_strategies[package_key] = strategy
|
|
85
|
-
elif strategy_type == 'policy':
|
|
86
|
-
cls._package_policy_strategies[package_key] = strategy
|
|
87
|
-
elif strategy_type == 'mapping':
|
|
88
|
-
cls._package_mapping_strategies[package_key] = strategy
|
|
89
|
-
else:
|
|
90
|
-
raise ValueError(f"Unknown package strategy type: {strategy_type}")
|
|
91
|
-
|
|
92
|
-
@classmethod
|
|
93
|
-
def get_package_strategy(
|
|
94
|
-
cls,
|
|
95
|
-
package_name: str,
|
|
96
|
-
strategy_type: str,
|
|
97
|
-
) -> Optional[Any]:
|
|
98
|
-
"""
|
|
99
|
-
Get a package strategy for a package.
|
|
100
|
-
|
|
101
|
-
Args:
|
|
102
|
-
package_name: Package name
|
|
103
|
-
strategy_type: One of 'execution', 'timing', 'discovery', 'policy', 'mapping'
|
|
104
|
-
|
|
105
|
-
Returns:
|
|
106
|
-
Strategy instance or None if not set
|
|
107
|
-
"""
|
|
108
|
-
package_key = package_name.lower()
|
|
109
|
-
with cls._lock:
|
|
110
|
-
if strategy_type == 'execution':
|
|
111
|
-
return cls._package_execution_strategies.get(package_key)
|
|
112
|
-
elif strategy_type == 'timing':
|
|
113
|
-
return cls._package_timing_strategies.get(package_key)
|
|
114
|
-
elif strategy_type == 'discovery':
|
|
115
|
-
return cls._package_discovery_strategies.get(package_key)
|
|
116
|
-
elif strategy_type == 'policy':
|
|
117
|
-
return cls._package_policy_strategies.get(package_key)
|
|
118
|
-
elif strategy_type == 'mapping':
|
|
119
|
-
return cls._package_mapping_strategies.get(package_key)
|
|
120
|
-
else:
|
|
121
|
-
raise ValueError(f"Unknown package strategy type: {strategy_type}")
|
|
122
|
-
|
|
123
|
-
@classmethod
|
|
124
|
-
def set_module_strategy(
|
|
125
|
-
cls,
|
|
126
|
-
package_name: str,
|
|
127
|
-
strategy_type: str,
|
|
128
|
-
strategy: Any,
|
|
129
|
-
) -> None:
|
|
130
|
-
"""
|
|
131
|
-
Set a module strategy for a package.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
package_name: Package name
|
|
135
|
-
strategy_type: One of 'helper', 'manager', 'caching'
|
|
136
|
-
strategy: Strategy instance
|
|
137
|
-
"""
|
|
138
|
-
package_key = package_name.lower()
|
|
139
|
-
with cls._lock:
|
|
140
|
-
if strategy_type == 'helper':
|
|
141
|
-
cls._module_helper_strategies[package_key] = strategy
|
|
142
|
-
elif strategy_type == 'manager':
|
|
143
|
-
cls._module_manager_strategies[package_key] = strategy
|
|
144
|
-
elif strategy_type == 'caching':
|
|
145
|
-
cls._module_caching_strategies[package_key] = strategy
|
|
146
|
-
else:
|
|
147
|
-
raise ValueError(f"Unknown module strategy type: {strategy_type}")
|
|
148
|
-
|
|
149
|
-
@classmethod
|
|
150
|
-
def get_module_strategy(
|
|
151
|
-
cls,
|
|
152
|
-
package_name: str,
|
|
153
|
-
strategy_type: str,
|
|
154
|
-
) -> Optional[Any]:
|
|
155
|
-
"""
|
|
156
|
-
Get a module strategy for a package.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
package_name: Package name
|
|
160
|
-
strategy_type: One of 'helper', 'manager', 'caching'
|
|
161
|
-
|
|
162
|
-
Returns:
|
|
163
|
-
Strategy instance or None if not set
|
|
164
|
-
"""
|
|
165
|
-
package_key = package_name.lower()
|
|
166
|
-
with cls._lock:
|
|
167
|
-
if strategy_type == 'helper':
|
|
168
|
-
return cls._module_helper_strategies.get(package_key)
|
|
169
|
-
elif strategy_type == 'manager':
|
|
170
|
-
return cls._module_manager_strategies.get(package_key)
|
|
171
|
-
elif strategy_type == 'caching':
|
|
172
|
-
return cls._module_caching_strategies.get(package_key)
|
|
173
|
-
else:
|
|
174
|
-
raise ValueError(f"Unknown module strategy type: {strategy_type}")
|
|
175
|
-
|
|
176
|
-
@classmethod
|
|
177
|
-
def clear_package_strategies(cls, package_name: str) -> None:
|
|
178
|
-
"""Clear all package strategies for a package."""
|
|
179
|
-
package_key = package_name.lower()
|
|
180
|
-
with cls._lock:
|
|
181
|
-
cls._package_execution_strategies.pop(package_key, None)
|
|
182
|
-
cls._package_timing_strategies.pop(package_key, None)
|
|
183
|
-
cls._package_discovery_strategies.pop(package_key, None)
|
|
184
|
-
cls._package_policy_strategies.pop(package_key, None)
|
|
185
|
-
cls._package_mapping_strategies.pop(package_key, None)
|
|
186
|
-
|
|
187
|
-
@classmethod
|
|
188
|
-
def clear_module_strategies(cls, package_name: str) -> None:
|
|
189
|
-
"""Clear all module strategies for a package."""
|
|
190
|
-
package_key = package_name.lower()
|
|
191
|
-
with cls._lock:
|
|
192
|
-
cls._module_helper_strategies.pop(package_key, None)
|
|
193
|
-
cls._module_manager_strategies.pop(package_key, None)
|
|
194
|
-
cls._module_caching_strategies.pop(package_key, None)
|
|
195
|
-
|
|
196
|
-
@classmethod
|
|
197
|
-
def clear_all_strategies(cls, package_name: str) -> None:
|
|
198
|
-
"""Clear all strategies (package and module) for a package."""
|
|
199
|
-
cls.clear_package_strategies(package_name)
|
|
200
|
-
cls.clear_module_strategies(package_name)
|
|
201
|
-
|
|
202
|
-
# ========================================================================
|
|
203
|
-
# Global Strategy Management (for runtime swapping)
|
|
204
|
-
# ========================================================================
|
|
205
|
-
|
|
206
|
-
@classmethod
|
|
207
|
-
def register_install_strategy(cls, name: str, strategy: 'IInstallStrategy') -> None:
|
|
208
|
-
"""
|
|
209
|
-
Register a global installation strategy for runtime swapping.
|
|
210
|
-
|
|
211
|
-
Args:
|
|
212
|
-
name: Strategy name (e.g., 'pip', 'wheel', 'async', 'cached')
|
|
213
|
-
strategy: Strategy instance implementing IInstallStrategy
|
|
214
|
-
"""
|
|
215
|
-
with cls._lock:
|
|
216
|
-
cls._global_install_strategies[name] = strategy
|
|
217
|
-
if cls._default_install_strategy is None:
|
|
218
|
-
cls._default_install_strategy = name
|
|
219
|
-
|
|
220
|
-
@classmethod
|
|
221
|
-
def get_install_strategy(cls, name: Optional[str] = None) -> Optional['IInstallStrategy']:
|
|
222
|
-
"""
|
|
223
|
-
Get global installation strategy by name.
|
|
224
|
-
|
|
225
|
-
Args:
|
|
226
|
-
name: Strategy name (uses default if None)
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
Strategy instance or None if not found
|
|
230
|
-
"""
|
|
231
|
-
with cls._lock:
|
|
232
|
-
if name is None:
|
|
233
|
-
name = cls._default_install_strategy
|
|
234
|
-
return cls._global_install_strategies.get(name) if name else None
|
|
235
|
-
|
|
236
|
-
@classmethod
|
|
237
|
-
def register_load_strategy(cls, name: str, strategy: 'ILoadStrategy') -> None:
|
|
238
|
-
"""
|
|
239
|
-
Register a global loading strategy for runtime swapping.
|
|
240
|
-
|
|
241
|
-
Args:
|
|
242
|
-
name: Strategy name (e.g., 'lazy', 'simple', 'advanced')
|
|
243
|
-
strategy: Strategy instance implementing ILoadStrategy
|
|
244
|
-
"""
|
|
245
|
-
with cls._lock:
|
|
246
|
-
cls._global_load_strategies[name] = strategy
|
|
247
|
-
if cls._default_load_strategy is None:
|
|
248
|
-
cls._default_load_strategy = name
|
|
249
|
-
|
|
250
|
-
@classmethod
|
|
251
|
-
def get_load_strategy(cls, name: Optional[str] = None) -> Optional['ILoadStrategy']:
|
|
252
|
-
"""
|
|
253
|
-
Get global loading strategy by name.
|
|
254
|
-
|
|
255
|
-
Args:
|
|
256
|
-
name: Strategy name (uses default if None)
|
|
257
|
-
|
|
258
|
-
Returns:
|
|
259
|
-
Strategy instance or None if not found
|
|
260
|
-
"""
|
|
261
|
-
with cls._lock:
|
|
262
|
-
if name is None:
|
|
263
|
-
name = cls._default_load_strategy
|
|
264
|
-
return cls._global_load_strategies.get(name) if name else None
|
|
265
|
-
|
|
266
|
-
@classmethod
|
|
267
|
-
def register_cache_strategy(cls, name: str, strategy: 'ICacheStrategy') -> None:
|
|
268
|
-
"""
|
|
269
|
-
Register a global caching strategy for runtime swapping.
|
|
270
|
-
|
|
271
|
-
Args:
|
|
272
|
-
name: Strategy name (e.g., 'lru', 'lfu', 'ttl', 'multitier')
|
|
273
|
-
strategy: Strategy instance implementing ICacheStrategy
|
|
274
|
-
"""
|
|
275
|
-
with cls._lock:
|
|
276
|
-
cls._global_cache_strategies[name] = strategy
|
|
277
|
-
if cls._default_cache_strategy is None:
|
|
278
|
-
cls._default_cache_strategy = name
|
|
279
|
-
|
|
280
|
-
@classmethod
|
|
281
|
-
def get_cache_strategy(cls, name: Optional[str] = None) -> Optional['ICacheStrategy']:
|
|
282
|
-
"""
|
|
283
|
-
Get global caching strategy by name.
|
|
284
|
-
|
|
285
|
-
Args:
|
|
286
|
-
name: Strategy name (uses default if None)
|
|
287
|
-
|
|
288
|
-
Returns:
|
|
289
|
-
Strategy instance or None if not found
|
|
290
|
-
"""
|
|
291
|
-
with cls._lock:
|
|
292
|
-
if name is None:
|
|
293
|
-
name = cls._default_cache_strategy
|
|
294
|
-
return cls._global_cache_strategies.get(name) if name else None
|
|
295
|
-
|
|
296
|
-
@classmethod
|
|
297
|
-
def swap_install_strategy(cls, name: str) -> bool:
|
|
298
|
-
"""Swap to a different global installation strategy."""
|
|
299
|
-
with cls._lock:
|
|
300
|
-
if name in cls._global_install_strategies:
|
|
301
|
-
cls._default_install_strategy = name
|
|
302
|
-
return True
|
|
303
|
-
return False
|
|
304
|
-
|
|
305
|
-
@classmethod
|
|
306
|
-
def swap_load_strategy(cls, name: str) -> bool:
|
|
307
|
-
"""Swap to a different global loading strategy."""
|
|
308
|
-
with cls._lock:
|
|
309
|
-
if name in cls._global_load_strategies:
|
|
310
|
-
cls._default_load_strategy = name
|
|
311
|
-
return True
|
|
312
|
-
return False
|
|
313
|
-
|
|
314
|
-
@classmethod
|
|
315
|
-
def swap_cache_strategy(cls, name: str) -> bool:
|
|
316
|
-
"""Swap to a different global caching strategy."""
|
|
317
|
-
with cls._lock:
|
|
318
|
-
if name in cls._global_cache_strategies:
|
|
319
|
-
cls._default_cache_strategy = name
|
|
320
|
-
return True
|
|
321
|
-
return False
|
|
322
|
-
|
|
323
|
-
__all__ = ['StrategyRegistry']
|
|
324
|
-
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Package Strategies
|
|
3
|
-
|
|
4
|
-
All package strategy implementations.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
# Mapping strategies
|
|
8
|
-
from .package_mapping_manifest_first import ManifestFirstMapping
|
|
9
|
-
from .package_mapping_discovery_first import DiscoveryFirstMapping
|
|
10
|
-
from .package_mapping_hybrid import HybridMapping
|
|
11
|
-
|
|
12
|
-
# Policy strategies
|
|
13
|
-
from .package_policy_permissive import PermissivePolicy
|
|
14
|
-
from .package_policy_allow_list import AllowListPolicy
|
|
15
|
-
from .package_policy_deny_list import DenyListPolicy
|
|
16
|
-
|
|
17
|
-
# Timing strategies
|
|
18
|
-
from .package_timing_smart import SmartTiming
|
|
19
|
-
from .package_timing_full import FullTiming
|
|
20
|
-
from .package_timing_clean import CleanTiming
|
|
21
|
-
from .package_timing_temporary import TemporaryTiming
|
|
22
|
-
|
|
23
|
-
# Execution strategies
|
|
24
|
-
from .package_execution_pip import PipExecution
|
|
25
|
-
from .package_execution_wheel import WheelExecution
|
|
26
|
-
from .package_execution_cached import CachedExecution
|
|
27
|
-
from .package_execution_async import AsyncExecution
|
|
28
|
-
|
|
29
|
-
# Discovery strategies
|
|
30
|
-
from .package_discovery_file import FileBasedDiscovery
|
|
31
|
-
from .package_discovery_manifest import ManifestBasedDiscovery
|
|
32
|
-
from .package_discovery_hybrid import HybridDiscovery
|
|
33
|
-
|
|
34
|
-
__all__ = [
|
|
35
|
-
# Mapping strategies
|
|
36
|
-
'ManifestFirstMapping',
|
|
37
|
-
'DiscoveryFirstMapping',
|
|
38
|
-
'HybridMapping',
|
|
39
|
-
# Policy strategies
|
|
40
|
-
'PermissivePolicy',
|
|
41
|
-
'AllowListPolicy',
|
|
42
|
-
'DenyListPolicy',
|
|
43
|
-
# Timing strategies
|
|
44
|
-
'SmartTiming',
|
|
45
|
-
'FullTiming',
|
|
46
|
-
'CleanTiming',
|
|
47
|
-
'TemporaryTiming',
|
|
48
|
-
# Execution strategies
|
|
49
|
-
'PipExecution',
|
|
50
|
-
'WheelExecution',
|
|
51
|
-
'CachedExecution',
|
|
52
|
-
'AsyncExecution',
|
|
53
|
-
# Discovery strategies
|
|
54
|
-
'FileBasedDiscovery',
|
|
55
|
-
'ManifestBasedDiscovery',
|
|
56
|
-
'HybridDiscovery',
|
|
57
|
-
]
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
File-Based Discovery Strategy
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
File-based discovery - discovers dependencies from project files.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from ...package.base import ADiscoveryStrategy
|
|
16
|
-
|
|
17
|
-
class FileBasedDiscovery(ADiscoveryStrategy):
|
|
18
|
-
"""
|
|
19
|
-
File-based discovery strategy - discovers dependencies from project files.
|
|
20
|
-
|
|
21
|
-
Reads from pyproject.toml, requirements.txt, setup.py, etc.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def __init__(self, project_root: Optional[Path] = None):
|
|
25
|
-
"""
|
|
26
|
-
Initialize file-based discovery strategy.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
project_root: Project root directory (auto-detected if None)
|
|
30
|
-
"""
|
|
31
|
-
self._project_root = project_root or self._detect_project_root()
|
|
32
|
-
|
|
33
|
-
def _detect_project_root(self) -> Path:
|
|
34
|
-
"""Detect project root directory."""
|
|
35
|
-
import os
|
|
36
|
-
cwd = Path.cwd()
|
|
37
|
-
|
|
38
|
-
# Look for common project markers
|
|
39
|
-
markers = ['pyproject.toml', 'requirements.txt', 'setup.py', '.git']
|
|
40
|
-
for marker in markers:
|
|
41
|
-
current = cwd
|
|
42
|
-
for _ in range(5): # Search up to 5 levels
|
|
43
|
-
if (current / marker).exists():
|
|
44
|
-
return current
|
|
45
|
-
parent = current.parent
|
|
46
|
-
if parent == current: # Reached filesystem root
|
|
47
|
-
break
|
|
48
|
-
current = parent
|
|
49
|
-
|
|
50
|
-
return cwd
|
|
51
|
-
|
|
52
|
-
def discover(self, project_root: Any = None) -> dict[str, str]:
|
|
53
|
-
"""
|
|
54
|
-
Discover dependencies from project files.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
project_root: Optional project root (uses instance root if None)
|
|
58
|
-
|
|
59
|
-
Returns:
|
|
60
|
-
Dict mapping import_name -> package_name
|
|
61
|
-
"""
|
|
62
|
-
root = Path(project_root) if project_root else self._project_root
|
|
63
|
-
dependencies = {}
|
|
64
|
-
|
|
65
|
-
# Check pyproject.toml
|
|
66
|
-
pyproject = root / "pyproject.toml"
|
|
67
|
-
if pyproject.exists():
|
|
68
|
-
try:
|
|
69
|
-
import tomllib
|
|
70
|
-
except ImportError:
|
|
71
|
-
try:
|
|
72
|
-
import tomli as tomllib
|
|
73
|
-
except ImportError:
|
|
74
|
-
tomllib = None
|
|
75
|
-
|
|
76
|
-
if tomllib:
|
|
77
|
-
with open(pyproject, 'rb') as f:
|
|
78
|
-
data = tomllib.load(f)
|
|
79
|
-
project = data.get('project', {})
|
|
80
|
-
deps = project.get('dependencies', [])
|
|
81
|
-
for dep in deps:
|
|
82
|
-
# Parse dependency spec (e.g., "pandas>=1.0" -> "pandas")
|
|
83
|
-
package_name = dep.split('>=')[0].split('==')[0].split('!=')[0].strip()
|
|
84
|
-
# Use package name as import name (heuristic)
|
|
85
|
-
import_name = package_name.replace('-', '_')
|
|
86
|
-
dependencies[import_name] = package_name
|
|
87
|
-
|
|
88
|
-
# Check requirements.txt
|
|
89
|
-
requirements = root / "requirements.txt"
|
|
90
|
-
if requirements.exists():
|
|
91
|
-
with open(requirements, 'r', encoding='utf-8') as f:
|
|
92
|
-
for line in f:
|
|
93
|
-
line = line.strip()
|
|
94
|
-
if line and not line.startswith('#'):
|
|
95
|
-
# Parse requirement (e.g., "pandas>=1.0" -> "pandas")
|
|
96
|
-
package_name = line.split('>=')[0].split('==')[0].split('!=')[0].strip()
|
|
97
|
-
import_name = package_name.replace('-', '_')
|
|
98
|
-
dependencies[import_name] = package_name
|
|
99
|
-
|
|
100
|
-
return dependencies
|
|
101
|
-
|
|
102
|
-
def get_source(self, import_name: str) -> Optional[str]:
|
|
103
|
-
"""
|
|
104
|
-
Get the source of a discovered dependency.
|
|
105
|
-
|
|
106
|
-
Args:
|
|
107
|
-
import_name: Import name to check
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
Source file name or None
|
|
111
|
-
"""
|
|
112
|
-
root = self._project_root
|
|
113
|
-
|
|
114
|
-
# Check pyproject.toml
|
|
115
|
-
pyproject = root / "pyproject.toml"
|
|
116
|
-
if pyproject.exists():
|
|
117
|
-
deps = self.discover()
|
|
118
|
-
if import_name in deps:
|
|
119
|
-
return "pyproject.toml"
|
|
120
|
-
|
|
121
|
-
# Check requirements.txt
|
|
122
|
-
requirements = root / "requirements.txt"
|
|
123
|
-
if requirements.exists():
|
|
124
|
-
deps = self.discover()
|
|
125
|
-
if import_name in deps:
|
|
126
|
-
return "requirements.txt"
|
|
127
|
-
|
|
128
|
-
return None
|
|
129
|
-
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Hybrid Discovery Strategy
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Hybrid discovery - combines file-based and manifest-based discovery.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from ...package.base import ADiscoveryStrategy
|
|
16
|
-
from .package_discovery_file import FileBasedDiscovery
|
|
17
|
-
from .package_discovery_manifest import ManifestBasedDiscovery
|
|
18
|
-
|
|
19
|
-
class HybridDiscovery(ADiscoveryStrategy):
|
|
20
|
-
"""
|
|
21
|
-
Hybrid discovery strategy - combines file-based and manifest-based discovery.
|
|
22
|
-
|
|
23
|
-
Priority: Manifest > File-based
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self, package_name: str = 'default', project_root: Optional[Path] = None):
|
|
27
|
-
"""
|
|
28
|
-
Initialize hybrid discovery strategy.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
package_name: Package name for isolation
|
|
32
|
-
project_root: Project root directory (auto-detected if None)
|
|
33
|
-
"""
|
|
34
|
-
self._package_name = package_name
|
|
35
|
-
self._project_root = project_root
|
|
36
|
-
self._file_discovery = FileBasedDiscovery(project_root)
|
|
37
|
-
self._manifest_discovery = ManifestBasedDiscovery(package_name, project_root)
|
|
38
|
-
|
|
39
|
-
def discover(self, project_root: Any = None) -> dict[str, str]:
|
|
40
|
-
"""
|
|
41
|
-
Discover dependencies from all sources.
|
|
42
|
-
|
|
43
|
-
Priority: Manifest > File-based
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
project_root: Optional project root (uses instance root if None)
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
Dict mapping import_name -> package_name
|
|
50
|
-
"""
|
|
51
|
-
dependencies = {}
|
|
52
|
-
|
|
53
|
-
# First, get file-based dependencies
|
|
54
|
-
file_deps = self._file_discovery.discover(project_root)
|
|
55
|
-
dependencies.update(file_deps)
|
|
56
|
-
|
|
57
|
-
# Then, overlay manifest dependencies (takes precedence)
|
|
58
|
-
manifest_deps = self._manifest_discovery.discover(project_root)
|
|
59
|
-
dependencies.update(manifest_deps) # Manifest overrides file-based
|
|
60
|
-
|
|
61
|
-
return dependencies
|
|
62
|
-
|
|
63
|
-
def get_source(self, import_name: str) -> Optional[str]:
|
|
64
|
-
"""
|
|
65
|
-
Get the source of a discovered dependency.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
import_name: Import name to check
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
Source file name or "hybrid"
|
|
72
|
-
"""
|
|
73
|
-
# Check manifest first (higher priority)
|
|
74
|
-
manifest_source = self._manifest_discovery.get_source(import_name)
|
|
75
|
-
if manifest_source:
|
|
76
|
-
return manifest_source
|
|
77
|
-
|
|
78
|
-
# Check file-based
|
|
79
|
-
file_source = self._file_discovery.get_source(import_name)
|
|
80
|
-
if file_source:
|
|
81
|
-
return file_source
|
|
82
|
-
|
|
83
|
-
return None
|
|
84
|
-
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Manifest-Based Discovery Strategy
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Manifest-based discovery - discovers dependencies from manifest files.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from ...package.base import ADiscoveryStrategy
|
|
16
|
-
|
|
17
|
-
class ManifestBasedDiscovery(ADiscoveryStrategy):
|
|
18
|
-
"""
|
|
19
|
-
Manifest-based discovery strategy - discovers dependencies from manifest files.
|
|
20
|
-
|
|
21
|
-
Reads from xwlazy.manifest.json or pyproject.toml [tool.xwlazy] section.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def __init__(self, package_name: str = 'default', project_root: Optional[Path] = None):
|
|
25
|
-
"""
|
|
26
|
-
Initialize manifest-based discovery strategy.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
package_name: Package name for isolation
|
|
30
|
-
project_root: Project root directory (auto-detected if None)
|
|
31
|
-
"""
|
|
32
|
-
self._package_name = package_name
|
|
33
|
-
self._project_root = project_root or self._detect_project_root()
|
|
34
|
-
|
|
35
|
-
def _detect_project_root(self) -> Path:
|
|
36
|
-
"""Detect project root directory."""
|
|
37
|
-
import os
|
|
38
|
-
cwd = Path.cwd()
|
|
39
|
-
|
|
40
|
-
# Look for manifest files
|
|
41
|
-
markers = ['xwlazy.manifest.json', 'lazy.manifest.json', '.xwlazy.manifest.json', 'pyproject.toml']
|
|
42
|
-
for marker in markers:
|
|
43
|
-
current = cwd
|
|
44
|
-
for _ in range(5): # Search up to 5 levels
|
|
45
|
-
if (current / marker).exists():
|
|
46
|
-
return current
|
|
47
|
-
parent = current.parent
|
|
48
|
-
if parent == current: # Reached filesystem root
|
|
49
|
-
break
|
|
50
|
-
current = parent
|
|
51
|
-
|
|
52
|
-
return cwd
|
|
53
|
-
|
|
54
|
-
def discover(self, project_root: Any = None) -> dict[str, str]:
|
|
55
|
-
"""
|
|
56
|
-
Discover dependencies from manifest files.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
project_root: Optional project root (uses instance root if None)
|
|
60
|
-
|
|
61
|
-
Returns:
|
|
62
|
-
Dict mapping import_name -> package_name
|
|
63
|
-
"""
|
|
64
|
-
# Lazy import to avoid circular dependency
|
|
65
|
-
from ...package.manifest import get_manifest_loader
|
|
66
|
-
|
|
67
|
-
loader = get_manifest_loader()
|
|
68
|
-
manifest = loader.get_manifest(self._package_name)
|
|
69
|
-
|
|
70
|
-
if manifest and manifest.dependencies:
|
|
71
|
-
return manifest.dependencies.copy()
|
|
72
|
-
|
|
73
|
-
return {}
|
|
74
|
-
|
|
75
|
-
def get_source(self, import_name: str) -> Optional[str]:
|
|
76
|
-
"""
|
|
77
|
-
Get the source of a discovered dependency.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
import_name: Import name to check
|
|
81
|
-
|
|
82
|
-
Returns:
|
|
83
|
-
Source file name (e.g., "xwlazy.manifest.json")
|
|
84
|
-
"""
|
|
85
|
-
from ...package.manifest import get_manifest_loader
|
|
86
|
-
|
|
87
|
-
loader = get_manifest_loader()
|
|
88
|
-
manifest = loader.get_manifest(self._package_name)
|
|
89
|
-
|
|
90
|
-
if manifest and manifest.dependencies and import_name in manifest.dependencies:
|
|
91
|
-
# Try to find which file was used
|
|
92
|
-
root = self._project_root
|
|
93
|
-
for filename in ['xwlazy.manifest.json', 'lazy.manifest.json', '.xwlazy.manifest.json']:
|
|
94
|
-
if (root / filename).exists():
|
|
95
|
-
return filename
|
|
96
|
-
# Check pyproject.toml
|
|
97
|
-
if (root / "pyproject.toml").exists():
|
|
98
|
-
return "pyproject.toml [tool.xwlazy]"
|
|
99
|
-
|
|
100
|
-
return None
|
|
101
|
-
|