exonware-xwlazy 0.1.0.9__py3-none-any.whl → 0.1.0.11__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/xwlazy/__init__.py +0 -0
- exonware/xwlazy/version.py +2 -2
- exonware_xwlazy-0.1.0.11.dist-info/METADATA +380 -0
- exonware_xwlazy-0.1.0.11.dist-info/RECORD +20 -0
- xwlazy/__init__.py +34 -0
- xwlazy/lazy/__init__.py +301 -0
- xwlazy/lazy/bootstrap.py +106 -0
- xwlazy/lazy/config.py +163 -0
- xwlazy/lazy/host_conf.py +279 -0
- xwlazy/lazy/host_packages.py +122 -0
- xwlazy/lazy/lazy_base.py +465 -0
- xwlazy/lazy/lazy_contracts.py +290 -0
- xwlazy/lazy/lazy_core.py +3727 -0
- xwlazy/lazy/lazy_errors.py +271 -0
- xwlazy/lazy/lazy_state.py +86 -0
- xwlazy/lazy/logging_utils.py +194 -0
- xwlazy/lazy/manifest.py +489 -0
- xwlazy/version.py +77 -0
- exonware_xwlazy-0.1.0.9.dist-info/METADATA +0 -0
- exonware_xwlazy-0.1.0.9.dist-info/RECORD +0 -6
- {exonware_xwlazy-0.1.0.9.dist-info → exonware_xwlazy-0.1.0.11.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.9.dist-info → exonware_xwlazy-0.1.0.11.dist-info}/licenses/LICENSE +0 -0
xwlazy/lazy/__init__.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""
|
|
2
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/lazy_package/__init__.py
|
|
3
|
+
|
|
4
|
+
Company: eXonware.com
|
|
5
|
+
Author: Eng. Muhammad AlShehri
|
|
6
|
+
Email: connect@exonware.com
|
|
7
|
+
Version: 0.1.0.16
|
|
8
|
+
Generation Date: 10-Oct-2025
|
|
9
|
+
|
|
10
|
+
Lazy Package - Unified Lazy Loading System
|
|
11
|
+
|
|
12
|
+
This package provides per-package lazy loading with automatic installation
|
|
13
|
+
of missing dependencies. It consolidates all lazy loading functionality into
|
|
14
|
+
a clean, well-structured module following DEV_GUIDELINES.md.
|
|
15
|
+
|
|
16
|
+
Design Patterns:
|
|
17
|
+
- Facade: Unified API (LazySystemFacade, LazyModeFacade)
|
|
18
|
+
- Strategy: Pluggable discovery/installation/caching strategies
|
|
19
|
+
- Template Method: Base classes define common workflows
|
|
20
|
+
- Singleton: Global instances for system-wide state
|
|
21
|
+
- Registry: Per-package isolation (LazyInstallerRegistry)
|
|
22
|
+
- Observer: Performance monitoring
|
|
23
|
+
- Proxy: Deferred loading (LazyLoader, DeferredImportError)
|
|
24
|
+
- Factory: Handler creation
|
|
25
|
+
|
|
26
|
+
Core Goal: Per-Package Lazy Loading
|
|
27
|
+
Each package (xwsystem, xwnode, xwdata) can independently enable lazy mode.
|
|
28
|
+
Only packages installed with [lazy] extra get auto-installation.
|
|
29
|
+
|
|
30
|
+
Quick Start:
|
|
31
|
+
# In your package's __init__.py
|
|
32
|
+
from xwlazy.lazy import config_package_lazy_install_enabled
|
|
33
|
+
config_package_lazy_install_enabled("yourpackage") # Auto-detect from pip install
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
# Then just use normal Python imports!
|
|
37
|
+
import fastavro # Missing? Auto-installed! ✨
|
|
38
|
+
import pandas # Missing? Auto-installed! ✨
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Import all from submodules
|
|
42
|
+
from .lazy_contracts import (
|
|
43
|
+
# Enums
|
|
44
|
+
LazyInstallMode,
|
|
45
|
+
PathType,
|
|
46
|
+
# Dataclasses
|
|
47
|
+
DependencyInfo,
|
|
48
|
+
# Interfaces
|
|
49
|
+
IPackageDiscovery,
|
|
50
|
+
IPackageInstaller,
|
|
51
|
+
IImportHook,
|
|
52
|
+
IPackageCache,
|
|
53
|
+
ILazyLoader,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from .lazy_errors import (
|
|
57
|
+
# Exceptions
|
|
58
|
+
LazySystemError,
|
|
59
|
+
LazyInstallError,
|
|
60
|
+
LazyDiscoveryError,
|
|
61
|
+
LazyHookError,
|
|
62
|
+
LazySecurityError,
|
|
63
|
+
ExternallyManagedError,
|
|
64
|
+
DeferredImportError,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
from .lazy_base import (
|
|
68
|
+
# Abstract base classes
|
|
69
|
+
APackageDiscovery,
|
|
70
|
+
APackageInstaller,
|
|
71
|
+
AImportHook,
|
|
72
|
+
APackageCache,
|
|
73
|
+
ALazyLoader,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
from .lazy_core import (
|
|
77
|
+
# Core classes
|
|
78
|
+
DependencyMapper,
|
|
79
|
+
LazyDiscovery,
|
|
80
|
+
LazyInstaller,
|
|
81
|
+
LazyInstallPolicy,
|
|
82
|
+
LazyInstallerRegistry,
|
|
83
|
+
LazyImportHook,
|
|
84
|
+
LazyMetaPathFinder,
|
|
85
|
+
LazyLoader,
|
|
86
|
+
LazyImporter,
|
|
87
|
+
LazyModuleRegistry,
|
|
88
|
+
LazyPerformanceMonitor,
|
|
89
|
+
LazyInstallConfig,
|
|
90
|
+
LazyModeFacade,
|
|
91
|
+
WatchedPrefixRegistry,
|
|
92
|
+
AsyncInstallHandle,
|
|
93
|
+
|
|
94
|
+
# Discovery functions
|
|
95
|
+
get_lazy_discovery,
|
|
96
|
+
discover_dependencies,
|
|
97
|
+
export_dependency_mappings,
|
|
98
|
+
|
|
99
|
+
# Install functions
|
|
100
|
+
enable_lazy_install,
|
|
101
|
+
disable_lazy_install,
|
|
102
|
+
is_lazy_install_enabled,
|
|
103
|
+
set_lazy_install_mode,
|
|
104
|
+
get_lazy_install_mode,
|
|
105
|
+
install_missing_package,
|
|
106
|
+
install_and_import,
|
|
107
|
+
get_lazy_install_stats,
|
|
108
|
+
get_all_lazy_install_stats,
|
|
109
|
+
lazy_import_with_install,
|
|
110
|
+
xwimport,
|
|
111
|
+
|
|
112
|
+
# Hook functions
|
|
113
|
+
install_import_hook,
|
|
114
|
+
uninstall_import_hook,
|
|
115
|
+
is_import_hook_installed,
|
|
116
|
+
|
|
117
|
+
# Lazy loading functions
|
|
118
|
+
enable_lazy_imports,
|
|
119
|
+
disable_lazy_imports,
|
|
120
|
+
is_lazy_import_enabled,
|
|
121
|
+
lazy_import,
|
|
122
|
+
register_lazy_module,
|
|
123
|
+
preload_module,
|
|
124
|
+
get_lazy_module,
|
|
125
|
+
get_loading_stats,
|
|
126
|
+
preload_frequently_used,
|
|
127
|
+
get_lazy_import_stats,
|
|
128
|
+
|
|
129
|
+
# Lazy mode facade functions
|
|
130
|
+
enable_lazy_mode,
|
|
131
|
+
disable_lazy_mode,
|
|
132
|
+
is_lazy_mode_enabled,
|
|
133
|
+
get_lazy_mode_stats,
|
|
134
|
+
configure_lazy_mode,
|
|
135
|
+
preload_modules,
|
|
136
|
+
optimize_lazy_mode,
|
|
137
|
+
|
|
138
|
+
# Configuration
|
|
139
|
+
config_package_lazy_install_enabled,
|
|
140
|
+
sync_manifest_configuration,
|
|
141
|
+
refresh_lazy_manifests,
|
|
142
|
+
|
|
143
|
+
# Security & Policy
|
|
144
|
+
set_package_allow_list,
|
|
145
|
+
set_package_deny_list,
|
|
146
|
+
add_to_package_allow_list,
|
|
147
|
+
add_to_package_deny_list,
|
|
148
|
+
set_package_index_url,
|
|
149
|
+
set_package_extra_index_urls,
|
|
150
|
+
add_package_trusted_host,
|
|
151
|
+
set_package_lockfile,
|
|
152
|
+
generate_package_sbom,
|
|
153
|
+
check_externally_managed_environment,
|
|
154
|
+
|
|
155
|
+
# Registration helpers
|
|
156
|
+
register_lazy_module_prefix,
|
|
157
|
+
register_lazy_module_methods,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
from .config import LazyConfig, DEFAULT_LAZY_CONFIG
|
|
161
|
+
from .host_conf import get_conf_module
|
|
162
|
+
from .host_packages import register_host_package, refresh_host_package
|
|
163
|
+
from .logging_utils import (
|
|
164
|
+
get_log_categories,
|
|
165
|
+
is_log_category_enabled,
|
|
166
|
+
set_log_categories,
|
|
167
|
+
set_log_category,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Version info
|
|
171
|
+
__version__ = "0.0.1.382"
|
|
172
|
+
__author__ = "Eng. Muhammad AlShehri"
|
|
173
|
+
__email__ = "connect@exonware.com"
|
|
174
|
+
__company__ = "eXonware.com"
|
|
175
|
+
|
|
176
|
+
# Export all
|
|
177
|
+
__all__ = [
|
|
178
|
+
# Enums
|
|
179
|
+
'LazyInstallMode',
|
|
180
|
+
'PathType',
|
|
181
|
+
|
|
182
|
+
# Dataclasses
|
|
183
|
+
'DependencyInfo',
|
|
184
|
+
|
|
185
|
+
# Interfaces
|
|
186
|
+
'IPackageDiscovery',
|
|
187
|
+
'IPackageInstaller',
|
|
188
|
+
'IImportHook',
|
|
189
|
+
'IPackageCache',
|
|
190
|
+
'ILazyLoader',
|
|
191
|
+
|
|
192
|
+
# Exceptions
|
|
193
|
+
'LazySystemError',
|
|
194
|
+
'LazyInstallError',
|
|
195
|
+
'LazyDiscoveryError',
|
|
196
|
+
'LazyHookError',
|
|
197
|
+
'LazySecurityError',
|
|
198
|
+
'ExternallyManagedError',
|
|
199
|
+
'DeferredImportError',
|
|
200
|
+
|
|
201
|
+
# Abstract base classes
|
|
202
|
+
'APackageDiscovery',
|
|
203
|
+
'APackageInstaller',
|
|
204
|
+
'AImportHook',
|
|
205
|
+
'APackageCache',
|
|
206
|
+
'ALazyLoader',
|
|
207
|
+
|
|
208
|
+
# Core classes
|
|
209
|
+
'DependencyMapper',
|
|
210
|
+
'LazyDiscovery',
|
|
211
|
+
'LazyInstaller',
|
|
212
|
+
'LazyInstallPolicy',
|
|
213
|
+
'LazyInstallerRegistry',
|
|
214
|
+
'LazyImportHook',
|
|
215
|
+
'LazyMetaPathFinder',
|
|
216
|
+
'LazyLoader',
|
|
217
|
+
'LazyImporter',
|
|
218
|
+
'LazyModuleRegistry',
|
|
219
|
+
'LazyPerformanceMonitor',
|
|
220
|
+
'LazyInstallConfig',
|
|
221
|
+
'LazyModeFacade',
|
|
222
|
+
'WatchedPrefixRegistry',
|
|
223
|
+
'AsyncInstallHandle',
|
|
224
|
+
|
|
225
|
+
# Discovery functions
|
|
226
|
+
'get_lazy_discovery',
|
|
227
|
+
'discover_dependencies',
|
|
228
|
+
'export_dependency_mappings',
|
|
229
|
+
|
|
230
|
+
# Install functions
|
|
231
|
+
'enable_lazy_install',
|
|
232
|
+
'disable_lazy_install',
|
|
233
|
+
'is_lazy_install_enabled',
|
|
234
|
+
'set_lazy_install_mode',
|
|
235
|
+
'get_lazy_install_mode',
|
|
236
|
+
'install_missing_package',
|
|
237
|
+
'install_and_import',
|
|
238
|
+
'get_lazy_install_stats',
|
|
239
|
+
'get_all_lazy_install_stats',
|
|
240
|
+
'lazy_import_with_install',
|
|
241
|
+
'xwimport',
|
|
242
|
+
|
|
243
|
+
# Hook functions
|
|
244
|
+
'install_import_hook',
|
|
245
|
+
'uninstall_import_hook',
|
|
246
|
+
'is_import_hook_installed',
|
|
247
|
+
|
|
248
|
+
# Lazy loading functions
|
|
249
|
+
'enable_lazy_imports',
|
|
250
|
+
'disable_lazy_imports',
|
|
251
|
+
'is_lazy_import_enabled',
|
|
252
|
+
'lazy_import',
|
|
253
|
+
'register_lazy_module',
|
|
254
|
+
'preload_module',
|
|
255
|
+
'get_lazy_module',
|
|
256
|
+
'get_loading_stats',
|
|
257
|
+
'preload_frequently_used',
|
|
258
|
+
'get_lazy_import_stats',
|
|
259
|
+
|
|
260
|
+
# Lazy mode facade functions
|
|
261
|
+
'enable_lazy_mode',
|
|
262
|
+
'disable_lazy_mode',
|
|
263
|
+
'is_lazy_mode_enabled',
|
|
264
|
+
'get_lazy_mode_stats',
|
|
265
|
+
'configure_lazy_mode',
|
|
266
|
+
'preload_modules',
|
|
267
|
+
'optimize_lazy_mode',
|
|
268
|
+
|
|
269
|
+
# Configuration
|
|
270
|
+
'config_package_lazy_install_enabled',
|
|
271
|
+
'sync_manifest_configuration',
|
|
272
|
+
'refresh_lazy_manifests',
|
|
273
|
+
'LazyConfig',
|
|
274
|
+
'DEFAULT_LAZY_CONFIG',
|
|
275
|
+
|
|
276
|
+
# Security & Policy
|
|
277
|
+
'set_package_allow_list',
|
|
278
|
+
'set_package_deny_list',
|
|
279
|
+
'add_to_package_allow_list',
|
|
280
|
+
'add_to_package_deny_list',
|
|
281
|
+
'set_package_index_url',
|
|
282
|
+
'set_package_extra_index_urls',
|
|
283
|
+
'add_package_trusted_host',
|
|
284
|
+
'set_package_lockfile',
|
|
285
|
+
'generate_package_sbom',
|
|
286
|
+
'check_externally_managed_environment',
|
|
287
|
+
|
|
288
|
+
# Registration helpers
|
|
289
|
+
'register_lazy_module_prefix',
|
|
290
|
+
'register_lazy_module_methods',
|
|
291
|
+
'register_host_package',
|
|
292
|
+
'refresh_host_package',
|
|
293
|
+
'get_conf_module',
|
|
294
|
+
|
|
295
|
+
# Logging helpers
|
|
296
|
+
'get_log_categories',
|
|
297
|
+
'is_log_category_enabled',
|
|
298
|
+
'set_log_categories',
|
|
299
|
+
'set_log_category',
|
|
300
|
+
]
|
|
301
|
+
|
xwlazy/lazy/bootstrap.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#exonware/xwlazy/src/exonware/xwlazy/lazy/bootstrap.py
|
|
2
|
+
"""
|
|
3
|
+
Early bootstrap utilities for lazily installing import hooks.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import atexit
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _env_enabled(env_value: Optional[str]) -> Optional[bool]:
|
|
16
|
+
if not env_value:
|
|
17
|
+
return None
|
|
18
|
+
normalized = env_value.strip().lower()
|
|
19
|
+
if normalized in ('true', '1', 'yes', 'on'):
|
|
20
|
+
return True
|
|
21
|
+
if normalized in ('false', '0', 'no', 'off'):
|
|
22
|
+
return False
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def bootstrap_lazy_mode(package_name: str) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Detect whether lazy mode should be enabled for ``package_name`` and bootstrap hooks.
|
|
29
|
+
"""
|
|
30
|
+
package_name = package_name.lower()
|
|
31
|
+
env_value = os.environ.get(f"{package_name.upper()}_LAZY_INSTALL")
|
|
32
|
+
env_enabled = _env_enabled(env_value)
|
|
33
|
+
enabled = env_enabled
|
|
34
|
+
|
|
35
|
+
if enabled is None:
|
|
36
|
+
try:
|
|
37
|
+
from xwlazy.lazy.lazy_core import _detect_lazy_installation
|
|
38
|
+
enabled = _detect_lazy_installation(package_name)
|
|
39
|
+
except Exception as exc: # pragma: no cover
|
|
40
|
+
logging.getLogger("xwlazy.lazy.bootstrap").debug(
|
|
41
|
+
"Lazy detection failed: %s", exc, exc_info=True
|
|
42
|
+
)
|
|
43
|
+
enabled = False
|
|
44
|
+
|
|
45
|
+
if not enabled:
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
from xwlazy.lazy.lazy_core import config_package_lazy_install_enabled
|
|
50
|
+
|
|
51
|
+
config_package_lazy_install_enabled(
|
|
52
|
+
package_name,
|
|
53
|
+
enabled=True,
|
|
54
|
+
install_hook=True,
|
|
55
|
+
)
|
|
56
|
+
except Exception as exc: # pragma: no cover
|
|
57
|
+
logging.getLogger("xwlazy.lazy.bootstrap").debug(
|
|
58
|
+
"Lazy bootstrap failed for %s: %s", package_name, exc, exc_info=True
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bootstrap_lazy_mode_deferred(package_name: str) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Schedule lazy mode bootstrap to run AFTER the calling package finishes importing.
|
|
65
|
+
|
|
66
|
+
This avoids hook interference with the package's own imports (e.g., requests/certifi in xwsystem).
|
|
67
|
+
Uses a post-import hook to detect when the package module is fully loaded.
|
|
68
|
+
"""
|
|
69
|
+
package_name_lower = package_name.lower()
|
|
70
|
+
package_module_name = f"exonware.{package_name_lower}"
|
|
71
|
+
|
|
72
|
+
# Store original __import__ to restore later
|
|
73
|
+
original_import = __builtins__.__import__ if hasattr(__builtins__, '__import__') else __import__
|
|
74
|
+
|
|
75
|
+
def _import_hook(name, *args, **kwargs):
|
|
76
|
+
# Call original import first
|
|
77
|
+
result = original_import(name, *args, **kwargs)
|
|
78
|
+
|
|
79
|
+
# Check if the target package just finished importing
|
|
80
|
+
if name == package_module_name or name.startswith(f"{package_module_name}."):
|
|
81
|
+
# Check if package root is now fully in sys.modules
|
|
82
|
+
if package_module_name in sys.modules:
|
|
83
|
+
# Package is loaded, install hook on next event loop iteration
|
|
84
|
+
import threading
|
|
85
|
+
def _install_hook():
|
|
86
|
+
# Restore original import
|
|
87
|
+
if hasattr(__builtins__, '__import__'):
|
|
88
|
+
__builtins__.__import__ = original_import
|
|
89
|
+
else:
|
|
90
|
+
import builtins
|
|
91
|
+
builtins.__import__ = original_import
|
|
92
|
+
# Now install the lazy hook
|
|
93
|
+
bootstrap_lazy_mode(package_name_lower)
|
|
94
|
+
|
|
95
|
+
# Schedule for immediate execution after current import completes
|
|
96
|
+
threading.Timer(0.0, _install_hook).start()
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
# Install temporary import hook
|
|
101
|
+
if hasattr(__builtins__, '__import__'):
|
|
102
|
+
__builtins__.__import__ = _import_hook
|
|
103
|
+
else:
|
|
104
|
+
import builtins
|
|
105
|
+
builtins.__import__ = _import_hook
|
|
106
|
+
|
xwlazy/lazy/config.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Configuration helpers for the lazy-loading subsystem."""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
from .lazy_core import (
|
|
9
|
+
config_package_lazy_install_enabled,
|
|
10
|
+
disable_lazy_mode,
|
|
11
|
+
enable_lazy_mode,
|
|
12
|
+
is_import_hook_installed,
|
|
13
|
+
is_lazy_install_enabled,
|
|
14
|
+
is_lazy_mode_enabled,
|
|
15
|
+
install_import_hook,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class LazyConfig:
|
|
21
|
+
"""Bridge configuration settings with the lazy package implementation."""
|
|
22
|
+
|
|
23
|
+
packages: Tuple[str, ...] = field(
|
|
24
|
+
default_factory=lambda: ("default",)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def __post_init__(self) -> None:
|
|
28
|
+
self.packages = tuple(package.lower() for package in self.packages)
|
|
29
|
+
|
|
30
|
+
# High-level API -----------------------------------------------------
|
|
31
|
+
@property
|
|
32
|
+
def lazy_import(self) -> bool:
|
|
33
|
+
"""Return whether lazy mode is currently active."""
|
|
34
|
+
return is_lazy_mode_enabled()
|
|
35
|
+
|
|
36
|
+
@lazy_import.setter
|
|
37
|
+
def lazy_import(self, value: bool) -> None:
|
|
38
|
+
self.set_lazy_import(bool(value))
|
|
39
|
+
|
|
40
|
+
def set_lazy_import(
|
|
41
|
+
self,
|
|
42
|
+
enabled: bool,
|
|
43
|
+
*,
|
|
44
|
+
lazy_imports: bool = True,
|
|
45
|
+
lazy_install: bool = True,
|
|
46
|
+
install_hook: bool = True,
|
|
47
|
+
mode: str = "auto",
|
|
48
|
+
) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Toggle lazy mode with optional fine-grained controls.
|
|
51
|
+
|
|
52
|
+
Includes re-hooking support: If lazy is enabled and install_hook is True,
|
|
53
|
+
ensures the import hook is installed even if it wasn't installed initially.
|
|
54
|
+
"""
|
|
55
|
+
if enabled:
|
|
56
|
+
self._configure_packages(True, mode=mode, install_hook=install_hook)
|
|
57
|
+
enable_lazy_mode(
|
|
58
|
+
package_name=self.packages[0],
|
|
59
|
+
enable_lazy_imports=lazy_imports,
|
|
60
|
+
enable_lazy_install=lazy_install,
|
|
61
|
+
install_hook=install_hook,
|
|
62
|
+
lazy_install_mode=mode,
|
|
63
|
+
)
|
|
64
|
+
# Re-hook: Install hook if lazy is enabled and hook not already installed
|
|
65
|
+
# Root cause: Hook not installed when lazy enabled after package load
|
|
66
|
+
# Priority impact: Usability (#2) - Users expect lazy to work when enabled
|
|
67
|
+
if install_hook:
|
|
68
|
+
self._ensure_hook_installed()
|
|
69
|
+
else:
|
|
70
|
+
disable_lazy_mode()
|
|
71
|
+
self._configure_packages(False, install_hook=False)
|
|
72
|
+
|
|
73
|
+
def enable(
|
|
74
|
+
self,
|
|
75
|
+
*,
|
|
76
|
+
lazy_imports: bool = True,
|
|
77
|
+
lazy_install: bool = True,
|
|
78
|
+
install_hook: bool = True,
|
|
79
|
+
mode: str = "auto",
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Enable lazy mode using the provided options."""
|
|
82
|
+
self.set_lazy_import(
|
|
83
|
+
True,
|
|
84
|
+
lazy_imports=lazy_imports,
|
|
85
|
+
lazy_install=lazy_install,
|
|
86
|
+
install_hook=install_hook,
|
|
87
|
+
mode=mode,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def disable(self) -> None:
|
|
91
|
+
"""Disable lazy mode entirely."""
|
|
92
|
+
self.set_lazy_import(False)
|
|
93
|
+
|
|
94
|
+
# DX: Status check methods -------------------------------------------
|
|
95
|
+
def get_lazy_status(self) -> dict:
|
|
96
|
+
"""
|
|
97
|
+
Get detailed lazy installation status (DX enhancement).
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dictionary with lazy mode status information
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
104
|
+
return {
|
|
105
|
+
'enabled': self.lazy_import,
|
|
106
|
+
'hook_installed': is_import_hook_installed(primary_package),
|
|
107
|
+
'lazy_install_enabled': is_lazy_install_enabled(primary_package),
|
|
108
|
+
'active': self.lazy_import and is_import_hook_installed(primary_package)
|
|
109
|
+
}
|
|
110
|
+
except Exception:
|
|
111
|
+
return {
|
|
112
|
+
'enabled': self.lazy_import,
|
|
113
|
+
'hook_installed': False,
|
|
114
|
+
'lazy_install_enabled': False,
|
|
115
|
+
'active': False,
|
|
116
|
+
'error': 'Could not check hook status'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
def is_lazy_active(self) -> bool:
|
|
120
|
+
"""
|
|
121
|
+
Check if lazy mode is active (DX enhancement).
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if lazy mode is enabled and hook is installed
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
128
|
+
return self.lazy_import and is_import_hook_installed(primary_package)
|
|
129
|
+
except Exception:
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
# Internal helpers ---------------------------------------------------
|
|
133
|
+
def _configure_packages(
|
|
134
|
+
self,
|
|
135
|
+
enabled: bool,
|
|
136
|
+
*,
|
|
137
|
+
mode: str = "auto",
|
|
138
|
+
install_hook: bool = True,
|
|
139
|
+
) -> None:
|
|
140
|
+
for package in self.packages:
|
|
141
|
+
config_package_lazy_install_enabled(
|
|
142
|
+
package,
|
|
143
|
+
enabled,
|
|
144
|
+
mode,
|
|
145
|
+
install_hook=install_hook,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def _ensure_hook_installed(self) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Ensure import hook is installed for primary package.
|
|
151
|
+
|
|
152
|
+
Re-hooking support: Install hook if not already installed.
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
primary_package = self.packages[0] if self.packages else "default"
|
|
156
|
+
if not is_import_hook_installed(primary_package):
|
|
157
|
+
install_import_hook(primary_package)
|
|
158
|
+
except Exception:
|
|
159
|
+
# Fail silently - hook installation failure shouldn't break package
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
DEFAULT_LAZY_CONFIG = LazyConfig()
|