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.
Files changed (96) 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/strategies/__init__.py +28 -0
  14. exonware/xwlazy/common/strategies/caching_dict.py +45 -0
  15. exonware/xwlazy/common/strategies/caching_installation.py +89 -0
  16. exonware/xwlazy/common/strategies/caching_lfu.py +67 -0
  17. exonware/xwlazy/common/strategies/caching_lru.py +64 -0
  18. exonware/xwlazy/common/strategies/caching_multitier.py +60 -0
  19. exonware/xwlazy/common/strategies/caching_ttl.py +60 -0
  20. {xwlazy/lazy → exonware/xwlazy}/config.py +52 -20
  21. exonware/xwlazy/contracts.py +1410 -0
  22. exonware/xwlazy/defs.py +397 -0
  23. xwlazy/lazy/lazy_errors.py → exonware/xwlazy/errors.py +21 -8
  24. exonware/xwlazy/facade.py +1049 -0
  25. exonware/xwlazy/module/__init__.py +18 -0
  26. exonware/xwlazy/module/base.py +569 -0
  27. exonware/xwlazy/module/data.py +17 -0
  28. exonware/xwlazy/module/facade.py +247 -0
  29. exonware/xwlazy/module/importer_engine.py +2161 -0
  30. exonware/xwlazy/module/strategies/__init__.py +22 -0
  31. exonware/xwlazy/module/strategies/module_helper_lazy.py +94 -0
  32. exonware/xwlazy/module/strategies/module_helper_simple.py +66 -0
  33. exonware/xwlazy/module/strategies/module_manager_advanced.py +112 -0
  34. exonware/xwlazy/module/strategies/module_manager_simple.py +96 -0
  35. exonware/xwlazy/package/__init__.py +18 -0
  36. exonware/xwlazy/package/base.py +807 -0
  37. xwlazy/lazy/host_conf.py → exonware/xwlazy/package/conf.py +62 -10
  38. exonware/xwlazy/package/data.py +17 -0
  39. exonware/xwlazy/package/facade.py +481 -0
  40. exonware/xwlazy/package/services/__init__.py +84 -0
  41. exonware/xwlazy/package/services/async_install_handle.py +89 -0
  42. exonware/xwlazy/package/services/config_manager.py +246 -0
  43. exonware/xwlazy/package/services/discovery.py +374 -0
  44. {xwlazy/lazy → exonware/xwlazy/package/services}/host_packages.py +43 -16
  45. exonware/xwlazy/package/services/install_async.py +278 -0
  46. exonware/xwlazy/package/services/install_cache.py +146 -0
  47. exonware/xwlazy/package/services/install_interactive.py +60 -0
  48. exonware/xwlazy/package/services/install_policy.py +158 -0
  49. exonware/xwlazy/package/services/install_registry.py +56 -0
  50. exonware/xwlazy/package/services/install_result.py +17 -0
  51. exonware/xwlazy/package/services/install_sbom.py +154 -0
  52. exonware/xwlazy/package/services/install_utils.py +83 -0
  53. exonware/xwlazy/package/services/installer_engine.py +408 -0
  54. exonware/xwlazy/package/services/lazy_installer.py +720 -0
  55. {xwlazy/lazy → exonware/xwlazy/package/services}/manifest.py +42 -25
  56. exonware/xwlazy/package/services/strategy_registry.py +188 -0
  57. exonware/xwlazy/package/strategies/__init__.py +57 -0
  58. exonware/xwlazy/package/strategies/package_discovery_file.py +130 -0
  59. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +85 -0
  60. exonware/xwlazy/package/strategies/package_discovery_manifest.py +102 -0
  61. exonware/xwlazy/package/strategies/package_execution_async.py +114 -0
  62. exonware/xwlazy/package/strategies/package_execution_cached.py +91 -0
  63. exonware/xwlazy/package/strategies/package_execution_pip.py +100 -0
  64. exonware/xwlazy/package/strategies/package_execution_wheel.py +107 -0
  65. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +101 -0
  66. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +106 -0
  67. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +101 -0
  68. exonware/xwlazy/package/strategies/package_policy_allow_list.py +58 -0
  69. exonware/xwlazy/package/strategies/package_policy_deny_list.py +58 -0
  70. exonware/xwlazy/package/strategies/package_policy_permissive.py +47 -0
  71. exonware/xwlazy/package/strategies/package_timing_clean.py +68 -0
  72. exonware/xwlazy/package/strategies/package_timing_full.py +67 -0
  73. exonware/xwlazy/package/strategies/package_timing_smart.py +69 -0
  74. exonware/xwlazy/package/strategies/package_timing_temporary.py +67 -0
  75. exonware/xwlazy/runtime/__init__.py +18 -0
  76. exonware/xwlazy/runtime/adaptive_learner.py +131 -0
  77. exonware/xwlazy/runtime/base.py +276 -0
  78. exonware/xwlazy/runtime/facade.py +95 -0
  79. exonware/xwlazy/runtime/intelligent_selector.py +173 -0
  80. exonware/xwlazy/runtime/metrics.py +64 -0
  81. exonware/xwlazy/runtime/performance.py +39 -0
  82. exonware/xwlazy/version.py +2 -2
  83. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/METADATA +86 -10
  84. exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
  85. exonware_xwlazy-0.1.0.11.dist-info/RECORD +0 -20
  86. xwlazy/__init__.py +0 -34
  87. xwlazy/lazy/__init__.py +0 -301
  88. xwlazy/lazy/bootstrap.py +0 -106
  89. xwlazy/lazy/lazy_base.py +0 -465
  90. xwlazy/lazy/lazy_contracts.py +0 -290
  91. xwlazy/lazy/lazy_core.py +0 -3727
  92. xwlazy/lazy/logging_utils.py +0 -194
  93. xwlazy/version.py +0 -77
  94. /xwlazy/lazy/lazy_state.py → /exonware/xwlazy/common/services/state_manager.py +0 -0
  95. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
  96. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,397 @@
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
+ Version: 0.1.0.19
8
+ Generation Date: 10-Oct-2025
9
+
10
+ Type Definitions and Constants for Lazy Loading System
11
+
12
+ This module defines type definitions, constants, and TypedDict structures
13
+ for the lazy loading system following GUIDE_ARCH.md structure.
14
+ """
15
+
16
+ from enum import Enum
17
+ from typing import TypedDict, Dict, List, Optional, Any, Tuple
18
+ from dataclasses import dataclass, field
19
+ from types import ModuleType
20
+
21
+
22
+ # =============================================================================
23
+ # ENUMS
24
+ # =============================================================================
25
+
26
+ class LazyLoadMode(Enum):
27
+ """Controls lazy module loading behavior."""
28
+ NONE = "none" # Standard imports (no lazy loading)
29
+ AUTO = "auto" # Lazy loading enabled (deferred module loading)
30
+ PRELOAD = "preload" # Preload all modules on start
31
+ BACKGROUND = "background" # Load modules in background threads
32
+ CACHED = "cached" # Cache loaded modules but allow unloading
33
+ # Superior performance modes
34
+ TURBO = "turbo" # Multi-tier cache + parallel preloading + bytecode caching
35
+ ADAPTIVE = "adaptive" # Self-optimizing with pattern learning
36
+ HYPERPARALLEL = "hyperparallel" # Maximum parallelism with multi-threading
37
+ STREAMING = "streaming" # Asynchronous background loading with streaming
38
+ ULTRA = "ultra" # Aggressive optimizations (pre-compiled bytecode, mmap, etc.)
39
+ INTELLIGENT = "intelligent" # Automatically switches to fastest mode based on load level
40
+
41
+
42
+ class LazyInstallMode(Enum):
43
+ """Lazy installation modes."""
44
+ # Core modes
45
+ NONE = "none" # No auto-installation
46
+ SMART = "smart" # Install on first usage (on-demand) - replaces AUTO
47
+ FULL = "full" # Install all dependencies on start
48
+ CLEAN = "clean" # Install on usage + uninstall after completion
49
+ TEMPORARY = "temporary" # Always uninstall after use (more aggressive than CLEAN)
50
+ SIZE_AWARE = "size_aware" # Install small packages, skip large ones
51
+
52
+ # Special purpose modes (kept for specific use cases)
53
+ INTERACTIVE = "interactive" # Ask user before installing
54
+ WARN = "warn" # Log warning but don't install (for monitoring)
55
+ DISABLED = "disabled" # Don't install anything (alias for NONE, more explicit)
56
+ DRY_RUN = "dry_run" # Show what would be installed but don't install
57
+
58
+
59
+ class PathType(Enum):
60
+ """Path types for validation."""
61
+ FILE = "file"
62
+ DIRECTORY = "directory"
63
+ UNKNOWN = "unknown"
64
+
65
+
66
+ class InstallStatus(Enum):
67
+ """Installation status."""
68
+ PENDING = "pending"
69
+ RUNNING = "running"
70
+ SUCCESS = "success"
71
+ FAILED = "failed"
72
+ SKIPPED = "skipped"
73
+
74
+
75
+ class LoadLevel(Enum):
76
+ """Load level categories."""
77
+ LIGHT = "light_load"
78
+ MEDIUM = "medium_load"
79
+ HEAVY = "heavy_load"
80
+ ENTERPRISE = "enterprise_load"
81
+
82
+
83
+ # =============================================================================
84
+ # DATACLASSES
85
+ # =============================================================================
86
+
87
+ @dataclass
88
+ class DependencyInfo:
89
+ """Information about a discovered dependency."""
90
+ import_name: str
91
+ package_name: str
92
+ version: Optional[str] = None
93
+ source: str = "unknown"
94
+ category: str = "general"
95
+
96
+
97
+ @dataclass
98
+ class LazyModeConfig:
99
+ """Two-dimensional lazy mode configuration combining load and install modes."""
100
+ load_mode: LazyLoadMode = LazyLoadMode.NONE
101
+ install_mode: LazyInstallMode = LazyInstallMode.NONE
102
+
103
+ # Additional configuration options
104
+ auto_uninstall_large: bool = False # For AUTO_MODE behavior
105
+ large_package_threshold_mb: float = 50.0 # Size threshold for SIZE_AWARE mode
106
+ preload_priority: List[str] = field(default_factory=list) # Priority modules for PRELOAD
107
+ background_workers: int = 2 # Workers for BACKGROUND mode
108
+
109
+ def __post_init__(self):
110
+ """Normalize enum values."""
111
+ if isinstance(self.load_mode, str):
112
+ self.load_mode = LazyLoadMode(self.load_mode)
113
+ if isinstance(self.install_mode, str):
114
+ self.install_mode = LazyInstallMode(self.install_mode)
115
+
116
+
117
+ @dataclass
118
+ class InstallResult:
119
+ """Result of an installation operation."""
120
+ package_name: str
121
+ success: bool
122
+ status: InstallStatus
123
+ error: Optional[str] = None
124
+ version: Optional[str] = None
125
+ source: Optional[str] = None # "cache", "pip", "wheel", etc.
126
+
127
+
128
+ @dataclass
129
+ class LazyConfig:
130
+ """Bridge configuration settings with the lazy package implementation."""
131
+ packages: Tuple[str, ...] = field(
132
+ default_factory=lambda: ("default",)
133
+ )
134
+
135
+ def __post_init__(self) -> None:
136
+ self.packages = tuple(package.lower() for package in self.packages)
137
+
138
+
139
+ @dataclass(frozen=True)
140
+ class PackageManifest:
141
+ """Resolved manifest data for a single package."""
142
+ package: str
143
+ dependencies: Dict[str, str] = field(default_factory=dict)
144
+ watched_prefixes: Tuple[str, ...] = ()
145
+ async_installs: bool = False
146
+ async_workers: int = 1
147
+ class_wrap_prefixes: Tuple[str, ...] = ()
148
+ metadata: Dict[str, Any] = field(default_factory=dict)
149
+
150
+ def get_dependency(self, import_name: str) -> Optional[str]:
151
+ """Return the declared package for the given import name."""
152
+ if not import_name:
153
+ return None
154
+ direct = self.dependencies.get(import_name)
155
+ if direct is not None:
156
+ return direct
157
+ # Case-insensitive fallback for convenience
158
+ return self.dependencies.get(import_name.lower())
159
+
160
+
161
+ @dataclass(frozen=True)
162
+ class PackageData:
163
+ """
164
+ Immutable package data - same across all strategies.
165
+
166
+ This data structure is used by all package caching, helper, and manager strategies.
167
+ """
168
+ name: str
169
+ version: Optional[str] = None
170
+ installed: bool = False
171
+ install_time: Optional[float] = None
172
+ access_count: int = 0
173
+ install_mode: Optional['LazyInstallMode'] = None
174
+ error: Optional[Exception] = None
175
+ metadata: Dict[str, Any] = field(default_factory=dict)
176
+
177
+
178
+ @dataclass(frozen=True)
179
+ class ModuleData:
180
+ """
181
+ Immutable module data - same across all strategies.
182
+
183
+ This data structure is used by all module caching, helper, and manager strategies.
184
+ """
185
+ path: str
186
+ loaded_module: Optional['ModuleType'] = None
187
+ loading: bool = False
188
+ load_time: Optional[float] = None
189
+ access_count: int = 0
190
+ error: Optional[Exception] = None
191
+ metadata: Dict[str, Any] = field(default_factory=dict)
192
+
193
+
194
+ # =============================================================================
195
+ # TYPE DEFINITIONS
196
+ # =============================================================================
197
+
198
+ class DependencyMapping(TypedDict, total=False):
199
+ """Type definition for dependency mapping structure."""
200
+ import_name: str
201
+ package_name: str
202
+ version: Optional[str]
203
+ source: str
204
+ category: str
205
+
206
+
207
+ class PackageStats(TypedDict, total=False):
208
+ """Type definition for package statistics."""
209
+ enabled: bool
210
+ mode: str
211
+ package_name: str
212
+ installed_packages: List[str]
213
+ failed_packages: List[str]
214
+ total_installed: int
215
+ total_failed: int
216
+
217
+
218
+ class LazyStatus(TypedDict, total=False):
219
+ """Type definition for lazy mode status."""
220
+ enabled: bool
221
+ hook_installed: bool
222
+ lazy_install_enabled: bool
223
+ active: bool
224
+ error: Optional[str]
225
+
226
+
227
+ # =============================================================================
228
+ # CONSTANTS
229
+ # =============================================================================
230
+
231
+ # Default configuration values
232
+ DEFAULT_LARGE_PACKAGE_THRESHOLD_MB: float = 50.0
233
+ DEFAULT_BACKGROUND_WORKERS: int = 2
234
+ DEFAULT_PRELOAD_PRIORITY: List[str] = []
235
+
236
+ # Common import -> package mappings (will be populated from discovery)
237
+ COMMON_IMPORT_MAPPINGS: Dict[str, str] = {
238
+ # Common mappings that are frequently used
239
+ 'cv2': 'opencv-python',
240
+ 'PIL': 'Pillow',
241
+ 'yaml': 'PyYAML',
242
+ 'toml': 'toml',
243
+ 'pandas': 'pandas',
244
+ 'numpy': 'numpy',
245
+ 'scipy': 'scipy',
246
+ 'sklearn': 'scikit-learn',
247
+ 'matplotlib': 'matplotlib',
248
+ 'seaborn': 'seaborn',
249
+ 'requests': 'requests',
250
+ 'urllib3': 'urllib3',
251
+ 'bs4': 'beautifulsoup4',
252
+ 'lxml': 'lxml',
253
+ 'jinja2': 'Jinja2',
254
+ 'flask': 'Flask',
255
+ 'django': 'Django',
256
+ 'fastapi': 'fastapi',
257
+ 'pydantic': 'pydantic',
258
+ 'sqlalchemy': 'SQLAlchemy',
259
+ 'psycopg2': 'psycopg2-binary',
260
+ 'pymongo': 'pymongo',
261
+ 'redis': 'redis',
262
+ 'celery': 'celery',
263
+ 'boto3': 'boto3',
264
+ 'azure': 'azure-storage-blob',
265
+ 'google': 'google-cloud-storage',
266
+ 'tensorflow': 'tensorflow',
267
+ 'torch': 'torch',
268
+ 'transformers': 'transformers',
269
+ 'openai': 'openai',
270
+ 'anthropic': 'anthropic',
271
+ }
272
+
273
+ # Package discovery source names
274
+ DISCOVERY_SOURCE_PYPROJECT = "pyproject.toml"
275
+ DISCOVERY_SOURCE_REQUIREMENTS = "requirements.txt"
276
+ DISCOVERY_SOURCE_SETUP = "setup.py"
277
+ DISCOVERY_SOURCE_POETRY = "poetry.lock"
278
+ DISCOVERY_SOURCE_PIPFILE = "Pipfile"
279
+ DISCOVERY_SOURCE_MANIFEST = "manifest.json"
280
+
281
+ # Installation mode aliases
282
+ INSTALL_MODE_ALIASES: Dict[str, str] = {
283
+ 'auto': 'smart',
284
+ 'on_demand': 'smart',
285
+ 'on-demand': 'smart',
286
+ 'lazy': 'smart',
287
+ }
288
+
289
+ # Cache keys
290
+ CACHE_KEY_DEPENDENCIES = "dependencies"
291
+ CACHE_KEY_PACKAGE_INFO = "package_info"
292
+ CACHE_KEY_INSTALL_STATUS = "install_status"
293
+ CACHE_KEY_DISCOVERY_SOURCES = "discovery_sources"
294
+
295
+ # File patterns for discovery
296
+ PYPROJECT_PATTERN = "pyproject.toml"
297
+ REQUIREMENTS_PATTERN = "requirements*.txt"
298
+ SETUP_PATTERN = "setup.py"
299
+ POETRY_PATTERN = "poetry.lock"
300
+ PIPFILE_PATTERN = "Pipfile"
301
+
302
+ # =============================================================================
303
+ # PRESET MODE MAPPINGS
304
+ # =============================================================================
305
+
306
+ # Preset mode combinations for convenience
307
+ PRESET_MODES: Dict[str, LazyModeConfig] = {
308
+ "none": LazyModeConfig(
309
+ load_mode=LazyLoadMode.NONE,
310
+ install_mode=LazyInstallMode.NONE
311
+ ),
312
+ "lite": LazyModeConfig(
313
+ load_mode=LazyLoadMode.AUTO,
314
+ install_mode=LazyInstallMode.NONE
315
+ ),
316
+ "smart": LazyModeConfig(
317
+ load_mode=LazyLoadMode.AUTO,
318
+ install_mode=LazyInstallMode.SMART
319
+ ),
320
+ "full": LazyModeConfig(
321
+ load_mode=LazyLoadMode.AUTO,
322
+ install_mode=LazyInstallMode.FULL
323
+ ),
324
+ "clean": LazyModeConfig(
325
+ load_mode=LazyLoadMode.AUTO,
326
+ install_mode=LazyInstallMode.CLEAN
327
+ ),
328
+ "temporary": LazyModeConfig(
329
+ load_mode=LazyLoadMode.AUTO,
330
+ install_mode=LazyInstallMode.TEMPORARY
331
+ ),
332
+ "size_aware": LazyModeConfig(
333
+ load_mode=LazyLoadMode.AUTO,
334
+ install_mode=LazyInstallMode.SIZE_AWARE
335
+ ),
336
+ "auto": LazyModeConfig(
337
+ load_mode=LazyLoadMode.AUTO,
338
+ install_mode=LazyInstallMode.SMART,
339
+ auto_uninstall_large=True
340
+ ),
341
+ }
342
+
343
+
344
+ def get_preset_mode(preset_name: str) -> Optional[LazyModeConfig]:
345
+ """Get preset mode configuration by name."""
346
+ return PRESET_MODES.get(preset_name.lower())
347
+
348
+
349
+ # =============================================================================
350
+ # EXPORT ALL
351
+ # =============================================================================
352
+
353
+ __all__ = [
354
+ # Enums
355
+ 'LazyLoadMode',
356
+ 'LazyInstallMode',
357
+ 'PathType',
358
+ 'InstallStatus',
359
+ 'LoadLevel',
360
+ # Dataclasses
361
+ 'DependencyInfo',
362
+ 'LazyModeConfig',
363
+ 'InstallResult',
364
+ 'LazyConfig',
365
+ 'PackageManifest',
366
+ 'PackageData',
367
+ 'ModuleData',
368
+ # Type definitions
369
+ 'DependencyMapping',
370
+ 'PackageStats',
371
+ 'LazyStatus',
372
+ # Constants
373
+ 'DEFAULT_LARGE_PACKAGE_THRESHOLD_MB',
374
+ 'DEFAULT_BACKGROUND_WORKERS',
375
+ 'DEFAULT_PRELOAD_PRIORITY',
376
+ 'COMMON_IMPORT_MAPPINGS',
377
+ 'DISCOVERY_SOURCE_PYPROJECT',
378
+ 'DISCOVERY_SOURCE_REQUIREMENTS',
379
+ 'DISCOVERY_SOURCE_SETUP',
380
+ 'DISCOVERY_SOURCE_POETRY',
381
+ 'DISCOVERY_SOURCE_PIPFILE',
382
+ 'DISCOVERY_SOURCE_MANIFEST',
383
+ 'INSTALL_MODE_ALIASES',
384
+ 'CACHE_KEY_DEPENDENCIES',
385
+ 'CACHE_KEY_PACKAGE_INFO',
386
+ 'CACHE_KEY_INSTALL_STATUS',
387
+ 'CACHE_KEY_DISCOVERY_SOURCES',
388
+ 'PYPROJECT_PATTERN',
389
+ 'REQUIREMENTS_PATTERN',
390
+ 'SETUP_PATTERN',
391
+ 'POETRY_PATTERN',
392
+ 'PIPFILE_PATTERN',
393
+ # Preset modes
394
+ 'PRESET_MODES',
395
+ 'get_preset_mode',
396
+ ]
397
+
@@ -1,16 +1,16 @@
1
1
  """
