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.
Files changed (90) hide show
  1. exonware/__init__.py +86 -16
  2. exonware/xwlazy/version.py +5 -5
  3. exonware/xwlazy.py +2546 -0
  4. exonware/xwlazy_external_libs.toml +716 -0
  5. {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/METADATA +6 -6
  6. exonware_xwlazy-1.0.1.2.dist-info/RECORD +8 -0
  7. exonware/xwlazy/__init__.py +0 -367
  8. exonware/xwlazy/common/__init__.py +0 -47
  9. exonware/xwlazy/common/base.py +0 -56
  10. exonware/xwlazy/common/cache.py +0 -504
  11. exonware/xwlazy/common/logger.py +0 -257
  12. exonware/xwlazy/common/services/__init__.py +0 -72
  13. exonware/xwlazy/common/services/dependency_mapper.py +0 -232
  14. exonware/xwlazy/common/services/install_async_utils.py +0 -165
  15. exonware/xwlazy/common/services/install_cache_utils.py +0 -245
  16. exonware/xwlazy/common/services/keyword_detection.py +0 -283
  17. exonware/xwlazy/common/services/spec_cache.py +0 -165
  18. exonware/xwlazy/common/services/state_manager.py +0 -84
  19. exonware/xwlazy/common/strategies/__init__.py +0 -28
  20. exonware/xwlazy/common/strategies/caching_dict.py +0 -44
  21. exonware/xwlazy/common/strategies/caching_installation.py +0 -88
  22. exonware/xwlazy/common/strategies/caching_lfu.py +0 -66
  23. exonware/xwlazy/common/strategies/caching_lru.py +0 -63
  24. exonware/xwlazy/common/strategies/caching_multitier.py +0 -59
  25. exonware/xwlazy/common/strategies/caching_ttl.py +0 -59
  26. exonware/xwlazy/config.py +0 -193
  27. exonware/xwlazy/contracts.py +0 -1396
  28. exonware/xwlazy/defs.py +0 -378
  29. exonware/xwlazy/errors.py +0 -276
  30. exonware/xwlazy/facade.py +0 -991
  31. exonware/xwlazy/module/__init__.py +0 -18
  32. exonware/xwlazy/module/base.py +0 -565
  33. exonware/xwlazy/module/data.py +0 -17
  34. exonware/xwlazy/module/facade.py +0 -246
  35. exonware/xwlazy/module/importer_engine.py +0 -2117
  36. exonware/xwlazy/module/strategies/__init__.py +0 -22
  37. exonware/xwlazy/module/strategies/module_helper_lazy.py +0 -93
  38. exonware/xwlazy/module/strategies/module_helper_simple.py +0 -65
  39. exonware/xwlazy/module/strategies/module_manager_advanced.py +0 -111
  40. exonware/xwlazy/module/strategies/module_manager_simple.py +0 -95
  41. exonware/xwlazy/package/__init__.py +0 -18
  42. exonware/xwlazy/package/base.py +0 -798
  43. exonware/xwlazy/package/conf.py +0 -324
  44. exonware/xwlazy/package/data.py +0 -17
  45. exonware/xwlazy/package/facade.py +0 -480
  46. exonware/xwlazy/package/services/__init__.py +0 -84
  47. exonware/xwlazy/package/services/async_install_handle.py +0 -87
  48. exonware/xwlazy/package/services/config_manager.py +0 -245
  49. exonware/xwlazy/package/services/discovery.py +0 -370
  50. exonware/xwlazy/package/services/host_packages.py +0 -145
  51. exonware/xwlazy/package/services/install_async.py +0 -277
  52. exonware/xwlazy/package/services/install_cache.py +0 -145
  53. exonware/xwlazy/package/services/install_interactive.py +0 -59
  54. exonware/xwlazy/package/services/install_policy.py +0 -156
  55. exonware/xwlazy/package/services/install_registry.py +0 -54
  56. exonware/xwlazy/package/services/install_result.py +0 -17
  57. exonware/xwlazy/package/services/install_sbom.py +0 -153
  58. exonware/xwlazy/package/services/install_utils.py +0 -79
  59. exonware/xwlazy/package/services/installer_engine.py +0 -406
  60. exonware/xwlazy/package/services/lazy_installer.py +0 -718
  61. exonware/xwlazy/package/services/manifest.py +0 -496
  62. exonware/xwlazy/package/services/strategy_registry.py +0 -186
  63. exonware/xwlazy/package/strategies/__init__.py +0 -57
  64. exonware/xwlazy/package/strategies/package_discovery_file.py +0 -129
  65. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +0 -84
  66. exonware/xwlazy/package/strategies/package_discovery_manifest.py +0 -101
  67. exonware/xwlazy/package/strategies/package_execution_async.py +0 -113
  68. exonware/xwlazy/package/strategies/package_execution_cached.py +0 -90
  69. exonware/xwlazy/package/strategies/package_execution_pip.py +0 -99
  70. exonware/xwlazy/package/strategies/package_execution_wheel.py +0 -106
  71. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +0 -100
  72. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +0 -105
  73. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +0 -100
  74. exonware/xwlazy/package/strategies/package_policy_allow_list.py +0 -57
  75. exonware/xwlazy/package/strategies/package_policy_deny_list.py +0 -57
  76. exonware/xwlazy/package/strategies/package_policy_permissive.py +0 -46
  77. exonware/xwlazy/package/strategies/package_timing_clean.py +0 -67
  78. exonware/xwlazy/package/strategies/package_timing_full.py +0 -66
  79. exonware/xwlazy/package/strategies/package_timing_smart.py +0 -68
  80. exonware/xwlazy/package/strategies/package_timing_temporary.py +0 -66
  81. exonware/xwlazy/runtime/__init__.py +0 -18
  82. exonware/xwlazy/runtime/adaptive_learner.py +0 -129
  83. exonware/xwlazy/runtime/base.py +0 -274
  84. exonware/xwlazy/runtime/facade.py +0 -94
  85. exonware/xwlazy/runtime/intelligent_selector.py +0 -170
  86. exonware/xwlazy/runtime/metrics.py +0 -60
  87. exonware/xwlazy/runtime/performance.py +0 -37
  88. exonware_xwlazy-0.1.0.22.dist-info/RECORD +0 -87
  89. {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/WHEEL +0 -0
  90. {exonware_xwlazy-0.1.0.22.dist-info → exonware_xwlazy-1.0.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,1396 +0,0 @@
1
- """
2
- #exonware/xwlazy/src/exonware/xwlazy/contracts.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
- Contracts for Lazy Loading System
11
-
12
- This module defines all interfaces, enums, and protocols for the lazy loading
13
- system following GUIDE_ARCH.md structure.
14
- """
15
-
16
- from typing import Protocol, Dict, List, Optional, Any, Tuple, runtime_checkable
17
- from types import ModuleType
18
-
19
- # Import enums and dataclasses from defs.py
20
- from .defs import (
21
- LazyLoadMode,
22
- LazyInstallMode,
23
- PathType,
24
- DependencyInfo,
25
- LazyModeConfig,
26
- )
27
-
28
- # =============================================================================
29
- # PROTOCOLS / INTERFACES (Following GUIDE_DEV.md - Use IClass naming)
30
- # =============================================================================
31
-
32
- # NOTE: Old interfaces have been merged into three unified interfaces:
33
- # - IPackageHelper (package operations: discovery, installation, caching, config, manifest, mapping)
34
- # - IModuleHelper (module operations: installation, hooks, finding, interception, loading, importing, registry, bytecode)
35
- # - IRuntime (runtime services: state, learning, selection, metrics, monitoring, caching, registry)
36
-
37
- class IPackageHelper(Protocol):
38
- """
39
- Unified interface for package operations.
40
-
41
- Merges functionality from IPackageDiscovery, IPackageInstaller, IPackageCache,
42
- IConfigManager, IManifestLoader, and IDependencyMapper.
43
- Provides simple, clean API for working with packages (what you pip install).
44
- Packages are what you install via pip: pip install pymongo, pip install msgpack, etc.
45
-
46
- This interface combines:
47
- - Package discovery (mapping import names to package names)
48
- - Package installation (installing/uninstalling packages)
49
- - Package caching (caching installation status and metadata)
50
- - Configuration management (per-package lazy installation configuration)
51
- - Manifest loading (loading and caching dependency manifests)
52
- - Dependency mapping (mapping import names to package names)
53
- """
54
-
55
- # ========================================================================
56
- # Installation Status (from IPackageInstaller + IPackageHelper)
57
- # ========================================================================
58
-
59
- def installed(self, package_name: str) -> bool:
60
- """
61
- Check if a package is installed.
62
-
63
- Uses cache first to avoid expensive operations.
64
- Checks persistent cache, then in-memory cache, then importability.
65
-
66
- Args:
67
- package_name: Package name to check (e.g., 'pymongo', 'msgpack')
68
-
69
- Returns:
70
- True if package is installed, False otherwise
71
- """
72
- ...
73
-
74
- def uninstalled(self, package_name: str) -> bool:
75
- """
76
- Check if a package is uninstalled.
77
-
78
- Uses cache first to avoid expensive operations.
79
- Checks persistent cache, then in-memory cache, then importability.
80
-
81
- Args:
82
- package_name: Package name to check (e.g., 'pymongo', 'msgpack')
83
-
84
- Returns:
85
- True if package is uninstalled, False otherwise
86
- """
87
- ...
88
-
89
- # ========================================================================
90
- # Package Installation (from IPackageInstaller + IPackageHelper)
91
- # ========================================================================
92
-
93
- def install(self, *package_names: str) -> None:
94
- """
95
- Install one or more packages using pip.
96
-
97
- Skips packages that are already installed (using cache).
98
- Only installs unique packages to avoid duplicate operations.
99
- Updates cache after successful installation.
100
-
101
- Args:
102
- *package_names: One or more package names to install (e.g., 'pymongo', 'msgpack')
103
-
104
- Raises:
105
- subprocess.CalledProcessError: If installation fails
106
- """
107
- ...
108
-
109
- def install_package(self, package_name: str, module_name: Optional[str] = None) -> bool:
110
- """
111
- Install a single package (from IPackageInstaller).
112
-
113
- Args:
114
- package_name: Name of package to install
115
- module_name: Optional name of module being imported (for interactive mode)
116
-
117
- Returns:
118
- True if installation successful, False otherwise
119
- """
120
- ...
121
-
122
- def install_and_import(self, module_name: str, package_name: Optional[str] = None) -> Tuple[Optional[ModuleType], bool]:
123
- """
124
- Install package and import module (from IPackageInstaller).
125
-
126
- Args:
127
- module_name: Name of module to import
128
- package_name: Optional package name if different from module name
129
-
130
- Returns:
131
- Tuple of (module_object, success_flag)
132
- """
133
- ...
134
-
135
- def uninstall(self, *package_names: str) -> None:
136
- """
137
- Uninstall one or more packages using pip.
138
-
139
- Skips packages that are already uninstalled (using cache).
140
- Only uninstalls unique packages to avoid duplicate operations.
141
- Updates cache after successful uninstallation.
142
-
143
- Args:
144
- *package_names: One or more package names to uninstall (e.g., 'pymongo', 'msgpack')
145
-
146
- Raises:
147
- subprocess.CalledProcessError: If uninstallation fails
148
- """
149
- ...
150
-
151
- # ========================================================================
152
- # Package Discovery (from IPackageDiscovery)
153
- # ========================================================================
154
-
155
- def discover_all_dependencies(self) -> Dict[str, str]:
156
- """
157
- Discover all dependencies from all available sources.
158
-
159
- Discovers from pyproject.toml, requirements.txt, setup.py, etc.
160
-
161
- Returns:
162
- Dict mapping import_name -> package_name
163
- """
164
- ...
165
-
166
- def get_package_for_import(self, import_name: str) -> Optional[str]:
167
- """
168
- Get package name for a given import name.
169
-
170
- Args:
171
- import_name: Import name (e.g., 'cv2', 'PIL')
172
-
173
- Returns:
174
- Package name (e.g., 'opencv-python', 'Pillow') or None
175
- """
176
- ...
177
-
178
- def get_imports_for_package(self, package_name: str) -> List[str]:
179
- """
180
- Get all possible import names for a package.
181
-
182
- Args:
183
- package_name: Package name (e.g., 'opencv-python')
184
-
185
- Returns:
186
- List of import names (e.g., ['opencv-python', 'cv2'])
187
- """
188
- ...
189
-
190
- # ========================================================================
191
- # Package Caching (from IPackageCache)
192
- # ========================================================================
193
-
194
- def get_cached(self, key: str) -> Optional[Any]:
195
- """
196
- Get cached value.
197
-
198
- Args:
199
- key: Cache key
200
-
201
- Returns:
202
- Cached value or None if not found
203
- """
204
- ...
205
-
206
- def set_cached(self, key: str, value: Any) -> None:
207
- """
208
- Set cached value.
209
-
210
- Args:
211
- key: Cache key
212
- value: Value to cache
213
- """
214
- ...
215
-
216
- def clear_cache(self) -> None:
217
- """Clear all cached values."""
218
- ...
219
-
220
- def is_cache_valid(self, key: str) -> bool:
221
- """
222
- Check if cache entry is still valid.
223
-
224
- Args:
225
- key: Cache key
226
-
227
- Returns:
228
- True if valid, False otherwise
229
- """
230
- ...
231
-
232
- # ========================================================================
233
- # Configuration Management (from IConfigManager)
234
- # ========================================================================
235
-
236
- def is_enabled(self, package_name: str) -> bool:
237
- """
238
- Check if lazy install is enabled for a package.
239
-
240
- Args:
241
- package_name: Package name
242
-
243
- Returns:
244
- True if enabled, False otherwise
245
- """
246
- ...
247
-
248
- def get_mode(self, package_name: str) -> str:
249
- """
250
- Get installation mode for a package.
251
-
252
- Args:
253
- package_name: Package name
254
-
255
- Returns:
256
- Mode string
257
- """
258
- ...
259
-
260
- def get_load_mode(self, package_name: str) -> LazyLoadMode:
261
- """
262
- Get load mode for a package.
263
-
264
- Args:
265
- package_name: Package name
266
-
267
- Returns:
268
- LazyLoadMode enum
269
- """
270
- ...
271
-
272
- def get_install_mode(self, package_name: str) -> LazyInstallMode:
273
- """
274
- Get install mode for a package.
275
-
276
- Args:
277
- package_name: Package name
278
-
279
- Returns:
280
- LazyInstallMode enum
281
- """
282
- ...
283
-
284
- def get_mode_config(self, package_name: str) -> Optional[LazyModeConfig]:
285
- """
286
- Get full mode configuration for a package.
287
-
288
- Args:
289
- package_name: Package name
290
-
291
- Returns:
292
- LazyModeConfig or None
293
- """
294
- ...
295
-
296
- # ========================================================================
297
- # Manifest Loading (from IManifestLoader)
298
- # ========================================================================
299
-
300
- def get_manifest_signature(self, package_name: str) -> Optional[Tuple[str, float, float]]:
301
- """
302
- Get manifest file signature (path, mtime, size).
303
-
304
- Args:
305
- package_name: Package name
306
-
307
- Returns:
308
- Tuple of (path, mtime, size) or None
309
- """
310
- ...
311
-
312
- def get_shared_dependencies(self, package_name: str, signature: Optional[Tuple[str, float, float]] = None) -> Dict[str, str]:
313
- """
314
- Get shared dependencies from manifest.
315
-
316
- Args:
317
- package_name: Package name
318
- signature: Optional signature for cache validation
319
-
320
- Returns:
321
- Dict mapping import_name -> package_name
322
- """
323
- ...
324
-
325
- def get_watched_prefixes(self, package_name: str) -> Tuple[str, ...]:
326
- """
327
- Get watched prefixes from manifest.
328
-
329
- Args:
330
- package_name: Package name
331
-
332
- Returns:
333
- Tuple of watched prefixes
334
- """
335
- ...
336
-
337
- # ========================================================================
338
- # Dependency Mapping (from IDependencyMapper)
339
- # ========================================================================
340
-
341
- def get_package_name(self, import_name: str) -> Optional[str]:
342
- """
343
- Get package name for an import name (from IDependencyMapper).
344
-
345
- Note: This is similar to get_package_for_import() but uses different naming.
346
- Both methods serve the same purpose - mapping import names to package names.
347
-
348
- Args:
349
- import_name: Import name (e.g., 'cv2', 'msgpack')
350
-
351
- Returns:
352
- Package name (e.g., 'opencv-python', 'msgpack') or None
353
- """
354
- ...
355
-
356
- def get_import_names(self, package_name: str) -> List[str]:
357
- """
358
- Get all import names for a package (from IDependencyMapper).
359
-
360
- Note: This is similar to get_imports_for_package() but uses different naming.
361
- Both methods serve the same purpose - mapping package names to import names.
362
-
363
- Args:
364
- package_name: Package name
365
-
366
- Returns:
367
- List of import names
368
- """
369
- ...
370
-
371
- def is_stdlib_or_builtin(self, import_name: str) -> bool:
372
- """
373
- Check if import name is stdlib or builtin.
374
-
375
- Args:
376
- import_name: Import name to check
377
-
378
- Returns:
379
- True if stdlib/builtin, False otherwise
380
- """
381
- ...
382
-
383
- class IModuleHelper(Protocol):
384
- """
385
- Unified interface for module operations.
386
-
387
- Merges functionality from IModuleInstaller, IImportHook, IMetaPathFinder,
388
- IImportInterceptor, ILazyLoader, ILazyImporter, IWatchedRegistry, and IBytecodeCache.
389
- Provides simple, clean API for working with modules (what you import).
390
- Modules are what you use in Python code: import bson, import msgpack, etc.
391
-
392
- This interface combines:
393
- - Module installation (installing and importing modules)
394
- - Import hooks (intercepting import failures)
395
- - Meta path finding (sys.meta_path hook for lazy installation)
396
- - Import interception (high-level import interception)
397
- - Lazy loading (deferred module loading)
398
- - Lazy importing (lazy module loading with strategies)
399
- - Watched registry (tracking watched module prefixes)
400
- - Bytecode caching (caching compiled Python bytecode)
401
- """
402
-
403
- # ========================================================================
404
- # Module Operations (from IModuleHelper - existing)
405
- # ========================================================================
406
-
407
- def to_package(self, module_name: str) -> Optional[str]:
408
- """
409
- Map module name to package name.
410
-
411
- Args:
412
- module_name: Module name (e.g., 'bson', 'msgpack')
413
-
414
- Returns:
415
- Package name (e.g., 'pymongo', 'msgpack') or None if not found
416
- """
417
- ...
418
-
419
- def installed(self, module_name: str) -> bool:
420
- """
421
- Check if a module is installed.
422
-
423
- Uses cache first to avoid expensive operations.
424
- Maps module to package and checks if package is installed.
425
-
426
- Args:
427
- module_name: Module name to check (e.g., 'bson')
428
-
429
- Returns:
430
- True if module is installed, False otherwise
431
- """
432
- ...
433
-
434
- def uninstalled(self, module_name: str) -> bool:
435
- """
436
- Check if a module is uninstalled.
437
-
438
- Uses cache first to avoid expensive operations.
439
- Maps module to package and checks if package is uninstalled.
440
-
441
- Args:
442
- module_name: Module name to check (e.g., 'bson')
443
-
444
- Returns:
445
- True if module is uninstalled, False otherwise
446
- """
447
- ...
448
-
449
- def install(self, *module_names: str) -> None:
450
- """
451
- Install one or more modules by mapping to packages first.
452
-
453
- First deduplicates modules, then maps to packages, then installs packages.
454
- Skips modules that are already installed (using cache).
455
-
456
- Args:
457
- *module_names: One or more module names to install (e.g., 'bson', 'msgpack')
458
-
459
- Raises:
460
- subprocess.CalledProcessError: If installation fails
461
- """
462
- ...
463
-
464
- def uninstall(self, *module_names: str) -> None:
465
- """
466
- Uninstall one or more modules by mapping to packages first.
467
-
468
- First deduplicates modules, then maps to packages, then uninstalls packages.
469
- Skips modules that are already uninstalled (using cache).
470
-
471
- Args:
472
- *module_names: One or more module names to uninstall (e.g., 'bson', 'msgpack')
473
-
474
- Raises:
475
- subprocess.CalledProcessError: If uninstallation fails
476
- """
477
- ...
478
-
479
- def load(self, *module_names: str) -> List[ModuleType]:
480
- """
481
- Load one or more modules into memory.
482
-
483
- Imports modules and returns them. Uses lazy loading if enabled.
484
-
485
- Args:
486
- *module_names: One or more module names to load (e.g., 'bson', 'msgpack')
487
-
488
- Returns:
489
- List of loaded module objects
490
-
491
- Raises:
492
- ImportError: If module cannot be loaded
493
- """
494
- ...
495
-
496
- def unload(self, *module_names: str) -> None:
497
- """
498
- Unload one or more modules from memory.
499
-
500
- Removes modules from sys.modules and clears caches.
501
- Useful for freeing memory or forcing reload.
502
-
503
- Args:
504
- *module_names: One or more module names to unload (e.g., 'bson', 'msgpack')
505
- """
506
- ...
507
-
508
- # ========================================================================
509
- # Module Installation (from IModuleInstaller)
510
- # ========================================================================
511
-
512
- def install_and_import(self, module_name: str, package_name: Optional[str] = None) -> Tuple[Optional[ModuleType], bool]:
513
- """
514
- Install package and import module (from IModuleInstaller).
515
-
516
- CONTRACT:
517
- 1. Check persistent cache FIRST - if installed, import directly
518
- 2. Check importability - if importable, import directly
519
- 3. If not importable, remove finders from sys.meta_path
520
- 4. Install package via pip
521
- 5. Restore finders
522
- 6. Import module
523
- 7. Mark in persistent cache
524
- 8. Return (module, success)
525
-
526
- Args:
527
- module_name: Name of module to import
528
- package_name: Optional package name (if different from module)
529
-
530
- Returns:
531
- Tuple of (module_object, success_flag)
532
- """
533
- ...
534
-
535
- def is_package_installed(self, package_name: str) -> bool:
536
- """
537
- Check if package is installed (from IModuleInstaller).
538
-
539
- CONTRACT:
540
- - Check persistent cache FIRST
541
- - Check in-memory cache second
542
- - Check importability last
543
-
544
- Args:
545
- package_name: Package name to check
546
-
547
- Returns:
548
- True if installed, False otherwise
549
- """
550
- ...
551
-
552
- def mark_installed(self, package_name: str, version: Optional[str] = None) -> None:
553
- """
554
- Mark package as installed in persistent cache (from IModuleInstaller).
555
-
556
- Args:
557
- package_name: Package name
558
- version: Optional version string
559
- """
560
- ...
561
-
562
- # ========================================================================
563
- # Import Hooks (from IImportHook)
564
- # ========================================================================
565
-
566
- def install_hook(self) -> None:
567
- """Install the import hook into sys.meta_path."""
568
- ...
569
-
570
- def uninstall_hook(self) -> None:
571
- """Uninstall the import hook from sys.meta_path."""
572
- ...
573
-
574
- def is_hook_installed(self) -> bool:
575
- """
576
- Check if hook is installed.
577
-
578
- Returns:
579
- True if hook is in sys.meta_path, False otherwise
580
- """
581
- ...
582
-
583
- def handle_import_error(self, module_name: str) -> Optional[Any]:
584
- """
585
- Handle ImportError by attempting to install and re-import (from IImportHook).
586
-
587
- Args:
588
- module_name: Name of module that failed to import
589
-
590
- Returns:
591
- Imported module if successful, None otherwise
592
- """
593
- ...
594
-
595
- # ========================================================================
596
- # Meta Path Finding (from IMetaPathFinder)
597
- # ========================================================================
598
-
599
- def find_spec(self, fullname: str, path: Optional[str] = None, target=None) -> Optional[Any]:
600
- """
601
- Find module spec - intercepts imports to enable lazy installation (from IMetaPathFinder).
602
-
603
- CONTRACT:
604
- - If module is already in sys.modules: return None (let Python use it)
605
- - If module starts with '_': return None (skip C extensions/internal modules)
606
- - If lazy install is disabled: only intercept watched modules
607
- - If lazy install is enabled: intercept ALL missing imports
608
- - If module is in persistent cache as installed: return None (let Python import normally)
609
- - If module is missing and lazy install enabled: install it, then return None
610
-
611
- Args:
612
- fullname: Full module name (e.g., 'msgpack', 'yaml._yaml')
613
- path: Optional path (for package imports)
614
- target: Optional target module
615
-
616
- Returns:
617
- ModuleSpec if intercepted, None to let Python handle normally
618
- """
619
- ...
620
-
621
- def should_intercept(self, fullname: str) -> bool:
622
- """
623
- Determine if a module should be intercepted (from IMetaPathFinder).
624
-
625
- CONTRACT:
626
- - Return False if module is in sys.modules
627
- - Return False if module starts with '_' (C extension/internal)
628
- - Return False if lazy install disabled and module not watched
629
- - Return True if lazy install enabled and module not in cache
630
-
631
- Args:
632
- fullname: Full module name
633
-
634
- Returns:
635
- True if should intercept, False otherwise
636
- """
637
- ...
638
-
639
- def is_module_installed(self, fullname: str) -> bool:
640
- """
641
- Check if module is already installed (in cache or importable) (from IMetaPathFinder).
642
-
643
- CONTRACT:
644
- - Check persistent cache FIRST (fastest)
645
- - Check in-memory cache second
646
- - Check importability last (slowest)
647
-
648
- Args:
649
- fullname: Full module name
650
-
651
- Returns:
652
- True if installed, False otherwise
653
- """
654
- ...
655
-
656
- # ========================================================================
657
- # Import Interception (from IImportInterceptor)
658
- # ========================================================================
659
-
660
- def intercept_missing_import(self, module_name: str) -> Optional[ModuleType]:
661
- """
662
- Intercept a missing import and attempt to install it (from IImportInterceptor).
663
-
664
- CONTRACT:
665
- 1. Check if module is already installed (persistent cache)
666
- 2. If installed, return None (let Python import normally)
667
- 3. If not installed and lazy install enabled:
668
- a. Install package
669
- b. Import module
670
- c. Mark in persistent cache
671
- d. Return module
672
- 4. If lazy install disabled, return None
673
-
674
- Args:
675
- module_name: Name of module that failed to import
676
-
677
- Returns:
678
- Imported module if successful, None otherwise
679
- """
680
- ...
681
-
682
- def should_intercept_module(self, module_name: str) -> bool:
683
- """
684
- Determine if a module should be intercepted (from IImportInterceptor).
685
-
686
- CONTRACT:
687
- - Return False if module starts with '_' (C extension/internal)
688
- - Return False if module is stdlib/builtin
689
- - Return False if module is in deny list
690
- - Return False if lazy install disabled and module not watched
691
- - Return True if lazy install enabled
692
-
693
- Args:
694
- module_name: Module name to check
695
-
696
- Returns:
697
- True if should intercept, False otherwise
698
- """
699
- ...
700
-
701
- def prevent_recursion(self, module_name: str) -> bool:
702
- """
703
- Check if we should prevent recursion for this module (from IImportInterceptor).
704
-
705
- CONTRACT:
706
- - Return True if installation is in progress
707
- - Return True if import is in progress for this module
708
- - Return False otherwise
709
-
710
- Args:
711
- module_name: Module name to check
712
-
713
- Returns:
714
- True if should prevent recursion, False otherwise
715
- """
716
- ...
717
-
718
- # ========================================================================
719
- # Lazy Loading (from ILazyLoader)
720
- # ========================================================================
721
-
722
- def load_module(self, module_path: str) -> ModuleType:
723
- """
724
- Load a module lazily (from ILazyLoader).
725
-
726
- Args:
727
- module_path: Full module path to load
728
-
729
- Returns:
730
- Loaded module
731
- """
732
- ...
733
-
734
- def is_loaded(self, module_path: str) -> bool:
735
- """
736
- Check if module is already loaded (from ILazyLoader).
737
-
738
- Args:
739
- module_path: Module path to check
740
-
741
- Returns:
742
- True if loaded, False otherwise
743
- """
744
- ...
745
-
746
- def unload_module(self, module_path: str) -> None:
747
- """
748
- Unload a module from cache (from ILazyLoader).
749
-
750
- Args:
751
- module_path: Module path to unload
752
- """
753
- ...
754
-
755
- # ========================================================================
756
- # Lazy Importing (from ILazyImporter)
757
- # ========================================================================
758
-
759
- def import_module(self, module_name: str, package_name: Optional[str] = None) -> Any:
760
- """
761
- Import a module with lazy loading (from ILazyImporter).
762
-
763
- Args:
764
- module_name: Module name to import
765
- package_name: Optional package name
766
-
767
- Returns:
768
- Imported module
769
- """
770
- ...
771
-
772
- def enable_lazy_loading(self, load_mode: LazyLoadMode) -> None:
773
- """
774
- Enable lazy loading with a mode (from ILazyImporter).
775
-
776
- Args:
777
- load_mode: LazyLoadMode to use
778
- """
779
- ...
780
-
781
- def disable_lazy_loading(self) -> None:
782
- """Disable lazy loading (from ILazyImporter)."""
783
- ...
784
-
785
- def is_lazy_loading_enabled(self) -> bool:
786
- """
787
- Check if lazy loading is enabled (from ILazyImporter).
788
-
789
- Returns:
790
- True if enabled, False otherwise
791
- """
792
- ...
793
-
794
- # ========================================================================
795
- # Watched Registry (from IWatchedRegistry)
796
- # ========================================================================
797
-
798
- def has_root(self, root_name: str) -> bool:
799
- """
800
- Check if a root module name is being watched (from IWatchedRegistry).
801
-
802
- Args:
803
- root_name: Root module name (e.g., 'msgpack', 'yaml')
804
-
805
- Returns:
806
- True if watched, False otherwise
807
- """
808
- ...
809
-
810
- def get_matching_prefixes(self, fullname: str) -> Tuple[str, ...]:
811
- """
812
- Get all watched prefixes that match a module name (from IWatchedRegistry).
813
-
814
- Args:
815
- fullname: Full module name
816
-
817
- Returns:
818
- Tuple of matching prefixes
819
- """
820
- ...
821
-
822
- def is_prefix_owned_by(self, prefix: str, package_name: str) -> bool:
823
- """
824
- Check if a prefix is owned by a package (from IWatchedRegistry).
825
-
826
- Args:
827
- prefix: Prefix to check
828
- package_name: Package name
829
-
830
- Returns:
831
- True if owned, False otherwise
832
- """
833
- ...
834
-
835
- def is_watched_registry_empty(self) -> bool:
836
- """
837
- Check if registry is empty (from IWatchedRegistry).
838
-
839
- Returns:
840
- True if empty, False otherwise
841
- """
842
- ...
843
-
844
- # ========================================================================
845
- # Bytecode Caching (from IBytecodeCache)
846
- # ========================================================================
847
-
848
- def get_bytecode(self, module_path: str, source_code: str) -> Optional[bytes]:
849
- """
850
- Get cached bytecode for module (from IBytecodeCache).
851
-
852
- Args:
853
- module_path: Module path
854
- source_code: Source code
855
-
856
- Returns:
857
- Cached bytecode or None
858
- """
859
- ...
860
-
861
- def cache_bytecode(self, module_path: str, source_code: str, bytecode: bytes) -> None:
862
- """
863
- Cache bytecode for module (from IBytecodeCache).
864
-
865
- Args:
866
- module_path: Module path
867
- source_code: Source code
868
- bytecode: Compiled bytecode
869
- """
870
- ...
871
-
872
- def clear_bytecode_cache(self) -> None:
873
- """Clear bytecode cache (from IBytecodeCache)."""
874
- ...
875
-
876
- class IRuntime(Protocol):
877
- """
878
- Unified interface for runtime services.
879
-
880
- Merges functionality from IStateManager, IAdaptiveLearner, IIntelligentSelector,
881
- IMetricsCollector, IPerformanceMonitor, IMultiTierCache, and IRegistry.
882
- Provides runtime services for state management, learning, monitoring, and caching.
883
-
884
- This interface combines:
885
- - State management (persistent state for lazy installation)
886
- - Adaptive learning (learning import patterns and optimizing loading)
887
- - Intelligent selection (selecting optimal modes based on load characteristics)
888
- - Metrics collection (collecting and aggregating performance metrics)
889
- - Performance monitoring (monitoring lazy loading performance)
890
- - Multi-tier caching (L1/L2/L3 caching)
891
- - Registry (managing instances by key)
892
- """
893
-
894
- # ========================================================================
895
- # State Management (from IStateManager)
896
- # ========================================================================
897
-
898
- def get_manual_state(self) -> Optional[bool]:
899
- """
900
- Get manual state override (from IStateManager).
901
-
902
- Returns:
903
- True/False if manually set, None otherwise
904
- """
905
- ...
906
-
907
- def set_manual_state(self, value: Optional[bool]) -> None:
908
- """
909
- Set manual state override (from IStateManager).
910
-
911
- Args:
912
- value: True/False to set, None to clear
913
- """
914
- ...
915
-
916
- def get_cached_auto_state(self) -> Optional[bool]:
917
- """
918
- Get cached auto-detected state (from IStateManager).
919
-
920
- Returns:
921
- True/False if cached, None otherwise
922
- """
923
- ...
924
-
925
- def set_auto_state(self, value: Optional[bool]) -> None:
926
- """
927
- Set cached auto-detected state (from IStateManager).
928
-
929
- Args:
930
- value: True/False to cache, None to clear
931
- """
932
- ...
933
-
934
- # ========================================================================
935
- # Adaptive Learning (from IAdaptiveLearner)
936
- # ========================================================================
937
-
938
- def record_import(self, module_name: str, import_time: float) -> None:
939
- """
940
- Record an import event (from IAdaptiveLearner).
941
-
942
- Args:
943
- module_name: Module name that was imported
944
- import_time: Time taken to import (seconds)
945
- """
946
- ...
947
-
948
- def predict_next_imports(self, current_module: str, count: int = 3) -> List[str]:
949
- """
950
- Predict next likely imports based on patterns (from IAdaptiveLearner).
951
-
952
- Args:
953
- current_module: Current module name
954
- count: Number of predictions to return
955
-
956
- Returns:
957
- List of predicted module names
958
- """
959
- ...
960
-
961
- def get_module_score(self, module_name: str) -> float:
962
- """
963
- Get priority score for a module (from IAdaptiveLearner).
964
-
965
- Args:
966
- module_name: Module name
967
-
968
- Returns:
969
- Priority score (higher = more important)
970
- """
971
- ...
972
-
973
- # ========================================================================
974
- # Intelligent Selection (from IIntelligentSelector)
975
- # ========================================================================
976
-
977
- def detect_load_level(
978
- self,
979
- module_count: int = 0,
980
- total_import_time: float = 0.0,
981
- import_count: int = 0,
982
- memory_usage_mb: float = 0.0
983
- ) -> Any:
984
- """
985
- Detect current load level (from IIntelligentSelector).
986
-
987
- Args:
988
- module_count: Number of modules loaded
989
- total_import_time: Total import time (seconds)
990
- import_count: Number of imports
991
- memory_usage_mb: Memory usage in MB
992
-
993
- Returns:
994
- LoadLevel enum
995
- """
996
- ...
997
-
998
- def get_optimal_mode(self, load_level: Any) -> Tuple[LazyLoadMode, LazyInstallMode]:
999
- """
1000
- Get optimal mode for a load level (from IIntelligentSelector).
1001
-
1002
- Args:
1003
- load_level: LoadLevel enum
1004
-
1005
- Returns:
1006
- Tuple of (LazyLoadMode, LazyInstallMode)
1007
- """
1008
- ...
1009
-
1010
- def update_mode_map(self, mode_map: Dict[Any, Tuple[LazyLoadMode, LazyInstallMode]]) -> None:
1011
- """
1012
- Update mode mapping with benchmark results (from IIntelligentSelector).
1013
-
1014
- Args:
1015
- mode_map: New mode mapping
1016
- """
1017
- ...
1018
-
1019
- # ========================================================================
1020
- # Metrics Collection (from IMetricsCollector)
1021
- # ========================================================================
1022
-
1023
- def record_metric(self, name: str, value: float, timestamp: Optional[Any] = None) -> None:
1024
- """
1025
- Record a metric value (from IMetricsCollector).
1026
-
1027
- Args:
1028
- name: Metric name
1029
- value: Metric value
1030
- timestamp: Optional timestamp
1031
- """
1032
- ...
1033
-
1034
- def get_metric_stats(self, name: str) -> Dict[str, Any]:
1035
- """
1036
- Get statistics for a metric (from IMetricsCollector).
1037
-
1038
- Args:
1039
- name: Metric name
1040
-
1041
- Returns:
1042
- Dict with count, total, average, min, max
1043
- """
1044
- ...
1045
-
1046
- def get_all_stats(self) -> Dict[str, Dict[str, Any]]:
1047
- """
1048
- Get statistics for all metrics (from IMetricsCollector).
1049
-
1050
- Returns:
1051
- Dict mapping metric name -> stats
1052
- """
1053
- ...
1054
-
1055
- def clear_metrics(self) -> None:
1056
- """Clear all collected metrics (from IMetricsCollector)."""
1057
- ...
1058
-
1059
- # ========================================================================
1060
- # Performance Monitoring (from IPerformanceMonitor)
1061
- # ========================================================================
1062
-
1063
- def record_load_time(self, module: str, load_time: float) -> None:
1064
- """
1065
- Record module load time (from IPerformanceMonitor).
1066
-
1067
- Args:
1068
- module: Module name
1069
- load_time: Load time in seconds
1070
- """
1071
- ...
1072
-
1073
- def record_access(self, module: str) -> None:
1074
- """
1075
- Record module access (from IPerformanceMonitor).
1076
-
1077
- Args:
1078
- module: Module name
1079
- """
1080
- ...
1081
-
1082
- def get_performance_stats(self) -> Dict[str, Any]:
1083
- """
1084
- Get performance statistics (from IPerformanceMonitor).
1085
-
1086
- Returns:
1087
- Dict with load_times, access_counts, memory_usage
1088
- """
1089
- ...
1090
-
1091
- # ========================================================================
1092
- # Multi-Tier Cache (from IMultiTierCache)
1093
- # ========================================================================
1094
-
1095
- def get_cached_value(self, key: str) -> Optional[Any]:
1096
- """
1097
- Get value from cache (L1 -> L2 -> L3) (from IMultiTierCache).
1098
-
1099
- Args:
1100
- key: Cache key
1101
-
1102
- Returns:
1103
- Cached value or None
1104
- """
1105
- ...
1106
-
1107
- def set_cached_value(self, key: str, value: Any) -> None:
1108
- """
1109
- Set value in cache (L1 and L2) (from IMultiTierCache).
1110
-
1111
- Args:
1112
- key: Cache key
1113
- value: Value to cache
1114
- """
1115
- ...
1116
-
1117
- def clear_multi_tier_cache(self) -> None:
1118
- """Clear all cache tiers (from IMultiTierCache)."""
1119
- ...
1120
-
1121
- def shutdown_cache(self) -> None:
1122
- """Shutdown cache (flush L2, cleanup threads) (from IMultiTierCache)."""
1123
- ...
1124
-
1125
- # ========================================================================
1126
- # Registry (from IRegistry)
1127
- # ========================================================================
1128
-
1129
- def get_instance(self, key: str) -> Any:
1130
- """
1131
- Get instance by key (from IRegistry).
1132
-
1133
- Args:
1134
- key: Registry key
1135
-
1136
- Returns:
1137
- Registered instance
1138
- """
1139
- ...
1140
-
1141
- def register(self, key: str, instance: Any) -> None:
1142
- """
1143
- Register an instance (from IRegistry).
1144
-
1145
- Args:
1146
- key: Registry key
1147
- instance: Instance to register
1148
- """
1149
- ...
1150
-
1151
- def unregister(self, key: str) -> None:
1152
- """
1153
- Unregister an instance (from IRegistry).
1154
-
1155
- Args:
1156
- key: Registry key
1157
- """
1158
- ...
1159
-
1160
- def has_key(self, key: str) -> bool:
1161
- """
1162
- Check if key is registered (from IRegistry).
1163
-
1164
- Args:
1165
- key: Registry key
1166
-
1167
- Returns:
1168
- True if registered, False otherwise
1169
- """
1170
- ...
1171
-
1172
- # =============================================================================
1173
- # STRATEGY INTERFACES (New Strategy Pattern)
1174
- # =============================================================================
1175
-
1176
- @runtime_checkable
1177
- class ICachingStrategy(Protocol):
1178
- """
1179
- Generic caching strategy interface - works with ANY data type.
1180
-
1181
- Used by both modules and packages for caching strategies.
1182
- """
1183
- def get(self, key: str) -> Optional[Any]:
1184
- """Get value from cache."""
1185
- ...
1186
-
1187
- def set(self, key: str, value: Any) -> None:
1188
- """Set value in cache."""
1189
- ...
1190
-
1191
- def invalidate(self, key: str) -> None:
1192
- """Invalidate cached value."""
1193
- ...
1194
-
1195
- def clear(self) -> None:
1196
- """Clear all cached values."""
1197
- ...
1198
-
1199
- @runtime_checkable
1200
- class IModuleHelperStrategy(Protocol):
1201
- """
1202
- Module helper strategy interface.
1203
-
1204
- Operations on a single module (loading, unloading, checking).
1205
- """
1206
- def load(self, module_path: str, package_helper: Any) -> ModuleType:
1207
- """Load the module."""
1208
- ...
1209
-
1210
- def unload(self, module_path: str) -> None:
1211
- """Unload the module."""
1212
- ...
1213
-
1214
- def check_importability(self, path: str) -> bool:
1215
- """Check if module is importable."""
1216
- ...
1217
-
1218
- @runtime_checkable
1219
- class IPackageHelperStrategy(Protocol):
1220
- """
1221
- Package helper strategy interface.
1222
-
1223
- Operations on a single package (installing, uninstalling, checking).
1224
- """
1225
- def install(self, package_name: str) -> bool:
1226
- """Install the package."""
1227
- ...
1228
-
1229
- def uninstall(self, package_name: str) -> None:
1230
- """Uninstall the package."""
1231
- ...
1232
-
1233
- def check_installed(self, name: str) -> bool:
1234
- """Check if package is installed."""
1235
- ...
1236
-
1237
- def get_version(self, name: str) -> Optional[str]:
1238
- """Get installed version."""
1239
- ...
1240
-
1241
- @runtime_checkable
1242
- class IModuleManagerStrategy(Protocol):
1243
- """
1244
- Module manager strategy interface.
1245
-
1246
- Orchestrates multiple modules (loading, hooks, error handling).
1247
- """
1248
- def load_module(self, module_path: str) -> ModuleType:
1249
- """Load a module."""
1250
- ...
1251
-
1252
- def unload_module(self, module_path: str) -> None:
1253
- """Unload a module."""
1254
- ...
1255
-
1256
- def install_hook(self) -> None:
1257
- """Install import hook."""
1258
- ...
1259
-
1260
- def uninstall_hook(self) -> None:
1261
- """Uninstall import hook."""
1262
- ...
1263
-
1264
- def handle_import_error(self, module_name: str) -> Optional[ModuleType]:
1265
- """Handle import error."""
1266
- ...
1267
-
1268
- @runtime_checkable
1269
- class IPackageManagerStrategy(Protocol):
1270
- """
1271
- Package manager strategy interface.
1272
-
1273
- Orchestrates multiple packages (installation, discovery, policy).
1274
- """
1275
- def install_package(self, package_name: str, module_name: Optional[str] = None) -> bool:
1276
- """Install a package."""
1277
- ...
1278
-
1279
- def uninstall_package(self, package_name: str) -> None:
1280
- """Uninstall a package."""
1281
- ...
1282
-
1283
- def discover_dependencies(self) -> Dict[str, str]:
1284
- """Discover dependencies."""
1285
- ...
1286
-
1287
- def check_security_policy(self, package_name: str) -> Tuple[bool, str]:
1288
- """Check security policy."""
1289
- ...
1290
-
1291
- # =============================================================================
1292
- # NEW PACKAGE STRATEGY TYPES (Redesigned Architecture)
1293
- # =============================================================================
1294
-
1295
- @runtime_checkable
1296
- class IInstallExecutionStrategy(Protocol):
1297
- """
1298
- Installation execution strategy - HOW to execute installation.
1299
-
1300
- Defines the mechanism for actually installing packages (pip, wheel, cached, async).
1301
- """
1302
- def execute_install(self, package_name: str, policy_args: List[str]) -> Any:
1303
- """Execute installation of a package."""
1304
- ...
1305
-
1306
- def execute_uninstall(self, package_name: str) -> bool:
1307
- """Execute uninstallation of a package."""
1308
- ...
1309
-
1310
- @runtime_checkable
1311
- class IInstallTimingStrategy(Protocol):
1312
- """
1313
- Installation timing strategy - WHEN to install packages.
1314
-
1315
- Defines when packages should be installed (on-demand, upfront, temporary, etc.).
1316
- """
1317
- def should_install_now(self, package_name: str, context: Any) -> bool:
1318
- """Determine if package should be installed now."""
1319
- ...
1320
-
1321
- def should_uninstall_after(self, package_name: str, context: Any) -> bool:
1322
- """Determine if package should be uninstalled after use."""
1323
- ...
1324
-
1325
- def get_install_priority(self, packages: List[str]) -> List[str]:
1326
- """Get priority order for installing packages."""
1327
- ...
1328
-
1329
- @runtime_checkable
1330
- class IDiscoveryStrategy(Protocol):
1331
- """
1332
- Discovery strategy - HOW to discover dependencies.
1333
-
1334
- Defines how to find dependencies (from files, manifest, auto-detect).
1335
- """
1336
- def discover(self, project_root: Any) -> Dict[str, str]:
1337
- """Discover dependencies from sources."""
1338
- ...
1339
-
1340
- def get_source(self, import_name: str) -> Optional[str]:
1341
- """Get the source of a discovered dependency."""
1342
- ...
1343
-
1344
- @runtime_checkable
1345
- class IPolicyStrategy(Protocol):
1346
- """
1347
- Policy strategy - WHAT can be installed (security/policy).
1348
-
1349
- Defines security policies and what packages are allowed/denied.
1350
- """
1351
- def is_allowed(self, package_name: str) -> Tuple[bool, str]:
1352
- """Check if package is allowed to be installed."""
1353
- ...
1354
-
1355
- def get_pip_args(self, package_name: str) -> List[str]:
1356
- """Get pip arguments based on policy."""
1357
- ...
1358
-
1359
- @runtime_checkable
1360
- class IMappingStrategy(Protocol):
1361
- """
1362
- Mapping strategy - HOW to map import names to package names.
1363
-
1364
- Defines how to map import names (e.g., 'cv2') to package names (e.g., 'opencv-python').
1365
- """
1366
- def map_import_to_package(self, import_name: str) -> Optional[str]:
1367
- """Map import name to package name."""
1368
- ...
1369
-
1370
- def map_package_to_imports(self, package_name: str) -> List[str]:
1371
- """Map package name to possible import names."""
1372
- ...
1373
-
1374
- # =============================================================================
1375
- # EXPORT ALL
1376
- # =============================================================================
1377
-
1378
- __all__ = [
1379
- # Interfaces/Protocols (Unified)
1380
- 'IPackageHelper', # Merges: IPackageDiscovery, IPackageInstaller, IPackageCache, IConfigManager, IManifestLoader, IDependencyMapper
1381
- 'IModuleHelper', # Merges: IModuleInstaller, IImportHook, IMetaPathFinder, IImportInterceptor, ILazyLoader, ILazyImporter, IWatchedRegistry, IBytecodeCache
1382
- 'IRuntime', # Merges: IStateManager, IAdaptiveLearner, IIntelligentSelector, IMetricsCollector, IPerformanceMonitor, IMultiTierCache, IRegistry
1383
- # Strategy Interfaces (New)
1384
- 'ICachingStrategy',
1385
- 'IModuleHelperStrategy',
1386
- 'IModuleManagerStrategy',
1387
- 'IPackageHelperStrategy',
1388
- 'IPackageManagerStrategy',
1389
- # New Package Strategy Types
1390
- 'IInstallExecutionStrategy',
1391
- 'IInstallTimingStrategy',
1392
- 'IDiscoveryStrategy',
1393
- 'IPolicyStrategy',
1394
- 'IMappingStrategy',
1395
- ]
1396
-