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