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.
- flux_cache-0.1.0/LICENSE +21 -0
- flux_cache-0.1.0/PKG-INFO +123 -0
- flux_cache-0.1.0/README.md +104 -0
- flux_cache-0.1.0/flux_cache/__init__.py +1 -0
- flux_cache-0.1.0/flux_cache/backends/__init__.py +2 -0
- flux_cache-0.1.0/flux_cache/backends/base.py +25 -0
- flux_cache-0.1.0/flux_cache/backends/memory.py +41 -0
- flux_cache-0.1.0/flux_cache/backends/redis.py +62 -0
- flux_cache-0.1.0/flux_cache/core.py +82 -0
- flux_cache-0.1.0/flux_cache/utils.py +7 -0
- flux_cache-0.1.0/flux_cache.egg-info/PKG-INFO +123 -0
- flux_cache-0.1.0/flux_cache.egg-info/SOURCES.txt +15 -0
- flux_cache-0.1.0/flux_cache.egg-info/dependency_links.txt +1 -0
- flux_cache-0.1.0/flux_cache.egg-info/requires.txt +3 -0
- flux_cache-0.1.0/flux_cache.egg-info/top_level.txt +1 -0
- flux_cache-0.1.0/pyproject.toml +31 -0
- flux_cache-0.1.0/setup.cfg +4 -0
flux_cache-0.1.0/LICENSE
ADDED
|
@@ -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,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,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 @@
|
|
|
1
|
+
|
|
@@ -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"
|