ttl-cache-decorator 0.1.2__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.
Potentially problematic release.
This version of ttl-cache-decorator might be problematic. Click here for more details.
ttl_cache/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._ttl_lru import ttl_lru_cache, TTLCachedFunction # noqa
|
ttl_cache/_ttl_lru.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import random
|
|
3
|
+
import time
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from functools import lru_cache, wraps
|
|
6
|
+
from typing import Any, Protocol, cast
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TTLCachedFunction[**P, R](Protocol):
|
|
10
|
+
"""Same signature as the wrapped function, plus ``lru_cache``'s helpers."""
|
|
11
|
+
|
|
12
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: ...
|
|
13
|
+
def cache_info(self) -> functools._CacheInfo: ...
|
|
14
|
+
def cache_clear(self) -> None: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ttl_lru_cache[**P, R](
|
|
18
|
+
ttl: float = 60,
|
|
19
|
+
jitter: float = 0,
|
|
20
|
+
maxsize: int | None = 128,
|
|
21
|
+
typed: bool = False,
|
|
22
|
+
) -> Callable[[Callable[P, R]], TTLCachedFunction[P, R]]:
|
|
23
|
+
"""A decorator that works like ``lru_cache`` but with a time to live (ttl).
|
|
24
|
+
|
|
25
|
+
Under the hood it still uses ``lru_cache``, but the cached call receives an
|
|
26
|
+
extra positional-only ``time_bucket`` argument::
|
|
27
|
+
|
|
28
|
+
time_bucket = int((time.monotonic() + offset) // ttl)
|
|
29
|
+
|
|
30
|
+
An entry stays "alive" while the bucket value is unchanged and expires when
|
|
31
|
+
the bucket rolls over. ``offset`` is drawn once per decorated function from
|
|
32
|
+
``random.uniform(0, jitter)``, so different functions (or processes) expire
|
|
33
|
+
at different moments instead of all at once.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
ttl: Time to live in seconds. Defaults to 60.
|
|
37
|
+
jitter: Max random offset in seconds applied to the bucket boundaries.
|
|
38
|
+
Defaults to 0.
|
|
39
|
+
maxsize: Max size of the cache, or ``None`` for unbounded. Defaults to 128.
|
|
40
|
+
typed: Whether argument types are part of the cache key. Defaults to False.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A decorator that wraps a function in a TTL-aware LRU cache. The wrapped
|
|
44
|
+
function keeps its signature and exposes ``cache_info`` and
|
|
45
|
+
``cache_clear``.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ValueError: If ``ttl`` is not positive or ``jitter`` is negative.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> @ttl_lru_cache(ttl=30, jitter=5)
|
|
52
|
+
... def fetch(key: str) -> str:
|
|
53
|
+
... return key.upper()
|
|
54
|
+
"""
|
|
55
|
+
if ttl <= 0:
|
|
56
|
+
raise ValueError(f"ttl must be positive, got {ttl!r}")
|
|
57
|
+
if jitter < 0:
|
|
58
|
+
raise ValueError(f"jitter must be non-negative, got {jitter!r}")
|
|
59
|
+
|
|
60
|
+
def decorator(func: Callable[P, R]) -> TTLCachedFunction[P, R]:
|
|
61
|
+
offset = random.uniform(0, jitter)
|
|
62
|
+
|
|
63
|
+
@lru_cache(maxsize=maxsize, typed=typed)
|
|
64
|
+
def cached_with_time(_time_bucket: int, /, *args: Any, **kwargs: Any) -> R:
|
|
65
|
+
return func(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
# lru_cache types its arguments as Hashable, which P.args/P.kwargs can't
|
|
68
|
+
# be proven to be. Unhashable arguments still raise TypeError at call
|
|
69
|
+
# time, exactly as with plain lru_cache.
|
|
70
|
+
call_cached = cast(Callable[..., R], cached_with_time)
|
|
71
|
+
|
|
72
|
+
@wraps(func)
|
|
73
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
74
|
+
time_bucket = int((time.monotonic() + offset) // ttl)
|
|
75
|
+
return call_cached(time_bucket, *args, **kwargs)
|
|
76
|
+
|
|
77
|
+
wrapper.__dict__.update(
|
|
78
|
+
cache_info=cached_with_time.cache_info,
|
|
79
|
+
cache_clear=cached_with_time.cache_clear,
|
|
80
|
+
)
|
|
81
|
+
return cast(TTLCachedFunction[P, R], wrapper)
|
|
82
|
+
|
|
83
|
+
return decorator
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ttl-cache-decorator
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: LRU Cache Decorator with a Time to Live
|
|
5
|
+
Project-URL: Changelog, https://github.com/Johandielangman/ttl-cache-decorator/blob/main/CHANGELOG.md
|
|
6
|
+
Project-URL: Documentation, https://johandielangman.github.io/ttl-cache-decorator/
|
|
7
|
+
Project-URL: Issues, https://github.com/Johandielangman/ttl-cache-decorator/issues
|
|
8
|
+
Project-URL: Source, https://github.com/Johandielangman/ttl-cache-decorator
|
|
9
|
+
Author-email: Johandielangman <johan@datatreehouse.org>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# TTL Cache
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
29
|
+
[](https://pypi.org/project/ttl-cache-decorator)
|
|
30
|
+
|
|
31
|
+
An LRU cache decorator with a time to live — like `functools.lru_cache`, but entries expire after a configurable number of seconds.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```console
|
|
36
|
+
pip install ttl-cache-decorator
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ttl_cache import ttl_lru_cache
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@ttl_lru_cache(ttl=30)
|
|
46
|
+
def fetch_user(user_id: int) -> dict:
|
|
47
|
+
return {"id": user_id, "name": "Ada"}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
fetch_user(1) # Calls the function
|
|
51
|
+
fetch_user(1) # Cache hit
|
|
52
|
+
# ... 30 seconds later ...
|
|
53
|
+
fetch_user(1) # Expired — calls the function again
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
`ttl-cache-decorator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
ttl_cache/__init__.py,sha256=2oTIuMAlfWiXoSNXjXX6n0tVkjjj8ewKR0YOhCYcYFI,63
|
|
2
|
+
ttl_cache/_ttl_lru.py,sha256=3_aSt8sfby6TeDB67816W4i7Al9HtMzpQRrYi4hjXHs,3121
|
|
3
|
+
ttl_cache_decorator-0.1.2.dist-info/METADATA,sha256=mdxWNUiKY4MhPSnxwYISgzPIIAnAxubMTu5ibXZHtps,2103
|
|
4
|
+
ttl_cache_decorator-0.1.2.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
5
|
+
ttl_cache_decorator-0.1.2.dist-info/licenses/LICENSE.txt,sha256=9XrdCLKFXDpgek58eydUna_ucPUJuR75euMJp0_6f0I,1103
|
|
6
|
+
ttl_cache_decorator-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Johandielangman <jghanekom2@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|