cachetools 7.1.2__tar.gz → 7.1.4__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.
- {cachetools-7.1.2 → cachetools-7.1.4}/CHANGELOG.rst +16 -0
- cachetools-7.1.4/MANIFEST.in +3 -0
- {cachetools-7.1.2/src/cachetools.egg-info → cachetools-7.1.4}/PKG-INFO +1 -1
- {cachetools-7.1.2 → cachetools-7.1.4}/pyproject.toml +4 -4
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/__init__.py +1 -4
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/func.pyi +20 -19
- {cachetools-7.1.2 → cachetools-7.1.4/src/cachetools.egg-info}/PKG-INFO +1 -1
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/SOURCES.txt +1 -0
- cachetools-7.1.4/tests/test_threading.py +126 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tox.ini +5 -7
- cachetools-7.1.2/tests/test_threading.py +0 -62
- {cachetools-7.1.2 → cachetools-7.1.4}/LICENSE +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/README.rst +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/docs/conf.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/docs/index.rst +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/setup.cfg +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/__init__.pyi +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/_cached.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/_cachedmethod.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/func.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/keys.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/keys.pyi +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/py.typed +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/__init__.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cache.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cached.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cachedmethod.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_classmethod.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_fifo.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_func.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_keys.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_lfu.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_lru.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_rr.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_tlru.py +0 -0
- {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_ttl.py +0 -0
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
v7.1.4 (2026-05-22)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
- Minor unit test improvements.
|
|
5
|
+
|
|
6
|
+
- Update build environment.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
v7.1.3 (2026-05-18)
|
|
10
|
+
===================
|
|
11
|
+
|
|
12
|
+
- Minor type stub improvements.
|
|
13
|
+
|
|
14
|
+
- Update build environment.
|
|
15
|
+
|
|
16
|
+
|
|
1
17
|
v7.1.2 (2026-05-16)
|
|
2
18
|
===================
|
|
3
19
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools >=
|
|
2
|
+
requires = ["setuptools >= 80", "setuptools-scm >= 8.2"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -49,6 +49,6 @@ version = {attr = "cachetools.__version__"}
|
|
|
49
49
|
|
|
50
50
|
[tool.pyright]
|
|
51
51
|
typeCheckingMode = "standard"
|
|
52
|
-
reportFunctionMemberAccess =
|
|
53
|
-
reportOptionalContextManager =
|
|
54
|
-
reportOptionalMemberAccess =
|
|
52
|
+
reportFunctionMemberAccess = "information"
|
|
53
|
+
reportOptionalContextManager = "warning"
|
|
54
|
+
reportOptionalMemberAccess = "warning"
|
|
@@ -12,7 +12,7 @@ __all__ = (
|
|
|
12
12
|
"cachedmethod",
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
__version__ = "7.1.
|
|
15
|
+
__version__ = "7.1.4"
|
|
16
16
|
|
|
17
17
|
import collections
|
|
18
18
|
import collections.abc
|
|
@@ -23,9 +23,6 @@ import time
|
|
|
23
23
|
|
|
24
24
|
from . import keys
|
|
25
25
|
|
|
26
|
-
# Typing stubs for this package are provided by typeshed:
|
|
27
|
-
# https://github.com/python/typeshed/tree/main/stubs/cachetools
|
|
28
|
-
|
|
29
26
|
|
|
30
27
|
class _DefaultSize:
|
|
31
28
|
"""A minimal "fake" dict that returns a constant size 1 for any key."""
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
from collections.abc import Callable, Sequence
|
|
2
|
-
from typing import Any, Final, Generic, TypeVar, overload, type_check_only
|
|
2
|
+
from typing import Any, Final, Generic, ParamSpec, TypeVar, overload, type_check_only
|
|
3
3
|
|
|
4
4
|
from . import _CacheInfo
|
|
5
5
|
|
|
6
6
|
__all__: Final = ("fifo_cache", "lfu_cache", "lru_cache", "rr_cache", "ttl_cache")
|
|
7
7
|
|
|
8
|
+
_P = ParamSpec("_P")
|
|
8
9
|
_T = TypeVar("_T")
|
|
9
10
|
_R = TypeVar("_R")
|
|
10
11
|
|
|
11
12
|
@type_check_only
|
|
12
|
-
class _cachetools_cache_wrapper(Generic[_R]):
|
|
13
|
-
__wrapped__: Callable[
|
|
13
|
+
class _cachetools_cache_wrapper(Generic[_P, _R]):
|
|
14
|
+
__wrapped__: Callable[_P, _R]
|
|
14
15
|
__name__: str
|
|
15
16
|
__doc__: str | None
|
|
16
|
-
def __call__(self, /, *args:
|
|
17
|
+
def __call__(self, /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...
|
|
17
18
|
def cache_info(self) -> _CacheInfo: ...
|
|
18
19
|
def cache_clear(self) -> None: ...
|
|
19
20
|
def cache_parameters(self) -> dict[str, Any]: ...
|
|
@@ -21,50 +22,50 @@ class _cachetools_cache_wrapper(Generic[_R]):
|
|
|
21
22
|
@overload
|
|
22
23
|
def fifo_cache(
|
|
23
24
|
maxsize: int | None = 128, typed: bool = False
|
|
24
|
-
) -> Callable[[Callable[
|
|
25
|
+
) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
|
|
25
26
|
@overload
|
|
26
27
|
def fifo_cache(
|
|
27
|
-
maxsize: Callable[
|
|
28
|
-
) -> _cachetools_cache_wrapper[_R]: ...
|
|
28
|
+
maxsize: Callable[_P, _R], typed: bool = False
|
|
29
|
+
) -> _cachetools_cache_wrapper[_P, _R]: ...
|
|
29
30
|
@overload
|
|
30
31
|
def lfu_cache(
|
|
31
32
|
maxsize: int | None = 128, typed: bool = False
|
|
32
|
-
) -> Callable[[Callable[
|
|
33
|
+
) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
|
|
33
34
|
@overload
|
|
34
35
|
def lfu_cache(
|
|
35
|
-
maxsize: Callable[
|
|
36
|
-
) -> _cachetools_cache_wrapper[_R]: ...
|
|
36
|
+
maxsize: Callable[_P, _R], typed: bool = False
|
|
37
|
+
) -> _cachetools_cache_wrapper[_P, _R]: ...
|
|
37
38
|
@overload
|
|
38
39
|
def lru_cache(
|
|
39
40
|
maxsize: int | None = 128, typed: bool = False
|
|
40
|
-
) -> Callable[[Callable[
|
|
41
|
+
) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
|
|
41
42
|
@overload
|
|
42
43
|
def lru_cache(
|
|
43
|
-
maxsize: Callable[
|
|
44
|
-
) -> _cachetools_cache_wrapper[_R]: ...
|
|
44
|
+
maxsize: Callable[_P, _R], typed: bool = False
|
|
45
|
+
) -> _cachetools_cache_wrapper[_P, _R]: ...
|
|
45
46
|
@overload
|
|
46
47
|
def rr_cache(
|
|
47
48
|
maxsize: int | None = 128,
|
|
48
49
|
choice: Callable[[Sequence[_T]], _T] = ...,
|
|
49
50
|
typed: bool = False,
|
|
50
|
-
) -> Callable[[Callable[
|
|
51
|
+
) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
|
|
51
52
|
@overload
|
|
52
53
|
def rr_cache(
|
|
53
|
-
maxsize: Callable[
|
|
54
|
+
maxsize: Callable[_P, _R],
|
|
54
55
|
choice: Callable[[Sequence[_T]], _T] = ...,
|
|
55
56
|
typed: bool = False,
|
|
56
|
-
) -> _cachetools_cache_wrapper[_R]: ...
|
|
57
|
+
) -> _cachetools_cache_wrapper[_P, _R]: ...
|
|
57
58
|
@overload
|
|
58
59
|
def ttl_cache(
|
|
59
60
|
maxsize: int | None = 128,
|
|
60
61
|
ttl: Any = 600,
|
|
61
62
|
timer: Callable[[], _T] = ...,
|
|
62
63
|
typed: bool = False,
|
|
63
|
-
) -> Callable[[Callable[
|
|
64
|
+
) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
|
|
64
65
|
@overload
|
|
65
66
|
def ttl_cache(
|
|
66
|
-
maxsize: Callable[
|
|
67
|
+
maxsize: Callable[_P, _R],
|
|
67
68
|
ttl: Any = 600,
|
|
68
69
|
timer: Callable[[], _T] = ...,
|
|
69
70
|
typed: bool = False,
|
|
70
|
-
) -> _cachetools_cache_wrapper[_R]: ...
|
|
71
|
+
) -> _cachetools_cache_wrapper[_P, _R]: ...
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import unittest
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from cachetools import LRUCache, cached, cachedmethod
|
|
7
|
+
|
|
8
|
+
count = 0
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@cached(cache=LRUCache(1), condition=threading.Condition(), info=True)
|
|
12
|
+
def func():
|
|
13
|
+
global count
|
|
14
|
+
time.sleep(1.0)
|
|
15
|
+
count += 1
|
|
16
|
+
return count
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@cached(cache=LRUCache(1), lock=threading.Lock(), info=True)
|
|
20
|
+
def locked_func():
|
|
21
|
+
global count
|
|
22
|
+
time.sleep(1.0)
|
|
23
|
+
count += 1
|
|
24
|
+
return 42
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ThreadingTest(unittest.TestCase):
|
|
28
|
+
NTHREADS = 10
|
|
29
|
+
|
|
30
|
+
TIMEOUT = 10
|
|
31
|
+
|
|
32
|
+
cache: LRUCache[Any, int] = LRUCache(1)
|
|
33
|
+
|
|
34
|
+
cond = threading.Condition()
|
|
35
|
+
|
|
36
|
+
lock = threading.Lock()
|
|
37
|
+
|
|
38
|
+
count = 0
|
|
39
|
+
|
|
40
|
+
@cachedmethod(
|
|
41
|
+
cache=lambda self: self.cache, condition=lambda self: self.cond, info=True
|
|
42
|
+
)
|
|
43
|
+
def method(self):
|
|
44
|
+
time.sleep(1.0)
|
|
45
|
+
self.count += 1
|
|
46
|
+
return 42
|
|
47
|
+
|
|
48
|
+
@cachedmethod(cache=lambda self: self.cache, lock=lambda self: self.lock, info=True)
|
|
49
|
+
def locked_method(self):
|
|
50
|
+
time.sleep(1.0)
|
|
51
|
+
self.count += 1
|
|
52
|
+
return 42
|
|
53
|
+
|
|
54
|
+
def test_cached_stampede(self):
|
|
55
|
+
global count
|
|
56
|
+
count = 0
|
|
57
|
+
threads = [threading.Thread(target=func) for i in range(0, self.NTHREADS)]
|
|
58
|
+
for t in threads:
|
|
59
|
+
t.start()
|
|
60
|
+
for t in threads:
|
|
61
|
+
t.join(timeout=self.TIMEOUT)
|
|
62
|
+
self.assertFalse(t.is_alive())
|
|
63
|
+
|
|
64
|
+
self.assertEqual(count, 1)
|
|
65
|
+
|
|
66
|
+
info = func.cache_info()
|
|
67
|
+
self.assertEqual(info.hits, self.NTHREADS - 1)
|
|
68
|
+
self.assertEqual(info.misses, 1)
|
|
69
|
+
|
|
70
|
+
def test_cachedmethod_stampede(self):
|
|
71
|
+
self.cache = LRUCache(1)
|
|
72
|
+
self.count = 0
|
|
73
|
+
threads = [
|
|
74
|
+
threading.Thread(target=self.method) for i in range(0, self.NTHREADS)
|
|
75
|
+
]
|
|
76
|
+
for t in threads:
|
|
77
|
+
t.start()
|
|
78
|
+
for t in threads:
|
|
79
|
+
t.join(timeout=self.TIMEOUT)
|
|
80
|
+
self.assertFalse(t.is_alive())
|
|
81
|
+
|
|
82
|
+
self.assertEqual(self.count, 1)
|
|
83
|
+
|
|
84
|
+
info = self.method.cache_info()
|
|
85
|
+
self.assertEqual(info.hits, self.NTHREADS - 1)
|
|
86
|
+
self.assertEqual(info.misses, 1)
|
|
87
|
+
|
|
88
|
+
def test_cached_locked(self):
|
|
89
|
+
global count
|
|
90
|
+
count = 0
|
|
91
|
+
threads = [
|
|
92
|
+
threading.Thread(target=locked_func) for i in range(0, self.NTHREADS)
|
|
93
|
+
]
|
|
94
|
+
for t in threads:
|
|
95
|
+
t.start()
|
|
96
|
+
for t in threads:
|
|
97
|
+
t.join(timeout=self.TIMEOUT)
|
|
98
|
+
self.assertFalse(t.is_alive())
|
|
99
|
+
|
|
100
|
+
# Without condition/stampede prevention, func is called by multiple
|
|
101
|
+
# threads, but all return the same cached value (42).
|
|
102
|
+
self.assertGreaterEqual(count, 1)
|
|
103
|
+
|
|
104
|
+
info = locked_func.cache_info()
|
|
105
|
+
self.assertEqual(info.hits + info.misses, self.NTHREADS)
|
|
106
|
+
self.assertGreaterEqual(info.misses, 1)
|
|
107
|
+
|
|
108
|
+
def test_cachedmethod_locked(self):
|
|
109
|
+
self.cache = LRUCache(1)
|
|
110
|
+
self.count = 0
|
|
111
|
+
threads = [
|
|
112
|
+
threading.Thread(target=self.locked_method) for i in range(0, self.NTHREADS)
|
|
113
|
+
]
|
|
114
|
+
for t in threads:
|
|
115
|
+
t.start()
|
|
116
|
+
for t in threads:
|
|
117
|
+
t.join(timeout=self.TIMEOUT)
|
|
118
|
+
self.assertFalse(t.is_alive())
|
|
119
|
+
|
|
120
|
+
# Multiple threads may compute the value, but setdefault ensures
|
|
121
|
+
# the first cached result wins and all return 42.
|
|
122
|
+
self.assertGreaterEqual(self.count, 1)
|
|
123
|
+
|
|
124
|
+
info = self.locked_method.cache_info()
|
|
125
|
+
self.assertEqual(info.hits + info.misses, self.NTHREADS)
|
|
126
|
+
self.assertGreaterEqual(info.misses, 1)
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
[tox]
|
|
2
|
-
envlist = docs,doctest,format,
|
|
2
|
+
envlist = py,docs,doctest,ruff,ruff-format,pyright
|
|
3
3
|
|
|
4
4
|
[testenv]
|
|
5
5
|
deps =
|
|
6
6
|
pytest
|
|
7
7
|
pytest-cov
|
|
8
|
-
setenv =
|
|
9
|
-
THREADING_TESTS = 1
|
|
10
8
|
commands =
|
|
11
9
|
py.test --basetemp={envtmpdir} --cov=cachetools --cov-report term-missing {posargs}
|
|
12
10
|
|
|
@@ -22,18 +20,18 @@ deps =
|
|
|
22
20
|
commands =
|
|
23
21
|
sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs {envtmpdir}/doctest
|
|
24
22
|
|
|
25
|
-
[testenv:
|
|
23
|
+
[testenv:ruff]
|
|
26
24
|
deps =
|
|
27
25
|
ruff
|
|
28
26
|
commands =
|
|
29
|
-
ruff
|
|
27
|
+
ruff check {posargs}
|
|
30
28
|
skip_install = true
|
|
31
29
|
|
|
32
|
-
[testenv:
|
|
30
|
+
[testenv:ruff-format]
|
|
33
31
|
deps =
|
|
34
32
|
ruff
|
|
35
33
|
commands =
|
|
36
|
-
ruff
|
|
34
|
+
ruff format --diff -q {posargs}
|
|
37
35
|
skip_install = true
|
|
38
36
|
|
|
39
37
|
[testenv:pyright]
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import threading
|
|
2
|
-
import time
|
|
3
|
-
import unittest
|
|
4
|
-
from os import environ
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from cachetools import LRUCache, cached, cachedmethod
|
|
8
|
-
|
|
9
|
-
count = 0
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@cached(cache=LRUCache(1), condition=threading.Condition(), info=True)
|
|
13
|
-
def func():
|
|
14
|
-
global count
|
|
15
|
-
time.sleep(1.0)
|
|
16
|
-
count += 1
|
|
17
|
-
return count
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@unittest.skipUnless(environ.get("THREADING_TESTS", False), "THREADING_TESTS not set")
|
|
21
|
-
class ThreadingTest(unittest.TestCase):
|
|
22
|
-
NTHREADS = 10
|
|
23
|
-
|
|
24
|
-
cache: LRUCache[Any, int] = LRUCache(1)
|
|
25
|
-
|
|
26
|
-
cond = threading.Condition()
|
|
27
|
-
|
|
28
|
-
count = 0
|
|
29
|
-
|
|
30
|
-
@cachedmethod(
|
|
31
|
-
cache=lambda self: self.cache, condition=lambda self: self.cond, info=True
|
|
32
|
-
)
|
|
33
|
-
def meth(self):
|
|
34
|
-
time.sleep(1.0)
|
|
35
|
-
self.count += 1
|
|
36
|
-
return 42
|
|
37
|
-
|
|
38
|
-
def test_cached_stampede(self):
|
|
39
|
-
threads = [threading.Thread(target=func) for i in range(0, self.NTHREADS)]
|
|
40
|
-
for t in threads:
|
|
41
|
-
t.start()
|
|
42
|
-
for t in threads:
|
|
43
|
-
t.join()
|
|
44
|
-
|
|
45
|
-
self.assertEqual(count, 1)
|
|
46
|
-
|
|
47
|
-
info = func.cache_info()
|
|
48
|
-
self.assertEqual(info.hits, self.NTHREADS - 1)
|
|
49
|
-
self.assertEqual(info.misses, 1)
|
|
50
|
-
|
|
51
|
-
def test_cachedmethod_stampede(self):
|
|
52
|
-
threads = [threading.Thread(target=self.meth) for i in range(0, self.NTHREADS)]
|
|
53
|
-
for t in threads:
|
|
54
|
-
t.start()
|
|
55
|
-
for t in threads:
|
|
56
|
-
t.join()
|
|
57
|
-
|
|
58
|
-
self.assertEqual(self.count, 1)
|
|
59
|
-
|
|
60
|
-
info = self.meth.cache_info()
|
|
61
|
-
self.assertEqual(info.hits, self.NTHREADS - 1)
|
|
62
|
-
self.assertEqual(info.misses, 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|