exonware-xwlazy 0.1.0.1__py3-none-any.whl → 0.1.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/xwlazy/__init__.py +0 -0
- exonware/xwlazy/version.py +2 -2
- exonware_xwlazy-0.1.0.8.dist-info/METADATA +0 -0
- exonware_xwlazy-0.1.0.8.dist-info/RECORD +6 -0
- exonware/__init__.py +0 -42
- exonware/xwlazy/common/__init__.py +0 -55
- exonware/xwlazy/common/base.py +0 -65
- exonware/xwlazy/common/cache.py +0 -504
- exonware/xwlazy/common/logger.py +0 -257
- exonware/xwlazy/common/services/__init__.py +0 -72
- exonware/xwlazy/common/services/dependency_mapper.py +0 -250
- exonware/xwlazy/common/services/install_async_utils.py +0 -170
- exonware/xwlazy/common/services/install_cache_utils.py +0 -245
- exonware/xwlazy/common/services/keyword_detection.py +0 -283
- exonware/xwlazy/common/services/spec_cache.py +0 -165
- exonware/xwlazy/common/services/state_manager.py +0 -84
- exonware/xwlazy/common/strategies/__init__.py +0 -28
- exonware/xwlazy/common/strategies/caching_dict.py +0 -44
- exonware/xwlazy/common/strategies/caching_installation.py +0 -88
- exonware/xwlazy/common/strategies/caching_lfu.py +0 -66
- exonware/xwlazy/common/strategies/caching_lru.py +0 -63
- exonware/xwlazy/common/strategies/caching_multitier.py +0 -59
- exonware/xwlazy/common/strategies/caching_ttl.py +0 -59
- exonware/xwlazy/common/utils.py +0 -142
- exonware/xwlazy/config.py +0 -193
- exonware/xwlazy/contracts.py +0 -1533
- exonware/xwlazy/defs.py +0 -378
- exonware/xwlazy/errors.py +0 -276
- exonware/xwlazy/facade.py +0 -1137
- exonware/xwlazy/host/__init__.py +0 -8
- exonware/xwlazy/host/conf.py +0 -16
- exonware/xwlazy/module/__init__.py +0 -18
- exonware/xwlazy/module/base.py +0 -643
- exonware/xwlazy/module/data.py +0 -17
- exonware/xwlazy/module/facade.py +0 -246
- exonware/xwlazy/module/importer_engine.py +0 -2964
- exonware/xwlazy/module/partial_module_detector.py +0 -275
- exonware/xwlazy/module/strategies/__init__.py +0 -22
- exonware/xwlazy/module/strategies/module_helper_lazy.py +0 -93
- exonware/xwlazy/module/strategies/module_helper_simple.py +0 -65
- exonware/xwlazy/module/strategies/module_manager_advanced.py +0 -111
- exonware/xwlazy/module/strategies/module_manager_simple.py +0 -95
- exonware/xwlazy/package/__init__.py +0 -18
- exonware/xwlazy/package/base.py +0 -877
- exonware/xwlazy/package/conf.py +0 -324
- exonware/xwlazy/package/data.py +0 -17
- exonware/xwlazy/package/facade.py +0 -480
- exonware/xwlazy/package/services/__init__.py +0 -84
- exonware/xwlazy/package/services/async_install_handle.py +0 -87
- exonware/xwlazy/package/services/config_manager.py +0 -249
- exonware/xwlazy/package/services/discovery.py +0 -435
- exonware/xwlazy/package/services/host_packages.py +0 -180
- exonware/xwlazy/package/services/install_async.py +0 -291
- exonware/xwlazy/package/services/install_cache.py +0 -145
- exonware/xwlazy/package/services/install_interactive.py +0 -59
- exonware/xwlazy/package/services/install_policy.py +0 -156
- exonware/xwlazy/package/services/install_registry.py +0 -54
- exonware/xwlazy/package/services/install_result.py +0 -17
- exonware/xwlazy/package/services/install_sbom.py +0 -153
- exonware/xwlazy/package/services/install_utils.py +0 -79
- exonware/xwlazy/package/services/installer_engine.py +0 -406
- exonware/xwlazy/package/services/lazy_installer.py +0 -803
- exonware/xwlazy/package/services/manifest.py +0 -503
- exonware/xwlazy/package/services/strategy_registry.py +0 -324
- exonware/xwlazy/package/strategies/__init__.py +0 -57
- exonware/xwlazy/package/strategies/package_discovery_file.py +0 -129
- exonware/xwlazy/package/strategies/package_discovery_hybrid.py +0 -84
- exonware/xwlazy/package/strategies/package_discovery_manifest.py +0 -101
- exonware/xwlazy/package/strategies/package_execution_async.py +0 -113
- exonware/xwlazy/package/strategies/package_execution_cached.py +0 -90
- exonware/xwlazy/package/strategies/package_execution_pip.py +0 -99
- exonware/xwlazy/package/strategies/package_execution_wheel.py +0 -106
- exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +0 -100
- exonware/xwlazy/package/strategies/package_mapping_hybrid.py +0 -105
- exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +0 -100
- exonware/xwlazy/package/strategies/package_policy_allow_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_deny_list.py +0 -57
- exonware/xwlazy/package/strategies/package_policy_permissive.py +0 -46
- exonware/xwlazy/package/strategies/package_timing_clean.py +0 -67
- exonware/xwlazy/package/strategies/package_timing_full.py +0 -66
- exonware/xwlazy/package/strategies/package_timing_smart.py +0 -68
- exonware/xwlazy/package/strategies/package_timing_temporary.py +0 -66
- exonware/xwlazy/runtime/__init__.py +0 -18
- exonware/xwlazy/runtime/adaptive_learner.py +0 -129
- exonware/xwlazy/runtime/base.py +0 -274
- exonware/xwlazy/runtime/facade.py +0 -94
- exonware/xwlazy/runtime/intelligent_selector.py +0 -170
- exonware/xwlazy/runtime/metrics.py +0 -60
- exonware/xwlazy/runtime/performance.py +0 -37
- exonware_xwlazy-0.1.0.1.dist-info/METADATA +0 -454
- exonware_xwlazy-0.1.0.1.dist-info/RECORD +0 -93
- xwlazy/__init__.py +0 -14
- xwlazy/lazy.py +0 -30
- {exonware_xwlazy-0.1.0.1.dist-info → exonware_xwlazy-0.1.0.8.dist-info}/WHEEL +0 -0
- {exonware_xwlazy-0.1.0.1.dist-info → exonware_xwlazy-0.1.0.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
#exonware/xwlazy/src/exonware/xwlazy/discovery/spec_cache.py
|
|
3
|
-
|
|
4
|
-
Spec cache utilities for module specification caching.
|
|
5
|
-
|
|
6
|
-
Company: eXonware.com
|
|
7
|
-
Author: Eng. Muhammad AlShehri
|
|
8
|
-
Email: connect@exonware.com
|
|
9
|
-
|
|
10
|
-
Generation Date: 10-Oct-2025
|
|
11
|
-
|
|
12
|
-
This module provides multi-level caching (L1: memory, L2: disk) for module specs
|
|
13
|
-
to optimize import performance.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
import os
|
|
17
|
-
import sys
|
|
18
|
-
import time
|
|
19
|
-
import threading
|
|
20
|
-
import importlib
|
|
21
|
-
import importlib.machinery
|
|
22
|
-
import importlib.util
|
|
23
|
-
from pathlib import Path
|
|
24
|
-
from typing import Optional
|
|
25
|
-
from collections import OrderedDict
|
|
26
|
-
from functools import lru_cache
|
|
27
|
-
|
|
28
|
-
# Environment variables
|
|
29
|
-
_SPEC_CACHE_MAX = int(os.environ.get("XWLAZY_SPEC_CACHE_MAX", "512") or 512)
|
|
30
|
-
_SPEC_CACHE_TTL = float(os.environ.get("XWLAZY_SPEC_CACHE_TTL", "60") or 60.0)
|
|
31
|
-
|
|
32
|
-
# Cache storage
|
|
33
|
-
_spec_cache_lock = threading.RLock()
|
|
34
|
-
_spec_cache: OrderedDict[str, tuple[importlib.machinery.ModuleSpec, float]] = OrderedDict()
|
|
35
|
-
|
|
36
|
-
# Multi-level cache: L1 (in-memory) + L2 (disk)
|
|
37
|
-
_CACHE_L2_DIR = Path(
|
|
38
|
-
os.environ.get(
|
|
39
|
-
"XWLAZY_CACHE_DIR",
|
|
40
|
-
os.path.join(os.path.expanduser("~"), ".xwlazy", "cache"),
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
_CACHE_L2_DIR.mkdir(parents=True, exist_ok=True)
|
|
44
|
-
|
|
45
|
-
# Stdlib module set
|
|
46
|
-
try:
|
|
47
|
-
_STDLIB_MODULE_SET: set[str] = set(sys.stdlib_module_names) # type: ignore[attr-defined]
|
|
48
|
-
except AttributeError:
|
|
49
|
-
_STDLIB_MODULE_SET = set()
|
|
50
|
-
_STDLIB_MODULE_SET.update(sys.builtin_module_names)
|
|
51
|
-
|
|
52
|
-
@lru_cache(maxsize=1024)
|
|
53
|
-
def _cached_stdlib_check(module_name: str) -> bool:
|
|
54
|
-
"""Check if module is part of stdlib or built-in modules."""
|
|
55
|
-
try:
|
|
56
|
-
spec = importlib.util.find_spec(module_name)
|
|
57
|
-
if spec is None:
|
|
58
|
-
return False
|
|
59
|
-
if spec.origin in ("built-in", None):
|
|
60
|
-
return True
|
|
61
|
-
origin = spec.origin or ""
|
|
62
|
-
return (
|
|
63
|
-
"python" in origin.lower()
|
|
64
|
-
and "site-packages" not in origin.lower()
|
|
65
|
-
and "dist-packages" not in origin.lower()
|
|
66
|
-
)
|
|
67
|
-
except Exception:
|
|
68
|
-
return False
|
|
69
|
-
|
|
70
|
-
def _spec_cache_prune_locked(now: Optional[float] = None) -> None:
|
|
71
|
-
"""Prune expired entries from spec cache (must be called with lock held)."""
|
|
72
|
-
if not _spec_cache:
|
|
73
|
-
return
|
|
74
|
-
current = now or time.monotonic()
|
|
75
|
-
while _spec_cache:
|
|
76
|
-
fullname, (_, ts) = next(iter(_spec_cache.items()))
|
|
77
|
-
if current - ts <= _SPEC_CACHE_TTL and len(_spec_cache) <= _SPEC_CACHE_MAX:
|
|
78
|
-
break
|
|
79
|
-
_spec_cache.popitem(last=False)
|
|
80
|
-
|
|
81
|
-
def _spec_cache_get(fullname: str) -> Optional[importlib.machinery.ModuleSpec]:
|
|
82
|
-
"""Get spec from multi-level cache (L1: memory, L2: disk)."""
|
|
83
|
-
with _spec_cache_lock:
|
|
84
|
-
_spec_cache_prune_locked()
|
|
85
|
-
entry = _spec_cache.get(fullname)
|
|
86
|
-
if entry is not None:
|
|
87
|
-
spec, _ = entry
|
|
88
|
-
_spec_cache.move_to_end(fullname)
|
|
89
|
-
return spec
|
|
90
|
-
|
|
91
|
-
# L2 cache: Check disk cache
|
|
92
|
-
try:
|
|
93
|
-
cache_file = _CACHE_L2_DIR / f"{fullname.replace('.', '_')}.spec"
|
|
94
|
-
if cache_file.exists():
|
|
95
|
-
mtime = cache_file.stat().st_mtime
|
|
96
|
-
age = time.time() - mtime
|
|
97
|
-
if age < _SPEC_CACHE_TTL:
|
|
98
|
-
try:
|
|
99
|
-
import pickle
|
|
100
|
-
with open(cache_file, 'rb') as f:
|
|
101
|
-
spec = pickle.load(f)
|
|
102
|
-
# Promote to L1 cache
|
|
103
|
-
_spec_cache[fullname] = (spec, time.monotonic())
|
|
104
|
-
_spec_cache.move_to_end(fullname)
|
|
105
|
-
return spec
|
|
106
|
-
except Exception:
|
|
107
|
-
pass
|
|
108
|
-
except Exception:
|
|
109
|
-
pass
|
|
110
|
-
|
|
111
|
-
return None
|
|
112
|
-
|
|
113
|
-
def _spec_cache_put(fullname: str, spec: Optional[importlib.machinery.ModuleSpec]) -> None:
|
|
114
|
-
"""Put spec in multi-level cache (L1: memory, L2: disk)."""
|
|
115
|
-
if spec is None:
|
|
116
|
-
return
|
|
117
|
-
with _spec_cache_lock:
|
|
118
|
-
# L1 cache: In-memory
|
|
119
|
-
_spec_cache[fullname] = (spec, time.monotonic())
|
|
120
|
-
_spec_cache.move_to_end(fullname)
|
|
121
|
-
_spec_cache_prune_locked()
|
|
122
|
-
|
|
123
|
-
# L2 cache: Disk (async, non-blocking)
|
|
124
|
-
try:
|
|
125
|
-
cache_file = _CACHE_L2_DIR / f"{fullname.replace('.', '_')}.spec"
|
|
126
|
-
import pickle
|
|
127
|
-
# Use protocol 5 for better performance
|
|
128
|
-
with open(cache_file, 'wb') as f:
|
|
129
|
-
pickle.dump(spec, f, protocol=5)
|
|
130
|
-
except Exception:
|
|
131
|
-
pass # Fail silently for disk cache
|
|
132
|
-
|
|
133
|
-
def _spec_cache_clear(fullname: Optional[str] = None) -> None:
|
|
134
|
-
"""Clear spec cache entries."""
|
|
135
|
-
with _spec_cache_lock:
|
|
136
|
-
if fullname is None:
|
|
137
|
-
_spec_cache.clear()
|
|
138
|
-
else:
|
|
139
|
-
_spec_cache.pop(fullname, None)
|
|
140
|
-
|
|
141
|
-
def _cache_spec_if_missing(fullname: str) -> None:
|
|
142
|
-
"""Ensure a ModuleSpec is cached for a known-good module."""
|
|
143
|
-
if _spec_cache_get(fullname):
|
|
144
|
-
return
|
|
145
|
-
try:
|
|
146
|
-
spec = importlib.util.find_spec(fullname)
|
|
147
|
-
except Exception:
|
|
148
|
-
spec = None
|
|
149
|
-
if spec is not None:
|
|
150
|
-
_spec_cache_put(fullname, spec)
|
|
151
|
-
|
|
152
|
-
def get_stdlib_module_set() -> set[str]:
|
|
153
|
-
"""Get the set of stdlib module names."""
|
|
154
|
-
return _STDLIB_MODULE_SET.copy()
|
|
155
|
-
|
|
156
|
-
__all__ = [
|
|
157
|
-
'_cached_stdlib_check',
|
|
158
|
-
'_spec_cache_get',
|
|
159
|
-
'_spec_cache_put',
|
|
160
|
-
'_spec_cache_clear',
|
|
161
|
-
'_spec_cache_prune_locked',
|
|
162
|
-
'_cache_spec_if_missing',
|
|
163
|
-
'get_stdlib_module_set',
|
|
164
|
-
]
|
|
165
|
-
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import os
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
def _get_base_config_dir() -> Path:
|
|
9
|
-
"""Determine a cross-platform directory for storing lazy configuration."""
|
|
10
|
-
if os.name == "nt":
|
|
11
|
-
appdata = os.getenv("APPDATA")
|
|
12
|
-
if appdata:
|
|
13
|
-
return Path(appdata) / "exonware" / "lazy"
|
|
14
|
-
return Path.home() / "AppData" / "Roaming" / "exonware" / "lazy"
|
|
15
|
-
|
|
16
|
-
# POSIX-style
|
|
17
|
-
xdg_config = os.getenv("XDG_CONFIG_HOME")
|
|
18
|
-
if xdg_config:
|
|
19
|
-
return Path(xdg_config) / "exonware" / "lazy"
|
|
20
|
-
return Path.home() / ".config" / "exonware" / "lazy"
|
|
21
|
-
|
|
22
|
-
class LazyStateManager:
|
|
23
|
-
"""Persist and retrieve lazy installation state."""
|
|
24
|
-
|
|
25
|
-
def __init__(self, package_name: str) -> None:
|
|
26
|
-
self._package = package_name.lower()
|
|
27
|
-
self._state_path = _get_base_config_dir() / "state.json"
|
|
28
|
-
self._state: dict[str, dict[str, bool]] = self._load_state()
|
|
29
|
-
|
|
30
|
-
# --------------------------------------------------------------------- #
|
|
31
|
-
# Persistence helpers
|
|
32
|
-
# --------------------------------------------------------------------- #
|
|
33
|
-
def _load_state(self) -> dict[str, dict[str, bool]]:
|
|
34
|
-
if not self._state_path.exists():
|
|
35
|
-
return {}
|
|
36
|
-
try:
|
|
37
|
-
with self._state_path.open("r", encoding="utf-8") as fh:
|
|
38
|
-
data = json.load(fh)
|
|
39
|
-
if isinstance(data, dict):
|
|
40
|
-
return data
|
|
41
|
-
except Exception:
|
|
42
|
-
pass
|
|
43
|
-
return {}
|
|
44
|
-
|
|
45
|
-
def _save_state(self) -> None:
|
|
46
|
-
self._state_path.parent.mkdir(parents=True, exist_ok=True)
|
|
47
|
-
with self._state_path.open("w", encoding="utf-8") as fh:
|
|
48
|
-
json.dump(self._state, fh, indent=2, sort_keys=True)
|
|
49
|
-
|
|
50
|
-
def _ensure_entry(self) -> dict[str, bool]:
|
|
51
|
-
return self._state.setdefault(self._package, {})
|
|
52
|
-
|
|
53
|
-
# --------------------------------------------------------------------- #
|
|
54
|
-
# Manual state management
|
|
55
|
-
# --------------------------------------------------------------------- #
|
|
56
|
-
def get_manual_state(self) -> Optional[bool]:
|
|
57
|
-
entry = self._state.get(self._package, {})
|
|
58
|
-
value = entry.get("manual")
|
|
59
|
-
return bool(value) if isinstance(value, bool) else None
|
|
60
|
-
|
|
61
|
-
def set_manual_state(self, value: Optional[bool]) -> None:
|
|
62
|
-
entry = self._ensure_entry()
|
|
63
|
-
if value is None:
|
|
64
|
-
entry.pop("manual", None)
|
|
65
|
-
else:
|
|
66
|
-
entry["manual"] = bool(value)
|
|
67
|
-
self._save_state()
|
|
68
|
-
|
|
69
|
-
# --------------------------------------------------------------------- #
|
|
70
|
-
# Auto detection cache
|
|
71
|
-
# --------------------------------------------------------------------- #
|
|
72
|
-
def get_cached_auto_state(self) -> Optional[bool]:
|
|
73
|
-
entry = self._state.get(self._package, {})
|
|
74
|
-
value = entry.get("auto")
|
|
75
|
-
return bool(value) if isinstance(value, bool) else None
|
|
76
|
-
|
|
77
|
-
def set_auto_state(self, value: Optional[bool]) -> None:
|
|
78
|
-
entry = self._ensure_entry()
|
|
79
|
-
if value is None:
|
|
80
|
-
entry.pop("auto", None)
|
|
81
|
-
else:
|
|
82
|
-
entry["auto"] = bool(value)
|
|
83
|
-
self._save_state()
|
|
84
|
-
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Common Caching Strategies - Shared by modules and packages.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Generic caching strategies that work with ANY data type.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from .caching_dict import DictCache
|
|
14
|
-
from .caching_lru import LRUCache
|
|
15
|
-
from .caching_lfu import LFUCache
|
|
16
|
-
from .caching_ttl import TTLCache
|
|
17
|
-
from .caching_multitier import MultiTierCacheStrategy
|
|
18
|
-
from .caching_installation import InstallationCacheWrapper
|
|
19
|
-
|
|
20
|
-
__all__ = [
|
|
21
|
-
'DictCache',
|
|
22
|
-
'LRUCache',
|
|
23
|
-
'LFUCache',
|
|
24
|
-
'TTLCache',
|
|
25
|
-
'MultiTierCacheStrategy',
|
|
26
|
-
'InstallationCacheWrapper',
|
|
27
|
-
]
|
|
28
|
-
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Dict Cache Strategy - Simple dictionary-based caching.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Simple dict-based cache implementation.
|
|
11
|
-
Works with ANY data type (modules, packages, etc.).
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from ...common.base import ACachingStrategy
|
|
16
|
-
|
|
17
|
-
class DictCache(ACachingStrategy):
|
|
18
|
-
"""
|
|
19
|
-
Simple dictionary-based cache.
|
|
20
|
-
|
|
21
|
-
No eviction policy - grows unbounded.
|
|
22
|
-
Use for small applications or when memory is not a concern.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def __init__(self):
|
|
26
|
-
"""Initialize dict cache."""
|
|
27
|
-
self._cache: dict[str, Any] = {}
|
|
28
|
-
|
|
29
|
-
def get(self, key: str) -> Optional[Any]:
|
|
30
|
-
"""Get value from cache."""
|
|
31
|
-
return self._cache.get(key)
|
|
32
|
-
|
|
33
|
-
def set(self, key: str, value: Any) -> None:
|
|
34
|
-
"""Set value in cache."""
|
|
35
|
-
self._cache[key] = value
|
|
36
|
-
|
|
37
|
-
def invalidate(self, key: str) -> None:
|
|
38
|
-
"""Invalidate cached value."""
|
|
39
|
-
self._cache.pop(key, None)
|
|
40
|
-
|
|
41
|
-
def clear(self) -> None:
|
|
42
|
-
"""Clear all cached values."""
|
|
43
|
-
self._cache.clear()
|
|
44
|
-
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Installation Cache Strategy - Wrapper for existing InstallationCache.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Wraps existing InstallationCache to implement ICachingStrategy interface.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from typing import Optional, Any
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
from ..cache import InstallationCache
|
|
16
|
-
from ..base import ACachingStrategy
|
|
17
|
-
from ...package.data import PackageData
|
|
18
|
-
|
|
19
|
-
class InstallationCacheWrapper(ACachingStrategy):
|
|
20
|
-
"""
|
|
21
|
-
Installation cache strategy wrapper.
|
|
22
|
-
|
|
23
|
-
Wraps existing InstallationCache to implement ICachingStrategy interface.
|
|
24
|
-
Used for package installation status caching.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, cache_file: Optional[Path] = None):
|
|
28
|
-
"""
|
|
29
|
-
Initialize installation cache wrapper.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
cache_file: Optional path to cache file
|
|
33
|
-
"""
|
|
34
|
-
self._cache = InstallationCache(cache_file)
|
|
35
|
-
|
|
36
|
-
def get(self, key: str) -> Optional[Any]:
|
|
37
|
-
"""
|
|
38
|
-
Get package from cache.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
key: Package name
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
PackageData if found, None otherwise
|
|
45
|
-
"""
|
|
46
|
-
if self._cache.is_installed(key):
|
|
47
|
-
version = self._cache.get_version(key)
|
|
48
|
-
return PackageData(
|
|
49
|
-
name=key,
|
|
50
|
-
installed=True,
|
|
51
|
-
version=version
|
|
52
|
-
)
|
|
53
|
-
return None
|
|
54
|
-
|
|
55
|
-
def set(self, key: str, value: Any) -> None:
|
|
56
|
-
"""
|
|
57
|
-
Cache a package.
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
key: Package name
|
|
61
|
-
value: PackageData or dict with installed/version info
|
|
62
|
-
"""
|
|
63
|
-
if isinstance(value, PackageData):
|
|
64
|
-
if value.installed:
|
|
65
|
-
self._cache.mark_installed(key, value.version)
|
|
66
|
-
else:
|
|
67
|
-
self._cache.mark_uninstalled(key)
|
|
68
|
-
elif isinstance(value, dict):
|
|
69
|
-
if value.get('installed', False):
|
|
70
|
-
self._cache.mark_installed(key, value.get('version'))
|
|
71
|
-
else:
|
|
72
|
-
self._cache.mark_uninstalled(key)
|
|
73
|
-
|
|
74
|
-
def invalidate(self, key: str) -> None:
|
|
75
|
-
"""
|
|
76
|
-
Invalidate cached package.
|
|
77
|
-
|
|
78
|
-
Args:
|
|
79
|
-
key: Package name
|
|
80
|
-
"""
|
|
81
|
-
self._cache.mark_uninstalled(key)
|
|
82
|
-
|
|
83
|
-
def clear(self) -> None:
|
|
84
|
-
"""Clear all cached packages."""
|
|
85
|
-
# InstallationCache doesn't have clear, so we'd need to extend it
|
|
86
|
-
# For now, just mark all as uninstalled (would need cache iteration)
|
|
87
|
-
pass
|
|
88
|
-
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
LFU Cache Strategy - Least Frequently Used eviction.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
LFU cache implementation with size limit.
|
|
11
|
-
Works with ANY data type (modules, packages, etc.).
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from collections import Counter
|
|
16
|
-
from ...common.base import ACachingStrategy
|
|
17
|
-
|
|
18
|
-
class LFUCache(ACachingStrategy):
|
|
19
|
-
"""
|
|
20
|
-
LFU (Least Frequently Used) cache with size limit.
|
|
21
|
-
|
|
22
|
-
Evicts least frequently accessed items when cache is full.
|
|
23
|
-
Good for access pattern-based caching.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self, max_size: int = 1000):
|
|
27
|
-
"""
|
|
28
|
-
Initialize LFU cache.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
max_size: Maximum number of items in cache
|
|
32
|
-
"""
|
|
33
|
-
self._cache: dict[str, Any] = {}
|
|
34
|
-
self._freq: Counter[str] = Counter()
|
|
35
|
-
self._max_size = max_size
|
|
36
|
-
|
|
37
|
-
def get(self, key: str) -> Optional[Any]:
|
|
38
|
-
"""Get value from cache (increments frequency)."""
|
|
39
|
-
if key in self._cache:
|
|
40
|
-
self._freq[key] += 1
|
|
41
|
-
return self._cache[key]
|
|
42
|
-
return None
|
|
43
|
-
|
|
44
|
-
def set(self, key: str, value: Any) -> None:
|
|
45
|
-
"""Set value in cache (evicts least frequent if full)."""
|
|
46
|
-
if key not in self._cache and len(self._cache) >= self._max_size:
|
|
47
|
-
# Evict least frequent
|
|
48
|
-
if self._freq:
|
|
49
|
-
least_frequent = min(self._freq.items(), key=lambda x: x[1])[0]
|
|
50
|
-
self._cache.pop(least_frequent, None)
|
|
51
|
-
self._freq.pop(least_frequent, None)
|
|
52
|
-
|
|
53
|
-
self._cache[key] = value
|
|
54
|
-
if key not in self._freq:
|
|
55
|
-
self._freq[key] = 0
|
|
56
|
-
|
|
57
|
-
def invalidate(self, key: str) -> None:
|
|
58
|
-
"""Invalidate cached value."""
|
|
59
|
-
self._cache.pop(key, None)
|
|
60
|
-
self._freq.pop(key, None)
|
|
61
|
-
|
|
62
|
-
def clear(self) -> None:
|
|
63
|
-
"""Clear all cached values."""
|
|
64
|
-
self._cache.clear()
|
|
65
|
-
self._freq.clear()
|
|
66
|
-
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
LRU Cache Strategy - Least Recently Used eviction.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
LRU cache implementation with size limit.
|
|
11
|
-
Works with ANY data type (modules, packages, etc.).
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from collections import OrderedDict
|
|
16
|
-
from ...common.base import ACachingStrategy
|
|
17
|
-
|
|
18
|
-
class LRUCache(ACachingStrategy):
|
|
19
|
-
"""
|
|
20
|
-
LRU (Least Recently Used) cache with size limit.
|
|
21
|
-
|
|
22
|
-
Evicts least recently used items when cache is full.
|
|
23
|
-
Good for general-purpose caching.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self, max_size: int = 1000):
|
|
27
|
-
"""
|
|
28
|
-
Initialize LRU cache.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
max_size: Maximum number of items in cache
|
|
32
|
-
"""
|
|
33
|
-
self._cache: OrderedDict[str, Any] = OrderedDict()
|
|
34
|
-
self._max_size = max_size
|
|
35
|
-
|
|
36
|
-
def get(self, key: str) -> Optional[Any]:
|
|
37
|
-
"""Get value from cache (moves to end for LRU)."""
|
|
38
|
-
if key in self._cache:
|
|
39
|
-
# Move to end (most recently used)
|
|
40
|
-
self._cache.move_to_end(key)
|
|
41
|
-
return self._cache[key]
|
|
42
|
-
return None
|
|
43
|
-
|
|
44
|
-
def set(self, key: str, value: Any) -> None:
|
|
45
|
-
"""Set value in cache (evicts oldest if full)."""
|
|
46
|
-
if key in self._cache:
|
|
47
|
-
# Update existing - move to end
|
|
48
|
-
self._cache.move_to_end(key)
|
|
49
|
-
else:
|
|
50
|
-
# New entry - check size limit
|
|
51
|
-
if len(self._cache) >= self._max_size:
|
|
52
|
-
# Remove oldest (first item)
|
|
53
|
-
self._cache.popitem(last=False)
|
|
54
|
-
self._cache[key] = value
|
|
55
|
-
|
|
56
|
-
def invalidate(self, key: str) -> None:
|
|
57
|
-
"""Invalidate cached value."""
|
|
58
|
-
self._cache.pop(key, None)
|
|
59
|
-
|
|
60
|
-
def clear(self) -> None:
|
|
61
|
-
"""Clear all cached values."""
|
|
62
|
-
self._cache.clear()
|
|
63
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Multi-Tier Cache Strategy - Wrapper for existing MultiTierCache.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
Wraps existing MultiTierCache to implement ICaching interface.
|
|
11
|
-
Works with ANY data type (modules, packages, etc.).
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from typing import Optional, Any
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from ...common.cache import MultiTierCache
|
|
17
|
-
from ...common.base import ACachingStrategy
|
|
18
|
-
|
|
19
|
-
class MultiTierCacheStrategy(ACachingStrategy):
|
|
20
|
-
"""
|
|
21
|
-
Multi-tier cache strategy (L1 memory + L2 disk + L3 predictive).
|
|
22
|
-
|
|
23
|
-
Wraps existing MultiTierCache to implement ICachingStrategy interface.
|
|
24
|
-
High-performance caching with multiple tiers.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, l1_size: int = 1000, l2_dir: Optional[Path] = None, enable_l3: bool = True):
|
|
28
|
-
"""
|
|
29
|
-
Initialize multi-tier cache.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
l1_size: Maximum size of L1 (memory) cache
|
|
33
|
-
l2_dir: Directory for L2 (disk) cache
|
|
34
|
-
enable_l3: Enable L3 (predictive) cache
|
|
35
|
-
"""
|
|
36
|
-
self._cache = MultiTierCache(l1_size=l1_size, l2_dir=l2_dir, enable_l3=enable_l3)
|
|
37
|
-
|
|
38
|
-
def get(self, key: str) -> Optional[Any]:
|
|
39
|
-
"""Get value from cache (L1 -> L2 -> L3)."""
|
|
40
|
-
return self._cache.get(key)
|
|
41
|
-
|
|
42
|
-
def set(self, key: str, value: Any) -> None:
|
|
43
|
-
"""Set value in cache (L1 + L2 batched)."""
|
|
44
|
-
self._cache.set(key, value)
|
|
45
|
-
|
|
46
|
-
def invalidate(self, key: str) -> None:
|
|
47
|
-
"""Invalidate cached value."""
|
|
48
|
-
# MultiTierCache doesn't have invalidate, so we set to None
|
|
49
|
-
# Or we could extend MultiTierCache to add invalidate
|
|
50
|
-
self._cache.set(key, None)
|
|
51
|
-
|
|
52
|
-
def clear(self) -> None:
|
|
53
|
-
"""Clear all cached values."""
|
|
54
|
-
self._cache.clear()
|
|
55
|
-
|
|
56
|
-
def shutdown(self) -> None:
|
|
57
|
-
"""Shutdown cache (flush L2, cleanup threads)."""
|
|
58
|
-
self._cache.shutdown()
|
|
59
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TTL Cache Strategy - Time-To-Live expiration.
|
|
3
|
-
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
|
|
8
|
-
Generation Date: 15-Nov-2025
|
|
9
|
-
|
|
10
|
-
TTL cache implementation with expiration.
|
|
11
|
-
Works with ANY data type (modules, packages, etc.).
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import time
|
|
15
|
-
from typing import Optional, Any
|
|
16
|
-
from ...common.base import ACachingStrategy
|
|
17
|
-
|
|
18
|
-
class TTLCache(ACachingStrategy):
|
|
19
|
-
"""
|
|
20
|
-
TTL (Time-To-Live) cache with expiration.
|
|
21
|
-
|
|
22
|
-
Automatically expires entries after TTL seconds.
|
|
23
|
-
Good for time-sensitive data.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self, ttl_seconds: float = 3600.0):
|
|
27
|
-
"""
|
|
28
|
-
Initialize TTL cache.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
ttl_seconds: Time-to-live in seconds (default: 1 hour)
|
|
32
|
-
"""
|
|
33
|
-
self._cache: dict[str, tuple[Any, float]] = {} # (value, expiry_time)
|
|
34
|
-
self._ttl = ttl_seconds
|
|
35
|
-
|
|
36
|
-
def get(self, key: str) -> Optional[Any]:
|
|
37
|
-
"""Get value from cache (returns None if expired)."""
|
|
38
|
-
if key in self._cache:
|
|
39
|
-
value, expiry = self._cache[key]
|
|
40
|
-
if time.time() < expiry:
|
|
41
|
-
return value
|
|
42
|
-
else:
|
|
43
|
-
# Expired - remove
|
|
44
|
-
del self._cache[key]
|
|
45
|
-
return None
|
|
46
|
-
|
|
47
|
-
def set(self, key: str, value: Any) -> None:
|
|
48
|
-
"""Set value in cache with TTL."""
|
|
49
|
-
expiry = time.time() + self._ttl
|
|
50
|
-
self._cache[key] = (value, expiry)
|
|
51
|
-
|
|
52
|
-
def invalidate(self, key: str) -> None:
|
|
53
|
-
"""Invalidate cached value."""
|
|
54
|
-
self._cache.pop(key, None)
|
|
55
|
-
|
|
56
|
-
def clear(self) -> None:
|
|
57
|
-
"""Clear all cached values."""
|
|
58
|
-
self._cache.clear()
|
|
59
|
-
|