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.
@@ -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
+
@@ -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()