flux-cache 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alvin Shaita
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: flux-cache
3
+ Version: 0.1.0
4
+ Summary: Caching library for Python with sync and async support
5
+ Author-email: Alvin Shaita <alvinshaita@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/alvinshaita/flux-cache
8
+ Project-URL: Source, https://github.com/alvinshaita/flux-cache
9
+ Keywords: cache,caching,async,performance
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: redis
17
+ Requires-Dist: redis>=5.0.0; extra == "redis"
18
+ Dynamic: license-file
19
+
20
+ # Flux Cache
21
+
22
+ A lightweight, extensible caching library for Python with support for synchronous and asynchronous functions.
23
+
24
+
25
+ ## Quick Start
26
+
27
+ ### Basic Usage
28
+
29
+ ```python
30
+ from flux_cache import cache
31
+
32
+ @cache(ttl=60)
33
+ def expensive_function(x, y):
34
+ print("Computing...")
35
+ return x + y
36
+
37
+ expensive_function(1, 2) # computes
38
+ expensive_function(1, 2) # cached
39
+ ```
40
+
41
+ ---
42
+
43
+ ### Async Usage
44
+
45
+ ```python
46
+ from flux_cache import async_cache
47
+
48
+ @async_cache(ttl=60)
49
+ async def fetch_data(x):
50
+ return x * 2
51
+ ```
52
+
53
+ ---
54
+
55
+ ## How It Works
56
+
57
+ * A cache key is generated using:
58
+
59
+ * Function module
60
+ * Function name
61
+ * Arguments (args + kwargs)
62
+ * The key is hashed using SHA-256
63
+ * Values are stored in the backend along with optional expiration timestamps
64
+
65
+ ---
66
+
67
+ ## Cache Control
68
+
69
+ ### Clear entire cache
70
+
71
+ ```python
72
+ expensive_function.clear()
73
+ ```
74
+
75
+ ### Invalidate specific call
76
+
77
+ ```python
78
+ expensive_function.invalidate(1, 2)
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Backends
84
+
85
+ ### Default: In-Memory
86
+
87
+ Thread-safe, simple dictionary-based backend.
88
+
89
+ ```python
90
+ from flux_cache.backends import MemoryBackend
91
+
92
+ @cache(backend=MemoryBackend())
93
+ def foo():
94
+ return "bar"
95
+ ```
96
+
97
+ ### Custom Backend
98
+
99
+ Implement the `BaseBackend` interface:
100
+
101
+ ```python
102
+ class BaseBackend:
103
+ def get(self, key): ...
104
+ def set(self, key, value, ttl=None): ...
105
+ def delete(self, key): ...
106
+ def clear(self): ...
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 🧪 Testing
112
+
113
+ Run tests using:
114
+
115
+ ```bash
116
+ pytest
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 📄 License
122
+
123
+ MIT License
@@ -0,0 +1,104 @@
1
+ # Flux Cache
2
+
3
+ A lightweight, extensible caching library for Python with support for synchronous and asynchronous functions.
4
+
5
+
6
+ ## Quick Start
7
+
8
+ ### Basic Usage
9
+
10
+ ```python
11
+ from flux_cache import cache
12
+
13
+ @cache(ttl=60)
14
+ def expensive_function(x, y):
15
+ print("Computing...")
16
+ return x + y
17
+
18
+ expensive_function(1, 2) # computes
19
+ expensive_function(1, 2) # cached
20
+ ```
21
+
22
+ ---
23
+
24
+ ### Async Usage
25
+
26
+ ```python
27
+ from flux_cache import async_cache
28
+
29
+ @async_cache(ttl=60)
30
+ async def fetch_data(x):
31
+ return x * 2
32
+ ```
33
+
34
+ ---
35
+
36
+ ## How It Works
37
+
38
+ * A cache key is generated using:
39
+
40
+ * Function module
41
+ * Function name
42
+ * Arguments (args + kwargs)
43
+ * The key is hashed using SHA-256
44
+ * Values are stored in the backend along with optional expiration timestamps
45
+
46
+ ---
47
+
48
+ ## Cache Control
49
+
50
+ ### Clear entire cache
51
+
52
+ ```python
53
+ expensive_function.clear()
54
+ ```
55
+
56
+ ### Invalidate specific call
57
+
58
+ ```python
59
+ expensive_function.invalidate(1, 2)
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Backends
65
+
66
+ ### Default: In-Memory
67
+
68
+ Thread-safe, simple dictionary-based backend.
69
+
70
+ ```python
71
+ from flux_cache.backends import MemoryBackend
72
+
73
+ @cache(backend=MemoryBackend())
74
+ def foo():
75
+ return "bar"
76
+ ```
77
+
78
+ ### Custom Backend
79
+
80
+ Implement the `BaseBackend` interface:
81
+
82
+ ```python
83
+ class BaseBackend:
84
+ def get(self, key): ...
85
+ def set(self, key, value, ttl=None): ...
86
+ def delete(self, key): ...
87
+ def clear(self): ...
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 🧪 Testing
93
+
94
+ Run tests using:
95
+
96
+ ```bash
97
+ pytest
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 📄 License
103
+
104
+ MIT License
@@ -0,0 +1 @@
1
+ from .core import async_cache, cache
@@ -0,0 +1,2 @@
1
+ from .memory import MemoryBackend
2
+ from .redis import RedisBackend
@@ -0,0 +1,25 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Optional
3
+
4
+
5
+ class BaseBackend(ABC):
6
+
7
+ @abstractmethod
8
+ def has(self, key: str) -> bool:
9
+ pass
10
+
11
+ @abstractmethod
12
+ def get(self, key: str) -> Optional[Any]:
13
+ pass
14
+
15
+ @abstractmethod
16
+ def set(self, key: str, value: Any) -> None:
17
+ pass
18
+
19
+ @abstractmethod
20
+ def delete(self, key: str) -> None:
21
+ pass
22
+
23
+ @abstractmethod
24
+ def clear(self) -> None:
25
+ pass
@@ -0,0 +1,41 @@
1
+ import threading
2
+ import time
3
+ from typing import Any, Optional
4
+
5
+ from .base import BaseBackend
6
+
7
+
8
+ class MemoryBackend(BaseBackend):
9
+ def __init__(self):
10
+ self.store = {}
11
+ self._lock = threading.RLock()
12
+
13
+ def has(self, key: str) -> bool:
14
+ with self._lock:
15
+ return key in self.store
16
+
17
+ def get(self, key: str) -> Optional[Any]:
18
+ with self._lock:
19
+ item = self.store.get(key)
20
+ if not item:
21
+ return None
22
+
23
+ value, expires_at = item
24
+ if expires_at and expires_at < time.time():
25
+ self.store.pop(key, None)
26
+ return None
27
+
28
+ return value, expires_at
29
+
30
+ def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
31
+ with self._lock:
32
+ expires_at = time.time() + ttl if ttl else None
33
+ self.store[key] = (value, expires_at)
34
+
35
+ def delete(self, key: str) -> None:
36
+ with self._lock:
37
+ self.store.pop(key, None)
38
+
39
+ def clear(self) -> None:
40
+ with self._lock:
41
+ self.store.clear()
@@ -0,0 +1,62 @@
1
+ import json
2
+ import threading
3
+ import time
4
+ from typing import Any, Optional
5
+
6
+ from .base import BaseBackend
7
+
8
+
9
+ class RedisBackend(BaseBackend):
10
+ def __init__(self, url="redis://localhost:6379/0", prefix="flux-cache"):
11
+ try:
12
+ import redis
13
+ except ImportError:
14
+ raise ImportError(
15
+ "RedisBackend requires 'redis' package.\n"
16
+ "Install with: pip install flux-cache[redis]"
17
+ )
18
+ self.prefix = prefix
19
+ self.red = redis.Redis.from_url(url)
20
+
21
+ def _serialize(self, value: Any) -> bytes:
22
+ return json.dumps(value)
23
+
24
+ def _deserialize(self, value: Optional[bytes]) -> Any:
25
+ if value is None:
26
+ return None
27
+
28
+ try:
29
+ return json.loads(value)
30
+ except:
31
+ return value
32
+
33
+ def _key(self, key: str) -> str:
34
+ return f"{self.prefix}:{key}"
35
+
36
+ def has(self, key: str) -> bool:
37
+ namespaced_key = self._key(key)
38
+ present = self.red.exists(namespaced_key)
39
+ return True if present == 1 else False
40
+
41
+ def get(self, key: str) -> Optional[Any]:
42
+ namespaced_key = self._key(key)
43
+ value = self.red.get(namespaced_key)
44
+ if value is None:
45
+ return None
46
+
47
+ deserialized_value = self._deserialize(value)
48
+ return deserialized_value, None
49
+
50
+ def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
51
+ serialized_value = self._serialize(value)
52
+ namespaced_key = self._key(key)
53
+ self.red.set(namespaced_key, serialized_value, ex=ttl)
54
+
55
+ def delete(self, key: str) -> None:
56
+ namespaced_key = self._key(key)
57
+ self.red.delete(namespaced_key)
58
+
59
+ def clear(self) -> None:
60
+ keys = self.red.keys(f"{self.prefix}*")
61
+ if keys:
62
+ self.red.delete(*keys)
@@ -0,0 +1,82 @@
1
+ import functools
2
+ from typing import Callable, Optional
3
+
4
+ from .backends import MemoryBackend
5
+ from .utils import generate_cache_key
6
+
7
+ def cache(
8
+ func: Optional[Callable] = None,
9
+ *,
10
+ ttl: Optional[int] = None,
11
+ backend = None
12
+ ):
13
+ if backend is None:
14
+ backend = MemoryBackend()
15
+
16
+ if func is None:
17
+ return lambda f: cache(f, ttl=ttl, backend=backend)
18
+
19
+ @functools.wraps(func)
20
+ def wrapper(*args, **kwargs):
21
+ key = generate_cache_key(func, args, kwargs)
22
+
23
+ item = backend.get(key)
24
+ if item:
25
+ value, _ = item
26
+ return value
27
+
28
+ result = func(*args, **kwargs)
29
+ backend.set(key, result, ttl=ttl)
30
+
31
+ return result
32
+
33
+ def clear():
34
+ backend.clear()
35
+
36
+ def invalidate(*args, **kwargs):
37
+ key = generate_cache_key(func, args, kwargs)
38
+ backend.delete(key)
39
+
40
+ wrapper.clear = clear
41
+ wrapper.invalidate = invalidate
42
+
43
+ return wrapper
44
+
45
+
46
+ def async_cache(
47
+ func: Optional[Callable] = None,
48
+ *,
49
+ ttl: Optional[int] = None,
50
+ backend = None
51
+ ):
52
+ if backend is None:
53
+ backend = MemoryBackend()
54
+
55
+ if func is None:
56
+ return lambda f: async_cache(f, ttl=ttl, backend=backend)
57
+
58
+ @functools.wraps(func)
59
+ async def wrapper(*args, **kwargs):
60
+ key = generate_cache_key(func, args, kwargs)
61
+
62
+ item = backend.get(key)
63
+ if item:
64
+ value, _ = item
65
+ return value
66
+
67
+ result = await func(*args, **kwargs)
68
+ backend.set(key, result, ttl=ttl)
69
+
70
+ return result
71
+
72
+ def clear():
73
+ backend.clear()
74
+
75
+ def invalidate(*args, **kwargs):
76
+ key = generate_cache_key(func, args, kwargs)
77
+ backend.delete(key)
78
+
79
+ wrapper.clear = clear
80
+ wrapper.invalidate = invalidate
81
+
82
+ return wrapper
@@ -0,0 +1,7 @@
1
+ import hashlib
2
+ import pickle
3
+
4
+ def generate_cache_key(func, args, kwargs) -> str:
5
+ key_data = (func.__module__, func.__name__, args, kwargs)
6
+ raw_data = pickle.dumps(key_data)
7
+ return hashlib.sha256(raw_data).hexdigest()
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: flux-cache
3
+ Version: 0.1.0
4
+ Summary: Caching library for Python with sync and async support
5
+ Author-email: Alvin Shaita <alvinshaita@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/alvinshaita/flux-cache
8
+ Project-URL: Source, https://github.com/alvinshaita/flux-cache
9
+ Keywords: cache,caching,async,performance
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: redis
17
+ Requires-Dist: redis>=5.0.0; extra == "redis"
18
+ Dynamic: license-file
19
+
20
+ # Flux Cache
21
+
22
+ A lightweight, extensible caching library for Python with support for synchronous and asynchronous functions.
23
+
24
+
25
+ ## Quick Start
26
+
27
+ ### Basic Usage
28
+
29
+ ```python
30
+ from flux_cache import cache
31
+
32
+ @cache(ttl=60)
33
+ def expensive_function(x, y):
34
+ print("Computing...")
35
+ return x + y
36
+
37
+ expensive_function(1, 2) # computes
38
+ expensive_function(1, 2) # cached
39
+ ```
40
+
41
+ ---
42
+
43
+ ### Async Usage
44
+
45
+ ```python
46
+ from flux_cache import async_cache
47
+
48
+ @async_cache(ttl=60)
49
+ async def fetch_data(x):
50
+ return x * 2
51
+ ```
52
+
53
+ ---
54
+
55
+ ## How It Works
56
+
57
+ * A cache key is generated using:
58
+
59
+ * Function module
60
+ * Function name
61
+ * Arguments (args + kwargs)
62
+ * The key is hashed using SHA-256
63
+ * Values are stored in the backend along with optional expiration timestamps
64
+
65
+ ---
66
+
67
+ ## Cache Control
68
+
69
+ ### Clear entire cache
70
+
71
+ ```python
72
+ expensive_function.clear()
73
+ ```
74
+
75
+ ### Invalidate specific call
76
+
77
+ ```python
78
+ expensive_function.invalidate(1, 2)
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Backends
84
+
85
+ ### Default: In-Memory
86
+
87
+ Thread-safe, simple dictionary-based backend.
88
+
89
+ ```python
90
+ from flux_cache.backends import MemoryBackend
91
+
92
+ @cache(backend=MemoryBackend())
93
+ def foo():
94
+ return "bar"
95
+ ```
96
+
97
+ ### Custom Backend
98
+
99
+ Implement the `BaseBackend` interface:
100
+
101
+ ```python
102
+ class BaseBackend:
103
+ def get(self, key): ...
104
+ def set(self, key, value, ttl=None): ...
105
+ def delete(self, key): ...
106
+ def clear(self): ...
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 🧪 Testing
112
+
113
+ Run tests using:
114
+
115
+ ```bash
116
+ pytest
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 📄 License
122
+
123
+ MIT License
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ flux_cache/__init__.py
5
+ flux_cache/core.py
6
+ flux_cache/utils.py
7
+ flux_cache.egg-info/PKG-INFO
8
+ flux_cache.egg-info/SOURCES.txt
9
+ flux_cache.egg-info/dependency_links.txt
10
+ flux_cache.egg-info/requires.txt
11
+ flux_cache.egg-info/top_level.txt
12
+ flux_cache/backends/__init__.py
13
+ flux_cache/backends/base.py
14
+ flux_cache/backends/memory.py
15
+ flux_cache/backends/redis.py
@@ -0,0 +1,3 @@
1
+
2
+ [redis]
3
+ redis>=5.0.0
@@ -0,0 +1 @@
1
+ flux_cache
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "flux-cache"
7
+ version = "0.1.0"
8
+ description = "Caching library for Python with sync and async support"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name="Alvin Shaita", email="alvinshaita@gmail.com" }
12
+ ]
13
+ license = { text = "MIT" }
14
+ requires-python = ">=3.8"
15
+
16
+ dependencies = []
17
+
18
+ keywords = ["cache", "caching", "async", "performance"]
19
+
20
+ classifiers = [
21
+ "Programming Language :: Python :: 3",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ redis = ["redis>=5.0.0"]
28
+
29
+ [project.urls]
30
+ "Homepage" = "https://github.com/alvinshaita/flux-cache"
31
+ "Source" = "https://github.com/alvinshaita/flux-cache"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+