2
- #exonware/xwsystem/src/exonware/xwsystem/utils/lazy_package/lazy_errors.py
2
+ #exonware/xwlazy/src/exonware/xwlazy/errors.py
3
3
 
4
4
  Company: eXonware.com
5
5
  Author: Eng. Muhammad AlShehri
6
6
  Email: connect@exonware.com
7
- Version: 0.1.0.16
7
+ Version: 0.1.0.19
8
8
  Generation Date: 10-Oct-2025
9
9
 
10
10
  Errors for Lazy Loading System
11
11
 
12
12
  This module defines all exception classes for the lazy loading system
13
- following DEV_GUIDELINES.md structure.
13
+ following GUIDE_ARCH.md structure.
14
14
  """
15
15
 
16
16
  from typing import Optional, Any
@@ -135,7 +135,7 @@ class DeferredImportError(Exception):
135
135
  - Caches successful imports to avoid repeated installs
136
136
 
137
137
  Note: This is both an error class and a proxy object. It stays in
138
- lazy_errors.py because it represents an error state, but acts as
138
+ errors.py because it represents an error state, but acts as
139
139
  a proxy until resolved.
140
140
  """
141
141
 
@@ -181,11 +181,11 @@ class DeferredImportError(Exception):
181
181
  Raises:
182
182
  Original ImportError if installation fails or is disabled
