exonware-xwlazy 0.1.0.22__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 +86 -16
- exonware/xwlazy/version.py +5 -5
- exonware/xwlazy.py +2546 -0
- exonware/xwlazy_external_libs.toml +716 -0
- {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/METADATA +6 -6
- exonware_xwlazy-1.0.1.2.dist-info/RECORD +8 -0
- exonware/xwlazy/__init__.py +0 -367
- exonware/xwlazy/common/__init__.py +0 -47
- exonware/xwlazy/common/base.py +0 -56
- 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 -232
- exonware/xwlazy/common/services/install_async_utils.py +0 -165
- 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/config.py +0 -193
- exonware/xwlazy/contracts.py +0 -1396
- exonware/xwlazy/defs.py +0 -378
- exonware/xwlazy/errors.py +0 -276
- exonware/xwlazy/facade.py +0 -991
- exonware/xwlazy/module/__init__.py +0 -18
- exonware/xwlazy/module/base.py +0 -565
- exonware/xwlazy/module/data.py +0 -17
- exonware/xwlazy/module/facade.py +0 -246
- exonware/xwlazy/module/importer_engine.py +0 -2117
- 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 -798
- 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 -245
- exonware/xwlazy/package/services/discovery.py +0 -370
- exonware/xwlazy/package/services/host_packages.py +0 -145
- exonware/xwlazy/package/services/install_async.py +0 -277
- 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 -718
- exonware/xwlazy/package/services/manifest.py +0 -496
- exonware/xwlazy/package/services/strategy_registry.py +0 -186
- 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.22.dist-info/RECORD +0 -87
- {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/licenses/LICENSE +0 -0
exonware/xwlazy/defs.py
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
#exonware/xwlazy/src/exonware/xwlazy/defs.py
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
Generation Date: 10-Oct-2025
|
|
8
|
-
|
|
9
|
-
Type Definitions and Constants for Lazy Loading System
|
|
10
|
-
|
|
11
|
-
This module defines type definitions, constants, and TypedDict structures
|
|
12
|
-
for the lazy loading system following GUIDE_ARCH.md structure.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from enum import Enum
|
|
16
|
-
from typing import TypedDict, Dict, List, Optional, Any, Tuple
|
|
17
|
-
from dataclasses import dataclass, field
|
|
18
|
-
from types import ModuleType
|
|
19
|
-
|
|
20
|
-
# =============================================================================
|
|
21
|
-
# ENUMS
|
|
22
|
-
# =============================================================================
|
|
23
|
-
|
|
24
|
-
class LazyLoadMode(Enum):
|
|
25
|
-
"""Controls lazy module loading behavior."""
|
|
26
|
-
NONE = "none" # Standard imports (no lazy loading)
|
|
27
|
-
AUTO = "auto" # Lazy loading enabled (deferred module loading)
|
|
28
|
-
PRELOAD = "preload" # Preload all modules on start
|
|
29
|
-
BACKGROUND = "background" # Load modules in background threads
|
|
30
|
-
CACHED = "cached" # Cache loaded modules but allow unloading
|
|
31
|
-
# Superior performance modes
|
|
32
|
-
TURBO = "turbo" # Multi-tier cache + parallel preloading + bytecode caching
|
|
33
|
-
ADAPTIVE = "adaptive" # Self-optimizing with pattern learning
|
|
34
|
-
HYPERPARALLEL = "hyperparallel" # Maximum parallelism with multi-threading
|
|
35
|
-
STREAMING = "streaming" # Asynchronous background loading with streaming
|
|
36
|
-
ULTRA = "ultra" # Aggressive optimizations (pre-compiled bytecode, mmap, etc.)
|
|
37
|
-
INTELLIGENT = "intelligent" # Automatically switches to fastest mode based on load level
|
|
38
|
-
|
|
39
|
-
class LazyInstallMode(Enum):
|
|
40
|
-
"""Lazy installation modes."""
|
|
41
|
-
# Core modes
|
|
42
|
-
NONE = "none" # No auto-installation
|
|
43
|
-
SMART = "smart" # Install on first usage (on-demand) - replaces AUTO
|
|
44
|
-
FULL = "full" # Install all dependencies on start
|
|
45
|
-
CLEAN = "clean" # Install on usage + uninstall after completion
|
|
46
|
-
TEMPORARY = "temporary" # Always uninstall after use (more aggressive than CLEAN)
|
|
47
|
-
SIZE_AWARE = "size_aware" # Install small packages, skip large ones
|
|
48
|
-
|
|
49
|
-
# Special purpose modes (kept for specific use cases)
|
|
50
|
-
INTERACTIVE = "interactive" # Ask user before installing
|
|
51
|
-
WARN = "warn" # Log warning but don't install (for monitoring)
|
|
52
|
-
DISABLED = "disabled" # Don't install anything (alias for NONE, more explicit)
|
|
53
|
-
DRY_RUN = "dry_run" # Show what would be installed but don't install
|
|
54
|
-
|
|
55
|
-
class PathType(Enum):
|
|
56
|
-
"""Path types for validation."""
|
|
57
|
-
FILE = "file"
|
|
58
|
-
DIRECTORY = "directory"
|
|
59
|
-
UNKNOWN = "unknown"
|
|
60
|
-
|
|
61
|
-
class InstallStatus(Enum):
|
|
62
|
-
"""Installation status."""
|
|
63
|
-
PENDING = "pending"
|
|
64
|
-
RUNNING = "running"
|
|
65
|
-
SUCCESS = "success"
|
|
66
|
-
FAILED = "failed"
|
|
67
|
-
SKIPPED = "skipped"
|
|
68
|
-
|
|
69
|
-
class LoadLevel(Enum):
|
|
70
|
-
"""Load level categories."""
|
|
71
|
-
LIGHT = "light_load"
|
|
72
|
-
MEDIUM = "medium_load"
|
|
73
|
-
HEAVY = "heavy_load"
|
|
74
|
-
ENTERPRISE = "enterprise_load"
|
|
75
|
-
|
|
76
|
-
# =============================================================================
|
|
77
|
-
# DATACLASSES
|
|
78
|
-
# =============================================================================
|
|
79
|
-
|
|
80
|
-
@dataclass
|
|
81
|
-
class DependencyInfo:
|
|
82
|
-
"""Information about a discovered dependency."""
|
|
83
|
-
import_name: str
|
|
84
|
-
package_name: str
|
|
85
|
-
version: Optional[str] = None
|
|
86
|
-
source: str = "unknown"
|
|
87
|
-
category: str = "general"
|
|
88
|
-
|
|
89
|
-
@dataclass
|
|
90
|
-
class LazyModeConfig:
|
|
91
|
-
"""Two-dimensional lazy mode configuration combining load and install modes."""
|
|
92
|
-
load_mode: LazyLoadMode = LazyLoadMode.NONE
|
|
93
|
-
install_mode: LazyInstallMode = LazyInstallMode.NONE
|
|
94
|
-
|
|
95
|
-
# Additional configuration options
|
|
96
|
-
auto_uninstall_large: bool = False # For AUTO_MODE behavior
|
|
97
|
-
large_package_threshold_mb: float = 50.0 # Size threshold for SIZE_AWARE mode
|
|
98
|
-
preload_priority: List[str] = field(default_factory=list) # Priority modules for PRELOAD
|
|
99
|
-
background_workers: int = 2 # Workers for BACKGROUND mode
|
|
100
|
-
|
|
101
|
-
def __post_init__(self):
|
|
102
|
-
"""Normalize enum values."""
|
|
103
|
-
if isinstance(self.load_mode, str):
|
|
104
|
-
self.load_mode = LazyLoadMode(self.load_mode)
|
|
105
|
-
if isinstance(self.install_mode, str):
|
|
106
|
-
self.install_mode = LazyInstallMode(self.install_mode)
|
|
107
|
-
|
|
108
|
-
@dataclass
|
|
109
|
-
class InstallResult:
|
|
110
|
-
"""Result of an installation operation."""
|
|
111
|
-
package_name: str
|
|
112
|
-
success: bool
|
|
113
|
-
status: InstallStatus
|
|
114
|
-
error: Optional[str] = None
|
|
115
|
-
version: Optional[str] = None
|
|
116
|
-
source: Optional[str] = None # "cache", "pip", "wheel", etc.
|
|
117
|
-
|
|
118
|
-
@dataclass
|
|
119
|
-
class LazyConfig:
|
|
120
|
-
"""Bridge configuration settings with the lazy package implementation."""
|
|
121
|
-
packages: Tuple[str, ...] = field(
|
|
122
|
-
default_factory=lambda: ("default",)
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
def __post_init__(self) -> None:
|
|
126
|
-
self.packages = tuple(package.lower() for package in self.packages)
|
|
127
|
-
|
|
128
|
-
@dataclass(frozen=True)
|
|
129
|
-
class PackageManifest:
|
|
130
|
-
"""Resolved manifest data for a single package."""
|
|
131
|
-
package: str
|
|
132
|
-
dependencies: Dict[str, str] = field(default_factory=dict)
|
|
133
|
-
watched_prefixes: Tuple[str, ...] = ()
|
|
134
|
-
async_installs: bool = False
|
|
135
|
-
async_workers: int = 1
|
|
136
|
-
class_wrap_prefixes: Tuple[str, ...] = ()
|
|
137
|
-
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
138
|
-
|
|
139
|
-
def get_dependency(self, import_name: str) -> Optional[str]:
|
|
140
|
-
"""Return the declared package for the given import name."""
|
|
141
|
-
if not import_name:
|
|
142
|
-
return None
|
|
143
|
-
direct = self.dependencies.get(import_name)
|
|
144
|
-
if direct is not None:
|
|
145
|
-
return direct
|
|
146
|
-
# Case-insensitive fallback for convenience
|
|
147
|
-
return self.dependencies.get(import_name.lower())
|
|
148
|
-
|
|
149
|
-
@dataclass(frozen=True)
|
|
150
|
-
class PackageData:
|
|
151
|
-
"""
|
|
152
|
-
Immutable package data - same across all strategies.
|
|
153
|
-
|
|
154
|
-
This data structure is used by all package caching, helper, and manager strategies.
|
|
155
|
-
"""
|
|
156
|
-
name: str
|
|
157
|
-
version: Optional[str] = None
|
|
158
|
-
installed: bool = False
|
|
159
|
-
install_time: Optional[float] = None
|
|
160
|
-
access_count: int = 0
|
|
161
|
-
install_mode: Optional['LazyInstallMode'] = None
|
|
162
|
-
error: Optional[Exception] = None
|
|
163
|
-
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
164
|
-
|
|
165
|
-
@dataclass(frozen=True)
|
|
166
|
-
class ModuleData:
|
|
167
|
-
"""
|
|
168
|
-
Immutable module data - same across all strategies.
|
|
169
|
-
|
|
170
|
-
This data structure is used by all module caching, helper, and manager strategies.
|
|
171
|
-
"""
|
|
172
|
-
path: str
|
|
173
|
-
loaded_module: Optional['ModuleType'] = None
|
|
174
|
-
loading: bool = False
|
|
175
|
-
load_time: Optional[float] = None
|
|
176
|
-
access_count: int = 0
|
|
177
|
-
error: Optional[Exception] = None
|
|
178
|
-
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
179
|
-
|
|
180
|
-
# =============================================================================
|
|
181
|
-
# TYPE DEFINITIONS
|
|
182
|
-
# =============================================================================
|
|
183
|
-
|
|
184
|
-
class DependencyMapping(TypedDict, total=False):
|
|
185
|
-
"""Type definition for dependency mapping structure."""
|
|
186
|
-
import_name: str
|
|
187
|
-
package_name: str
|
|
188
|
-
version: Optional[str]
|
|
189
|
-
source: str
|
|
190
|
-
category: str
|
|
191
|
-
|
|
192
|
-
class PackageStats(TypedDict, total=False):
|
|
193
|
-
"""Type definition for package statistics."""
|
|
194
|
-
enabled: bool
|
|
195
|
-
mode: str
|
|
196
|
-
package_name: str
|
|
197
|
-
installed_packages: List[str]
|
|
198
|
-
failed_packages: List[str]
|
|
199
|
-
total_installed: int
|
|
200
|
-
total_failed: int
|
|
201
|
-
|
|
202
|
-
class LazyStatus(TypedDict, total=False):
|
|
203
|
-
"""Type definition for lazy mode status."""
|
|
204
|
-
enabled: bool
|
|
205
|
-
hook_installed: bool
|
|
206
|
-
lazy_install_enabled: bool
|
|
207
|
-
active: bool
|
|
208
|
-
error: Optional[str]
|
|
209
|
-
|
|
210
|
-
# =============================================================================
|
|
211
|
-
# CONSTANTS
|
|
212
|
-
# =============================================================================
|
|
213
|
-
|
|
214
|
-
# Default configuration values
|
|
215
|
-
DEFAULT_LARGE_PACKAGE_THRESHOLD_MB: float = 50.0
|
|
216
|
-
DEFAULT_BACKGROUND_WORKERS: int = 2
|
|
217
|
-
DEFAULT_PRELOAD_PRIORITY: List[str] = []
|
|
218
|
-
|
|
219
|
-
# Common import -> package mappings (will be populated from discovery)
|
|
220
|
-
COMMON_IMPORT_MAPPINGS: Dict[str, str] = {
|
|
221
|
-
# Common mappings that are frequently used
|
|
222
|
-
'cv2': 'opencv-python',
|
|
223
|
-
'PIL': 'Pillow',
|
|
224
|
-
'yaml': 'PyYAML',
|
|
225
|
-
'toml': 'toml',
|
|
226
|
-
'pandas': 'pandas',
|
|
227
|
-
'numpy': 'numpy',
|
|
228
|
-
'scipy': 'scipy',
|
|
229
|
-
'sklearn': 'scikit-learn',
|
|
230
|
-
'matplotlib': 'matplotlib',
|
|
231
|
-
'seaborn': 'seaborn',
|
|
232
|
-
'requests': 'requests',
|
|
233
|
-
'urllib3': 'urllib3',
|
|
234
|
-
'bs4': 'beautifulsoup4',
|
|
235
|
-
'lxml': 'lxml',
|
|
236
|
-
'jinja2': 'Jinja2',
|
|
237
|
-
'flask': 'Flask',
|
|
238
|
-
'django': 'Django',
|
|
239
|
-
'fastapi': 'fastapi',
|
|
240
|
-
'pydantic': 'pydantic',
|
|
241
|
-
'sqlalchemy': 'SQLAlchemy',
|
|
242
|
-
'psycopg2': 'psycopg2-binary',
|
|
243
|
-
'pymongo': 'pymongo',
|
|
244
|
-
'redis': 'redis',
|
|
245
|
-
'celery': 'celery',
|
|
246
|
-
'boto3': 'boto3',
|
|
247
|
-
'azure': 'azure-storage-blob',
|
|
248
|
-
'google': 'google-cloud-storage',
|
|
249
|
-
'tensorflow': 'tensorflow',
|
|
250
|
-
'torch': 'torch',
|
|
251
|
-
'transformers': 'transformers',
|
|
252
|
-
'openai': 'openai',
|
|
253
|
-
'anthropic': 'anthropic',
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
# Package discovery source names
|
|
257
|
-
DISCOVERY_SOURCE_PYPROJECT = "pyproject.toml"
|
|
258
|
-
DISCOVERY_SOURCE_REQUIREMENTS = "requirements.txt"
|
|
259
|
-
DISCOVERY_SOURCE_SETUP = "setup.py"
|
|
260
|
-
DISCOVERY_SOURCE_POETRY = "poetry.lock"
|
|
261
|
-
DISCOVERY_SOURCE_PIPFILE = "Pipfile"
|
|
262
|
-
DISCOVERY_SOURCE_MANIFEST = "manifest.json"
|
|
263
|
-
|
|
264
|
-
# Installation mode aliases
|
|
265
|
-
INSTALL_MODE_ALIASES: Dict[str, str] = {
|
|
266
|
-
'auto': 'smart',
|
|
267
|
-
'on_demand': 'smart',
|
|
268
|
-
'on-demand': 'smart',
|
|
269
|
-
'lazy': 'smart',
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
# Cache keys
|
|
273
|
-
CACHE_KEY_DEPENDENCIES = "dependencies"
|
|
274
|
-
CACHE_KEY_PACKAGE_INFO = "package_info"
|
|
275
|
-
CACHE_KEY_INSTALL_STATUS = "install_status"
|
|
276
|
-
CACHE_KEY_DISCOVERY_SOURCES = "discovery_sources"
|
|
277
|
-
|
|
278
|
-
# File patterns for discovery
|
|
279
|
-
PYPROJECT_PATTERN = "pyproject.toml"
|
|
280
|
-
REQUIREMENTS_PATTERN = "requirements*.txt"
|
|
281
|
-
SETUP_PATTERN = "setup.py"
|
|
282
|
-
POETRY_PATTERN = "poetry.lock"
|
|
283
|
-
PIPFILE_PATTERN = "Pipfile"
|
|
284
|
-
|
|
285
|
-
# =============================================================================
|
|
286
|
-
# PRESET MODE MAPPINGS
|
|
287
|
-
# =============================================================================
|
|
288
|
-
|
|
289
|
-
# Preset mode combinations for convenience
|
|
290
|
-
PRESET_MODES: Dict[str, LazyModeConfig] = {
|
|
291
|
-
"none": LazyModeConfig(
|
|
292
|
-
load_mode=LazyLoadMode.NONE,
|
|
293
|
-
install_mode=LazyInstallMode.NONE
|
|
294
|
-
),
|
|
295
|
-
"lite": LazyModeConfig(
|
|
296
|
-
load_mode=LazyLoadMode.AUTO,
|
|
297
|
-
install_mode=LazyInstallMode.NONE
|
|
298
|
-
),
|
|
299
|
-
"smart": LazyModeConfig(
|
|
300
|
-
load_mode=LazyLoadMode.AUTO,
|
|
301
|
-
install_mode=LazyInstallMode.SMART
|
|
302
|
-
),
|
|
303
|
-
"full": LazyModeConfig(
|
|
304
|
-
load_mode=LazyLoadMode.AUTO,
|
|
305
|
-
install_mode=LazyInstallMode.FULL
|
|
306
|
-
),
|
|
307
|
-
"clean": LazyModeConfig(
|
|
308
|
-
load_mode=LazyLoadMode.AUTO,
|
|
309
|
-
install_mode=LazyInstallMode.CLEAN
|
|
310
|
-
),
|
|
311
|
-
"temporary": LazyModeConfig(
|
|
312
|
-
load_mode=LazyLoadMode.AUTO,
|
|
313
|
-
install_mode=LazyInstallMode.TEMPORARY
|
|
314
|
-
),
|
|
315
|
-
"size_aware": LazyModeConfig(
|
|
316
|
-
load_mode=LazyLoadMode.AUTO,
|
|
317
|
-
install_mode=LazyInstallMode.SIZE_AWARE
|
|
318
|
-
),
|
|
319
|
-
"auto": LazyModeConfig(
|
|
320
|
-
load_mode=LazyLoadMode.AUTO,
|
|
321
|
-
install_mode=LazyInstallMode.SMART,
|
|
322
|
-
auto_uninstall_large=True
|
|
323
|
-
),
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
def get_preset_mode(preset_name: str) -> Optional[LazyModeConfig]:
|
|
327
|
-
"""Get preset mode configuration by name."""
|
|
328
|
-
return PRESET_MODES.get(preset_name.lower())
|
|
329
|
-
|
|
330
|
-
# =============================================================================
|
|
331
|
-
# EXPORT ALL
|
|
332
|
-
# =============================================================================
|
|
333
|
-
|
|
334
|
-
__all__ = [
|
|
335
|
-
# Enums
|
|
336
|
-
'LazyLoadMode',
|
|
337
|
-
'LazyInstallMode',
|
|
338
|
-
'PathType',
|
|
339
|
-
'InstallStatus',
|
|
340
|
-
'LoadLevel',
|
|
341
|
-
# Dataclasses
|
|
342
|
-
'DependencyInfo',
|
|
343
|
-
'LazyModeConfig',
|
|
344
|
-
'InstallResult',
|
|
345
|
-
'LazyConfig',
|
|
346
|
-
'PackageManifest',
|
|
347
|
-
'PackageData',
|
|
348
|
-
'ModuleData',
|
|
349
|
-
# Type definitions
|
|
350
|
-
'DependencyMapping',
|
|
351
|
-
'PackageStats',
|
|
352
|
-
'LazyStatus',
|
|
353
|
-
# Constants
|
|
354
|
-
'DEFAULT_LARGE_PACKAGE_THRESHOLD_MB',
|
|
355
|
-
'DEFAULT_BACKGROUND_WORKERS',
|
|
356
|
-
'DEFAULT_PRELOAD_PRIORITY',
|
|
357
|
-
'COMMON_IMPORT_MAPPINGS',
|
|
358
|
-
'DISCOVERY_SOURCE_PYPROJECT',
|
|
359
|
-
'DISCOVERY_SOURCE_REQUIREMENTS',
|
|
360
|
-
'DISCOVERY_SOURCE_SETUP',
|
|
361
|
-
'DISCOVERY_SOURCE_POETRY',
|
|
362
|
-
'DISCOVERY_SOURCE_PIPFILE',
|
|
363
|
-
'DISCOVERY_SOURCE_MANIFEST',
|
|
364
|
-
'INSTALL_MODE_ALIASES',
|
|
365
|
-
'CACHE_KEY_DEPENDENCIES',
|
|
366
|
-
'CACHE_KEY_PACKAGE_INFO',
|
|
367
|
-
'CACHE_KEY_INSTALL_STATUS',
|
|
368
|
-
'CACHE_KEY_DISCOVERY_SOURCES',
|
|
369
|
-
'PYPROJECT_PATTERN',
|
|
370
|
-
'REQUIREMENTS_PATTERN',
|
|
371
|
-
'SETUP_PATTERN',
|
|
372
|
-
'POETRY_PATTERN',
|
|
373
|
-
'PIPFILE_PATTERN',
|
|
374
|
-
# Preset modes
|
|
375
|
-
'PRESET_MODES',
|
|
376
|
-
'get_preset_mode',
|
|
377
|
-
]
|
|
378
|
-
|
exonware/xwlazy/errors.py
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
#exonware/xwlazy/src/exonware/xwlazy/errors.py
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 10-Oct-2025
|
|
9
|
-
|
|
10
|
-
Errors for Lazy Loading System
|
|
11
|
-
|
|
12
|
-
This module defines all exception classes for the lazy loading system
|
|
13
|
-
following GUIDE_ARCH.md structure.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
from typing import Optional, Any
|
|
17
|
-
|
|
18
|
-
# =============================================================================
|
|
19
|
-
# BASE EXCEPTION
|
|
20
|
-
# =============================================================================
|
|
21
|
-
|
|
22
|
-
class LazySystemError(Exception):
|
|
23
|
-
"""
|
|
24
|
-
Base exception for all lazy system errors.
|
|
25
|
-
|
|
26
|
-
All lazy system exceptions inherit from this for easy error handling.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
def __init__(self, message: str, package_name: Optional[str] = None):
|
|
30
|
-
"""
|
|
31
|
-
Initialize lazy system error.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
message: Error message
|
|
35
|
-
package_name: Optional package name for scoped errors
|
|
36
|
-
"""
|
|
37
|
-
self.package_name = package_name
|
|
38
|
-
if package_name:
|
|
39
|
-
message = f"[{package_name}] {message}"
|
|
40
|
-
super().__init__(message)
|
|
41
|
-
|
|
42
|
-
# =============================================================================
|
|
43
|
-
# SPECIFIC EXCEPTIONS
|
|
44
|
-
# =============================================================================
|
|
45
|
-
|
|
46
|
-
class LazyInstallError(LazySystemError):
|
|
47
|
-
"""
|
|
48
|
-
Raised when package installation fails.
|
|
49
|
-
|
|
50
|
-
Examples:
|
|
51
|
-
- pip install command fails
|
|
52
|
-
- Package not found in PyPI
|
|
53
|
-
- Network error during installation
|
|
54
|
-
"""
|
|
55
|
-
pass
|
|
56
|
-
|
|
57
|
-
class LazyDiscoveryError(LazySystemError):
|
|
58
|
-
"""
|
|
59
|
-
Raised when dependency discovery fails.
|
|
60
|
-
|
|
61
|
-
Examples:
|
|
62
|
-
- Cannot read pyproject.toml
|
|
63
|
-
- Invalid TOML syntax
|
|
64
|
-
- Missing dependency configuration
|
|
65
|
-
"""
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
|
-
class LazyHookError(LazySystemError):
|
|
69
|
-
"""
|
|
70
|
-
Raised when import hook operation fails.
|
|
71
|
-
|
|
72
|
-
Examples:
|
|
73
|
-
- Cannot install hook in sys.meta_path
|
|
74
|
-
- Hook is already installed
|
|
75
|
-
- Hook interception fails
|
|
76
|
-
"""
|
|
77
|
-
pass
|
|
78
|
-
|
|
79
|
-
class LazySecurityError(LazySystemError):
|
|
80
|
-
"""
|
|
81
|
-
Raised when security policy is violated.
|
|
82
|
-
|
|
83
|
-
Examples:
|
|
84
|
-
- Package not in allow list
|
|
85
|
-
- Package in deny list
|
|
86
|
-
- Untrusted package source
|
|
87
|
-
"""
|
|
88
|
-
pass
|
|
89
|
-
|
|
90
|
-
class ExternallyManagedError(LazyInstallError):
|
|
91
|
-
"""
|
|
92
|
-
Raised when environment is externally managed (PEP 668).
|
|
93
|
-
|
|
94
|
-
This happens when the Python environment has an EXTERNALLY-MANAGED
|
|
95
|
-
marker file, preventing pip installations. Common in system Python
|
|
96
|
-
installations on Linux distributions.
|
|
97
|
-
|
|
98
|
-
Solutions:
|
|
99
|
-
1. Use a virtual environment
|
|
100
|
-
2. Use pipx for isolated installations
|
|
101
|
-
3. Override with --break-system-packages (not recommended)
|
|
102
|
-
"""
|
|
103
|
-
|
|
104
|
-
def __init__(self, package_name: str):
|
|
105
|
-
"""
|
|
106
|
-
Initialize externally managed error.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
package_name: Package that cannot be installed
|
|
110
|
-
"""
|
|
111
|
-
message = (
|
|
112
|
-
f"Cannot install '{package_name}': Environment is externally managed (PEP 668). "
|
|
113
|
-
f"Please use a virtual environment or pipx."
|
|
114
|
-
)
|
|
115
|
-
super().__init__(message, package_name=None)
|
|
116
|
-
|
|
117
|
-
class DeferredImportError(Exception):
|
|
118
|
-
"""
|
|
119
|
-
Placeholder for a failed import that will be retried when accessed.
|
|
120
|
-
|
|
121
|
-
This enables two-stage lazy loading:
|
|
122
|
-
- Stage 1: Import fails → Return DeferredImportError placeholder
|
|
123
|
-
- Stage 2: On first use → Install missing package and replace with real module
|
|
124
|
-
|
|
125
|
-
Performance optimized:
|
|
126
|
-
- Zero overhead until user actually accesses the deferred import
|
|
127
|
-
- Only installs dependencies when truly needed
|
|
128
|
-
- Caches successful imports to avoid repeated installs
|
|
129
|
-
|
|
130
|
-
Note: This is both an error class and a proxy object. It stays in
|
|
131
|
-
errors.py because it represents an error state, but acts as
|
|
132
|
-
a proxy until resolved.
|
|
133
|
-
"""
|
|
134
|
-
|
|
135
|
-
__slots__ = (
|
|
136
|
-
'_import_name',
|
|
137
|
-
'_original_error',
|
|
138
|
-
'_installer_package',
|
|
139
|
-
'_retry_attempted',
|
|
140
|
-
'_real_module',
|
|
141
|
-
'_async_handle',
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
def __init__(
|
|
145
|
-
self,
|
|
146
|
-
import_name: str,
|
|
147
|
-
original_error: Exception,
|
|
148
|
-
installer_package: str,
|
|
149
|
-
async_handle: Optional[Any] = None,
|
|
150
|
-
):
|
|
151
|
-
"""
|
|
152
|
-
Initialize deferred import placeholder.
|
|
153
|
-
|
|
154
|
-
Args:
|
|
155
|
-
import_name: Name of the module that failed to import (e.g., 'fastavro')
|
|
156
|
-
original_error: The original ImportError that was caught
|
|
157
|
-
installer_package: Package name to use for lazy installation (e.g., 'xwsystem')
|
|
158
|
-
"""
|
|
159
|
-
self._import_name = import_name
|
|
160
|
-
self._original_error = original_error
|
|
161
|
-
self._installer_package = installer_package
|
|
162
|
-
self._retry_attempted = False
|
|
163
|
-
self._real_module = None
|
|
164
|
-
self._async_handle = async_handle
|
|
165
|
-
super().__init__(f"Deferred import: {import_name}")
|
|
166
|
-
|
|
167
|
-
def _try_install_and_import(self):
|
|
168
|
-
"""
|
|
169
|
-
Attempt to install missing package and import it.
|
|
170
|
-
|
|
171
|
-
Returns:
|
|
172
|
-
The real module if installation succeeds
|
|
173
|
-
|
|
174
|
-
Raises:
|
|
175
|
-
Original ImportError if installation fails or is disabled
|
|
176
|
-
"""
|
|
177
|
-
# Import from facade and new structure
|
|
178
|
-
from .facade import lazy_import_with_install, is_lazy_install_enabled
|
|
179
|
-
from .common.logger import get_logger
|
|
180
|
-
|
|
181
|
-
logger = get_logger("xwlazy")
|
|
182
|
-
logger.info(f"[STAGE 2] _try_install_and_import called for '{self._import_name}'")
|
|
183
|
-
|
|
184
|
-
# Return cached module if already installed
|
|
185
|
-
if self._real_module is not None:
|
|
186
|
-
logger.info(f"[STAGE 2] Using cached module for '{self._import_name}'")
|
|
187
|
-
return self._real_module
|
|
188
|
-
|
|
189
|
-
# Only try once to avoid repeated failures
|
|
190
|
-
if self._retry_attempted:
|
|
191
|
-
logger.warning(f"[STAGE 2] Already attempted installation for '{self._import_name}', raising original error")
|
|
192
|
-
raise self._original_error
|
|
193
|
-
|
|
194
|
-
self._retry_attempted = True
|
|
195
|
-
|
|
196
|
-
if self._async_handle is not None:
|
|
197
|
-
logger.info(f"[STAGE 2] Waiting for async install of '{self._import_name}' to finish")
|
|
198
|
-
self._async_handle.wait()
|
|
199
|
-
|
|
200
|
-
# After async install, try simple import first (cache invalidation needed)
|
|
201
|
-
try:
|
|
202
|
-
import importlib
|
|
203
|
-
import sys
|
|
204
|
-
importlib.invalidate_caches()
|
|
205
|
-
sys.path_importer_cache.clear()
|
|
206
|
-
module = importlib.import_module(self._import_name)
|
|
207
|
-
self._real_module = module
|
|
208
|
-
logger.info(f"✅ [STAGE 2] Successfully loaded '{self._import_name}' after async install")
|
|
209
|
-
return module
|
|
210
|
-
except ImportError:
|
|
211
|
-
pass # Fall through to lazy_import_with_install
|
|
212
|
-
|
|
213
|
-
if not is_lazy_install_enabled(self._installer_package):
|
|
214
|
-
logger.warning(f"[STAGE 2] Lazy install disabled for {self._installer_package}, cannot load {self._import_name}")
|
|
215
|
-
raise self._original_error
|
|
216
|
-
|
|
217
|
-
logger.info(f"⏳ [STAGE 2] Installing '{self._import_name}' on first use...")
|
|
218
|
-
|
|
219
|
-
# Try to install and import
|
|
220
|
-
module, success = lazy_import_with_install(
|
|
221
|
-
self._import_name,
|
|
222
|
-
installer_package=self._installer_package
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
if success and module:
|
|
226
|
-
self._real_module = module
|
|
227
|
-
logger.info(f"✅ [STAGE 2] Successfully installed and loaded '{self._import_name}'")
|
|
228
|
-
return module
|
|
229
|
-
else:
|
|
230
|
-
logger.error(f"❌ [STAGE 2] Failed to install '{self._import_name}'")
|
|
231
|
-
raise self._original_error
|
|
232
|
-
|
|
233
|
-
def __call__(self, *args, **kwargs):
|
|
234
|
-
"""
|
|
235
|
-
When user tries to instantiate, install dependency first.
|
|
236
|
-
|
|
237
|
-
This enables: serializer = AvroSerializer() → installs fastavro → creates instance
|
|
238
|
-
"""
|
|
239
|
-
module = self._try_install_and_import()
|
|
240
|
-
# If module is callable (a class), instantiate it
|
|
241
|
-
if callable(module):
|
|
242
|
-
return module(*args, **kwargs)
|
|
243
|
-
return module
|
|
244
|
-
|
|
245
|
-
def __getattr__(self, name):
|
|
246
|
-
"""
|
|
247
|
-
When user accesses attributes, install dependency first.
|
|
248
|
-
|
|
249
|
-
This enables: from fastavro import reader → installs fastavro → returns reader
|
|
250
|
-
"""
|
|
251
|
-
module = self._try_install_and_import()
|
|
252
|
-
return getattr(module, name)
|
|
253
|
-
|
|
254
|
-
def __repr__(self):
|
|
255
|
-
"""Show helpful message about deferred import."""
|
|
256
|
-
if self._real_module is not None:
|
|
257
|
-
return f"<DeferredImport: {self._import_name} (loaded)>"
|
|
258
|
-
return f"<DeferredImport: {self._import_name} (will install on first use)>"
|
|
259
|
-
|
|
260
|
-
# =============================================================================
|
|
261
|
-
# EXPORT ALL
|
|
262
|
-
# =============================================================================
|
|
263
|
-
|
|
264
|
-
__all__ = [
|
|
265
|
-
# Base exception
|
|
266
|
-
'LazySystemError',
|
|
267
|
-
# Specific exceptions
|
|
268
|
-
'LazyInstallError',
|
|
269
|
-
'LazyDiscoveryError',
|
|
270
|
-
'LazyHookError',
|
|
271
|
-
'LazySecurityError',
|
|
272
|
-
'ExternallyManagedError',
|
|
273
|
-
# Two-stage loading
|
|
274
|
-
'DeferredImportError',
|
|
275
|
-
]
|
|
276
|
-
|