exonware-xwlazy 0.1.0.11__py3-none-any.whl → 0.1.0.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. exonware/__init__.py +22 -0
  2. exonware/xwlazy/__init__.py +0 -0
  3. exonware/xwlazy/common/__init__.py +47 -0
  4. exonware/xwlazy/common/base.py +58 -0
  5. exonware/xwlazy/common/cache.py +506 -0
  6. exonware/xwlazy/common/logger.py +268 -0
  7. exonware/xwlazy/common/services/__init__.py +72 -0
  8. exonware/xwlazy/common/services/dependency_mapper.py +234 -0
  9. exonware/xwlazy/common/services/install_async_utils.py +169 -0
  10. exonware/xwlazy/common/services/install_cache_utils.py +257 -0
  11. exonware/xwlazy/common/services/keyword_detection.py +292 -0
  12. exonware/xwlazy/common/services/spec_cache.py +173 -0
  13. exonware/xwlazy/common/strategies/__init__.py +28 -0
  14. exonware/xwlazy/common/strategies/caching_dict.py +45 -0
  15. exonware/xwlazy/common/strategies/caching_installation.py +89 -0
  16. exonware/xwlazy/common/strategies/caching_lfu.py +67 -0
  17. exonware/xwlazy/common/strategies/caching_lru.py +64 -0
  18. exonware/xwlazy/common/strategies/caching_multitier.py +60 -0
  19. exonware/xwlazy/common/strategies/caching_ttl.py +60 -0
  20. {xwlazy/lazy → exonware/xwlazy}/config.py +52 -20
  21. exonware/xwlazy/contracts.py +1410 -0
  22. exonware/xwlazy/defs.py +397 -0
  23. xwlazy/lazy/lazy_errors.py → exonware/xwlazy/errors.py +21 -8
  24. exonware/xwlazy/facade.py +1049 -0
  25. exonware/xwlazy/module/__init__.py +18 -0
  26. exonware/xwlazy/module/base.py +569 -0
  27. exonware/xwlazy/module/data.py +17 -0
  28. exonware/xwlazy/module/facade.py +247 -0
  29. exonware/xwlazy/module/importer_engine.py +2161 -0
  30. exonware/xwlazy/module/strategies/__init__.py +22 -0
  31. exonware/xwlazy/module/strategies/module_helper_lazy.py +94 -0
  32. exonware/xwlazy/module/strategies/module_helper_simple.py +66 -0
  33. exonware/xwlazy/module/strategies/module_manager_advanced.py +112 -0
  34. exonware/xwlazy/module/strategies/module_manager_simple.py +96 -0
  35. exonware/xwlazy/package/__init__.py +18 -0
  36. exonware/xwlazy/package/base.py +807 -0
  37. xwlazy/lazy/host_conf.py → exonware/xwlazy/package/conf.py +62 -10
  38. exonware/xwlazy/package/data.py +17 -0
  39. exonware/xwlazy/package/facade.py +481 -0
  40. exonware/xwlazy/package/services/__init__.py +84 -0
  41. exonware/xwlazy/package/services/async_install_handle.py +89 -0
  42. exonware/xwlazy/package/services/config_manager.py +246 -0
  43. exonware/xwlazy/package/services/discovery.py +374 -0
  44. {xwlazy/lazy → exonware/xwlazy/package/services}/host_packages.py +43 -16
  45. exonware/xwlazy/package/services/install_async.py +278 -0
  46. exonware/xwlazy/package/services/install_cache.py +146 -0
  47. exonware/xwlazy/package/services/install_interactive.py +60 -0
  48. exonware/xwlazy/package/services/install_policy.py +158 -0
  49. exonware/xwlazy/package/services/install_registry.py +56 -0
  50. exonware/xwlazy/package/services/install_result.py +17 -0
  51. exonware/xwlazy/package/services/install_sbom.py +154 -0
  52. exonware/xwlazy/package/services/install_utils.py +83 -0
  53. exonware/xwlazy/package/services/installer_engine.py +408 -0
  54. exonware/xwlazy/package/services/lazy_installer.py +720 -0
  55. {xwlazy/lazy → exonware/xwlazy/package/services}/manifest.py +42 -25
  56. exonware/xwlazy/package/services/strategy_registry.py +188 -0
  57. exonware/xwlazy/package/strategies/__init__.py +57 -0
  58. exonware/xwlazy/package/strategies/package_discovery_file.py +130 -0
  59. exonware/xwlazy/package/strategies/package_discovery_hybrid.py +85 -0
  60. exonware/xwlazy/package/strategies/package_discovery_manifest.py +102 -0
  61. exonware/xwlazy/package/strategies/package_execution_async.py +114 -0
  62. exonware/xwlazy/package/strategies/package_execution_cached.py +91 -0
  63. exonware/xwlazy/package/strategies/package_execution_pip.py +100 -0
  64. exonware/xwlazy/package/strategies/package_execution_wheel.py +107 -0
  65. exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +101 -0
  66. exonware/xwlazy/package/strategies/package_mapping_hybrid.py +106 -0
  67. exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +101 -0
  68. exonware/xwlazy/package/strategies/package_policy_allow_list.py +58 -0
  69. exonware/xwlazy/package/strategies/package_policy_deny_list.py +58 -0
  70. exonware/xwlazy/package/strategies/package_policy_permissive.py +47 -0
  71. exonware/xwlazy/package/strategies/package_timing_clean.py +68 -0
  72. exonware/xwlazy/package/strategies/package_timing_full.py +67 -0
  73. exonware/xwlazy/package/strategies/package_timing_smart.py +69 -0
  74. exonware/xwlazy/package/strategies/package_timing_temporary.py +67 -0
  75. exonware/xwlazy/runtime/__init__.py +18 -0
  76. exonware/xwlazy/runtime/adaptive_learner.py +131 -0
  77. exonware/xwlazy/runtime/base.py +276 -0
  78. exonware/xwlazy/runtime/facade.py +95 -0
  79. exonware/xwlazy/runtime/intelligent_selector.py +173 -0
  80. exonware/xwlazy/runtime/metrics.py +64 -0
  81. exonware/xwlazy/runtime/performance.py +39 -0
  82. exonware/xwlazy/version.py +2 -2
  83. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/METADATA +86 -10
  84. exonware_xwlazy-0.1.0.19.dist-info/RECORD +87 -0
  85. exonware_xwlazy-0.1.0.11.dist-info/RECORD +0 -20
  86. xwlazy/__init__.py +0 -34
  87. xwlazy/lazy/__init__.py +0 -301
  88. xwlazy/lazy/bootstrap.py +0 -106
  89. xwlazy/lazy/lazy_base.py +0 -465
  90. xwlazy/lazy/lazy_contracts.py +0 -290
  91. xwlazy/lazy/lazy_core.py +0 -3727
  92. xwlazy/lazy/logging_utils.py +0 -194
  93. xwlazy/version.py +0 -77
  94. /xwlazy/lazy/lazy_state.py → /exonware/xwlazy/common/services/state_manager.py +0 -0
  95. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/WHEEL +0 -0
  96. {exonware_xwlazy-0.1.0.11.dist-info → exonware_xwlazy-0.1.0.19.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,101 @@
1
+ """
2
+ Manifest-First Mapping Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Manifest-first mapping strategy - manifest takes precedence over discovery.
11
+ """
12
+
13
+ from typing import Optional, List
14
+ from ...package.base import AMappingStrategy
15
+ from ...package.services.manifest import get_manifest_loader
16
+
17
+
18
+ class ManifestFirstMapping(AMappingStrategy):
19
+ """
20
+ Manifest-first mapping strategy.
21
+
22
+ Priority order:
23
+ 1. Manifest dependencies (explicit user configuration - highest priority)
24
+ 2. Discovery mappings (automatic discovery)
25
+ 3. Common mappings (fallback)
26
+ """
27
+
28
+ def __init__(self, package_name: str = 'default'):
29
+ """
30
+ Initialize manifest-first mapping strategy.
31
+
32
+ Args:
33
+ package_name: Package name for isolation
34
+ """
35
+ self._package_name = package_name
36
+ self._discovery = None # Lazy init
37
+
38
+ def _get_discovery(self):
39
+ """Get discovery instance (lazy init)."""
40
+ if self._discovery is None:
41
+ from ...package.services.discovery import LazyDiscovery
42
+ self._discovery = LazyDiscovery(self._package_name)
43
+ return self._discovery
44
+
45
+ def map_import_to_package(self, import_name: str) -> Optional[str]:
46
+ """
47
+ Map import name to package name.
48
+
49
+ Priority: Manifest > Discovery > Common mappings
50
+
51
+ Args:
52
+ import_name: Import name (e.g., 'cv2')
53
+
54
+ Returns:
55
+ Package name (e.g., 'opencv-python') or None
56
+ """
57
+ # Check manifest FIRST - explicit user configuration takes precedence
58
+ loader = get_manifest_loader()
59
+ manifest = loader.get_manifest(self._package_name)
60
+ if manifest:
61
+ package = manifest.get_dependency(import_name)
62
+ if package:
63
+ return package
64
+
65
+ # Check discovery mappings
66
+ discovery = self._get_discovery()
67
+ discovery_mapping = discovery.get_import_package_mapping()
68
+ package = discovery_mapping.get(import_name)
69
+ if package:
70
+ return package
71
+
72
+ # Fallback to common mappings
73
+ common_mappings = discovery.COMMON_MAPPINGS
74
+ return common_mappings.get(import_name)
75
+
76
+ def map_package_to_imports(self, package_name: str) -> List[str]:
77
+ """
78
+ Map package name to possible import names.
79
+
80
+ Args:
81
+ package_name: Package name (e.g., 'opencv-python')
82
+
83
+ Returns:
84
+ List of possible import names (e.g., ['cv2'])
85
+ """
86
+ # Check discovery mappings
87
+ discovery = self._get_discovery()
88
+ package_mapping = discovery.get_package_import_mapping()
89
+ imports = package_mapping.get(package_name, [])
90
+
91
+ # Also check manifest (reverse lookup)
92
+ loader = get_manifest_loader()
93
+ manifest = loader.get_manifest(self._package_name)
94
+ if manifest:
95
+ for import_name, pkg in manifest.dependencies.items():
96
+ if pkg.lower() == package_name.lower():
97
+ if import_name not in imports:
98
+ imports.append(import_name)
99
+
100
+ return imports if imports else [package_name]
101
+
@@ -0,0 +1,58 @@
1
+ """
2
+ Allow List Policy Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Allow list policy - only allows packages in the allow list.
11
+ """
12
+
13
+ from typing import Tuple, List, Set
14
+ from ...package.base import APolicyStrategy
15
+
16
+
17
+ class AllowListPolicy(APolicyStrategy):
18
+ """
19
+ Allow list policy strategy - only allows packages in the allow list.
20
+
21
+ Only packages explicitly in the allow list can be installed.
22
+ """
23
+
24
+ def __init__(self, allowed_packages: Set[str]):
25
+ """
26
+ Initialize allow list policy.
27
+
28
+ Args:
29
+ allowed_packages: Set of allowed package names
30
+ """
31
+ self._allowed = {pkg.lower() for pkg in allowed_packages}
32
+
33
+ def is_allowed(self, package_name: str) -> Tuple[bool, str]:
34
+ """
35
+ Check if package is allowed to be installed.
36
+
37
+ Args:
38
+ package_name: Package name to check
39
+
40
+ Returns:
41
+ Tuple of (allowed: bool, reason: str)
42
+ """
43
+ if package_name.lower() in self._allowed:
44
+ return (True, f"Package '{package_name}' is in allow list")
45
+ return (False, f"Package '{package_name}' is not in allow list")
46
+
47
+ def get_pip_args(self, package_name: str) -> List[str]:
48
+ """
49
+ Get pip arguments based on policy.
50
+
51
+ Args:
52
+ package_name: Package name
53
+
54
+ Returns:
55
+ List of pip arguments (empty for allow list policy)
56
+ """
57
+ return []
58
+
@@ -0,0 +1,58 @@
1
+ """
2
+ Deny List Policy Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Deny list policy - blocks packages in the deny list.
11
+ """
12
+
13
+ from typing import Tuple, List, Set
14
+ from ...package.base import APolicyStrategy
15
+
16
+
17
+ class DenyListPolicy(APolicyStrategy):
18
+ """
19
+ Deny list policy strategy - blocks packages in the deny list.
20
+
21
+ Packages in the deny list cannot be installed.
22
+ """
23
+
24
+ def __init__(self, denied_packages: Set[str]):
25
+ """
26
+ Initialize deny list policy.
27
+
28
+ Args:
29
+ denied_packages: Set of denied package names
30
+ """
31
+ self._denied = {pkg.lower() for pkg in denied_packages}
32
+
33
+ def is_allowed(self, package_name: str) -> Tuple[bool, str]:
34
+ """
35
+ Check if package is allowed to be installed.
36
+
37
+ Args:
38
+ package_name: Package name to check
39
+
40
+ Returns:
41
+ Tuple of (allowed: bool, reason: str)
42
+ """
43
+ if package_name.lower() in self._denied:
44
+ return (False, f"Package '{package_name}' is in deny list")
45
+ return (True, f"Package '{package_name}' is not in deny list")
46
+
47
+ def get_pip_args(self, package_name: str) -> List[str]:
48
+ """
49
+ Get pip arguments based on policy.
50
+
51
+ Args:
52
+ package_name: Package name
53
+
54
+ Returns:
55
+ List of pip arguments (empty for deny list policy)
56
+ """
57
+ return []
58
+
@@ -0,0 +1,47 @@
1
+ """
2
+ Permissive Policy Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Permissive policy - allows all packages (default).
11
+ """
12
+
13
+ from typing import Tuple, List
14
+ from ...package.base import APolicyStrategy
15
+
16
+
17
+ class PermissivePolicy(APolicyStrategy):
18
+ """
19
+ Permissive policy strategy - allows all packages.
20
+
21
+ This is the default policy that doesn't restrict any packages.
22
+ """
23
+
24
+ def is_allowed(self, package_name: str) -> Tuple[bool, str]:
25
+ """
26
+ Check if package is allowed to be installed.
27
+
28
+ Args:
29
+ package_name: Package name to check
30
+
31
+ Returns:
32
+ Tuple of (allowed: bool, reason: str)
33
+ """
34
+ return (True, "Permissive policy allows all packages")
35
+
36
+ def get_pip_args(self, package_name: str) -> List[str]:
37
+ """
38
+ Get pip arguments based on policy.
39
+
40
+ Args:
41
+ package_name: Package name
42
+
43
+ Returns:
44
+ List of pip arguments (empty for permissive policy)
45
+ """
46
+ return []
47
+
@@ -0,0 +1,68 @@
1
+ """
2
+ Clean Timing Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Clean timing - install on usage + uninstall after completion.
11
+ """
12
+
13
+ from typing import List, Any
14
+ from ...package.base import AInstallTimingStrategy
15
+
16
+
17
+ class CleanTiming(AInstallTimingStrategy):
18
+ """
19
+ Clean timing strategy - installs on usage + uninstalls after completion (LazyInstallMode.CLEAN).
20
+
21
+ Installs package when needed, then uninstalls after use to keep environment clean.
22
+ """
23
+
24
+ def should_install_now(self, package_name: str, context: Any) -> bool:
25
+ """
26
+ Determine if package should be installed now.
27
+
28
+ Clean mode: Install when first needed.
29
+
30
+ Args:
31
+ package_name: Package name to check
32
+ context: Context information (e.g., import error)
33
+
34
+ Returns:
35
+ True if should install now
36
+ """
37
+ # In clean mode, install when first needed
38
+ return context is not None
39
+
40
+ def should_uninstall_after(self, package_name: str, context: Any) -> bool:
41
+ """
42
+ Determine if package should be uninstalled after use.
43
+
44
+ Clean mode: Uninstall after use.
45
+
46
+ Args:
47
+ package_name: Package name to check
48
+ context: Context information
49
+
50
+ Returns:
51
+ True (uninstall after use)
52
+ """
53
+ return True
54
+
55
+ def get_install_priority(self, packages: List[str]) -> List[str]:
56
+ """
57
+ Get priority order for installing packages.
58
+
59
+ Clean mode: Install in order requested.
60
+
61
+ Args:
62
+ packages: List of package names
63
+
64
+ Returns:
65
+ Priority-ordered list
66
+ """
67
+ return packages
68
+
@@ -0,0 +1,67 @@
1
+ """
2
+ Full Timing Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Full timing - install all dependencies upfront.
11
+ """
12
+
13
+ from typing import List, Any
14
+ from ...package.base import AInstallTimingStrategy
15
+
16
+
17
+ class FullTiming(AInstallTimingStrategy):
18
+ """
19
+ Full timing strategy - installs all packages upfront (LazyInstallMode.FULL).
20
+
21
+ Batch installs all dependencies in parallel on initialization.
22
+ """
23
+
24
+ def should_install_now(self, package_name: str, context: Any) -> bool:
25
+ """
26
+ Determine if package should be installed now.
27
+
28
+ Full mode: Install all upfront.
29
+
30
+ Args:
31
+ package_name: Package name to check
32
+ context: Context information (ignored in full mode)
33
+
34
+ Returns:
35
+ True (always install upfront)
36
+ """
37
+ return True
38
+
39
+ def should_uninstall_after(self, package_name: str, context: Any) -> bool:
40
+ """
41
+ Determine if package should be uninstalled after use.
42
+
43
+ Full mode: Keep installed after use.
44
+
45
+ Args:
46
+ package_name: Package name to check
47
+ context: Context information
48
+
49
+ Returns:
50
+ False (keep installed)
51
+ """
52
+ return False
53
+
54
+ def get_install_priority(self, packages: List[str]) -> List[str]:
55
+ """
56
+ Get priority order for installing packages.
57
+
58
+ Full mode: Install all in parallel (no specific order).
59
+
60
+ Args:
61
+ packages: List of package names
62
+
63
+ Returns:
64
+ Priority-ordered list (original order)
65
+ """
66
+ return packages
67
+
@@ -0,0 +1,69 @@
1
+ """
2
+ Smart Timing Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Smart timing - install on first usage (on-demand).
11
+ """
12
+
13
+ from typing import List, Any
14
+ from ...package.base import AInstallTimingStrategy
15
+ from ...defs import LazyInstallMode
16
+
17
+
18
+ class SmartTiming(AInstallTimingStrategy):
19
+ """
20
+ Smart timing strategy - installs packages on-demand (LazyInstallMode.SMART).
21
+
22
+ Installs package when first needed, then caches the result.
23
+ """
24
+
25
+ def should_install_now(self, package_name: str, context: Any) -> bool:
26
+ """
27
+ Determine if package should be installed now.
28
+
29
+ Smart mode: Install when first needed.
30
+
31
+ Args:
32
+ package_name: Package name to check
33
+ context: Context information (e.g., import error)
34
+
35
+ Returns:
36
+ True if should install now
37
+ """
38
+ # In smart mode, install when first needed (context indicates need)
39
+ return context is not None
40
+
41
+ def should_uninstall_after(self, package_name: str, context: Any) -> bool:
42
+ """
43
+ Determine if package should be uninstalled after use.
44
+
45
+ Smart mode: Keep installed after use.
46
+
47
+ Args:
48
+ package_name: Package name to check
49
+ context: Context information
50
+
51
+ Returns:
52
+ False (keep installed)
53
+ """
54
+ return False
55
+
56
+ def get_install_priority(self, packages: List[str]) -> List[str]:
57
+ """
58
+ Get priority order for installing packages.
59
+
60
+ Smart mode: Install in order requested.
61
+
62
+ Args:
63
+ packages: List of package names
64
+
65
+ Returns:
66
+ Priority-ordered list
67
+ """
68
+ return packages
69
+
@@ -0,0 +1,67 @@
1
+ """
2
+ Temporary Timing Strategy
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Version: 0.1.0.19
8
+ Generation Date: 15-Nov-2025
9
+
10
+ Temporary timing - always uninstall after use (more aggressive than CLEAN).
11
+ """
12
+
13
+ from typing import List, Any
14
+ from ...package.base import AInstallTimingStrategy
15
+
16
+
17
+ class TemporaryTiming(AInstallTimingStrategy):
18
+ """
19
+ Temporary timing strategy - always uninstalls after use (LazyInstallMode.TEMPORARY).
20
+
21
+ More aggressive than CLEAN - always uninstalls packages after use.
22
+ """
23
+
24
+ def should_install_now(self, package_name: str, context: Any) -> bool:
25
+ """
26
+ Determine if package should be installed now.
27
+
28
+ Temporary mode: Install when first needed.
29
+
30
+ Args:
31
+ package_name: Package name to check
32
+ context: Context information (e.g., import error)
33
+
34
+ Returns:
35
+ True if should install now
36
+ """
37
+ return context is not None
38
+
39
+ def should_uninstall_after(self, package_name: str, context: Any) -> bool:
40
+ """
41
+ Determine if package should be uninstalled after use.
42
+
43
+ Temporary mode: Always uninstall after use.
44
+
45
+ Args:
46
+ package_name: Package name to check
47
+ context: Context information (ignored)
48
+
49
+ Returns:
50
+ True (always uninstall)
51
+ """
52
+ return True
53
+
54
+ def get_install_priority(self, packages: List[str]) -> List[str]:
55
+ """
56
+ Get priority order for installing packages.
57
+
58
+ Temporary mode: Install in order requested.
59
+
60
+ Args:
61
+ packages: List of package names
62
+
63
+ Returns:
64
+ Priority-ordered list
65
+ """
66
+ return packages
67
+
@@ -0,0 +1,18 @@
1
+ """
2
+ Runtime Services Module
3
+
4
+ This module provides concrete implementations for runtime services.
5
+ Main facade: XWRuntimeHelper extends ARuntimeHelper
6
+ """
7
+
8
+ # Lazy imports to avoid circular dependencies
9
+ def __getattr__(name: str):
10
+ if name == 'XWRuntimeHelper':
11
+ from .facade import XWRuntimeHelper
12
+ return XWRuntimeHelper
13
+ if name == 'XWRuntime': # Backward compatibility alias
14
+ from .facade import XWRuntimeHelper
15
+ return XWRuntimeHelper
16
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
17
+
18
+ __all__ = ['XWRuntimeHelper', 'XWRuntime'] # XWRuntime is backward compatibility alias
@@ -0,0 +1,131 @@
1
+ """
2
+ #exonware/xwlazy/src/exonware/xwlazy/loading/adaptive_utils.py
3
+
4
+ Adaptive learning utilities for pattern-based optimization.
5
+
6
+ Company: eXonware.com
7
+ Author: Eng. Muhammad AlShehri
8
+ Email: connect@exonware.com
9
+ Version: 0.1.0.19
10
+ Generation Date: 19-Nov-2025
11
+
12
+ This module provides adaptive learning for ADAPTIVE mode.
13
+ """
14
+
15
+ import time
16
+ import threading
17
+ from typing import Dict, List, Tuple, Optional
18
+ from collections import defaultdict, deque
19
+
20
+ # Logger not used in this module, removed to avoid circular dependency
21
+
22
+
23
+ class AdaptiveLearner:
24
+ """Learns import patterns and optimizes loading strategy."""
25
+
26
+ def __init__(self, learning_window: int = 100, prediction_depth: int = 3):
27
+ """
28
+ Initialize adaptive learner.
29
+
30
+ Args:
31
+ learning_window: Number of imports to track for learning
32
+ prediction_depth: Depth of sequence predictions
33
+ """
34
+ self._learning_window = learning_window
35
+ self._prediction_depth = prediction_depth
36
+ self._import_sequences: deque = deque(maxlen=learning_window)
37
+ self._access_times: Dict[str, List[float]] = defaultdict(list)
38
+ self._import_chains: Dict[str, Dict[str, int]] = defaultdict(lambda: defaultdict(int))
39
+ self._module_scores: Dict[str, float] = {}
40
+ self._lock = threading.RLock()
41
+
42
+ def record_import(self, module_name: str, import_time: float) -> None:
43
+ """Record an import event."""
44
+ current_time = time.time()
45
+
46
+ # Lock-free append to deque (thread-safe for appends)
47
+ self._import_sequences.append((module_name, current_time, import_time))
48
+
49
+ with self._lock:
50
+ self._access_times[module_name].append(current_time)
51
+
52
+ # Update import chains (what imports after what)
53
+ if len(self._import_sequences) > 1:
54
+ prev_name, _, _ = self._import_sequences[-2]
55
+ self._import_chains[prev_name][module_name] += 1
56
+
57
+ # Update module score (frequency * recency) - defer heavy computation
58
+ if len(self._access_times[module_name]) % 5 == 0: # Update every 5th access
59
+ self._update_module_score(module_name)
60
+
61
+ def _update_module_score(self, module_name: str) -> None:
62
+ """Update module priority score."""
63
+ with self._lock:
64
+ accesses = self._access_times[module_name]
65
+ if not accesses:
66
+ return
67
+
68
+ # Frequency component
69
+ recent_accesses = [t for t in accesses if time.time() - t < 3600] # Last hour
70
+ frequency = len(recent_accesses)
71
+
72
+ # Recency component (more recent = higher score)
73
+ if accesses:
74
+ last_access = accesses[-1]
75
+ recency = 1.0 / (time.time() - last_access + 1.0)
76
+ else:
77
+ recency = 0.0
78
+
79
+ # Chain component (if often imported after another module)
80
+ chain_weight = sum(self._import_chains.get(prev, {}).get(module_name, 0)
81
+ for prev in self._access_times.keys()) / max(len(self._import_sequences), 1)
82
+
83
+ # Combined score
84
+ self._module_scores[module_name] = frequency * 0.4 + recency * 1000 * 0.4 + chain_weight * 0.2
85
+
86
+ def predict_next_imports(self, current_module: Optional[str] = None, limit: int = 5) -> List[str]:
87
+ """Predict likely next imports based on patterns."""
88
+ # Lock-free check first
89
+ if not self._import_sequences:
90
+ return []
91
+
92
+ with self._lock:
93
+ candidates: Dict[str, float] = {}
94
+
95
+ # Predict based on current module chain (lock-free read of dict)
96
+ if current_module:
97
+ chain_candidates = self._import_chains.get(current_module, {})
98
+ for module, count in chain_candidates.items():
99
+ candidates[module] = candidates.get(module, 0.0) + count * 2.0
100
+
101
+ # Add high-scoring modules (lock-free read of dict)
102
+ for module, score in self._module_scores.items():
103
+ candidates[module] = candidates.get(module, 0.0) + score * 0.5
104
+
105
+ # Sort by score
106
+ sorted_candidates = sorted(candidates.items(), key=lambda x: x[1], reverse=True)
107
+ return [module for module, _ in sorted_candidates[:limit]]
108
+
109
+ def get_priority_modules(self, limit: int = 10) -> List[str]:
110
+ """Get modules that should be preloaded based on scores."""
111
+ with self._lock:
112
+ sorted_modules = sorted(
113
+ self._module_scores.items(),
114
+ key=lambda x: x[1],
115
+ reverse=True
116
+ )
117
+ return [module for module, _ in sorted_modules[:limit]]
118
+
119
+ def get_stats(self) -> Dict:
120
+ """Get learning statistics."""
121
+ with self._lock:
122
+ return {
123
+ 'sequences_tracked': len(self._import_sequences),
124
+ 'unique_modules': len(self._access_times),
125
+ 'chains_tracked': sum(len(chains) for chains in self._import_chains.values()),
126
+ 'top_modules': self.get_priority_modules(5),
127
+ }
128
+
129
+
130
+ __all__ = ['AdaptiveLearner']
131
+