183
183
  """
184
- # Import here to avoid circular imports
185
- from .lazy_core import lazy_import_with_install, is_lazy_install_enabled
186
- from .logging_utils import get_logger
184
+ # Import from facade and new structure
185
+ from .facade import lazy_import_with_install, is_lazy_install_enabled
186
+ from .common.logger import get_logger
187
187
 
188
- logger = get_logger("xwlazy.lazy")
188
+ logger = get_logger("xwlazy")
189
189
  logger.info(f"[STAGE 2] _try_install_and_import called for '{self._import_name}'")
190
190
 
191
191
  # Return cached module if already installed
@@ -203,6 +203,19 @@ class DeferredImportError(Exception):
203
203
  if self._async_handle is not None:
204
204
  logger.info(f"[STAGE 2] Waiting for async install of '{self._import_name}' to finish")
205
205
  self._async_handle.wait()
206
+
207
+ # After async install, try simple import first (cache invalidation needed)
208
+ try:
209
+ import importlib
210
+ import sys
211
+ importlib.invalidate_caches()
212
+ sys.path_importer_cache.clear()
213
+ module = importlib.import_module(self._import_name)
214
+ self._real_module = module
215
+ logger.info(f"✅ [STAGE 2] Successfully loaded '{self._import_name}' after async install")
216
+ return module
217
+ except ImportError:
218
+ pass # Fall through to lazy_import_with_install
206
219
 
207
220
  if not is_lazy_install_enabled(self._installer_package):
208
221
  logger.warning(f"[STAGE 2] Lazy install disabled for {self._installer_package}, cannot load {self._import_name}")