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.
Files changed (38) hide show
  1. {cachetools-7.1.2 → cachetools-7.1.4}/CHANGELOG.rst +16 -0
  2. cachetools-7.1.4/MANIFEST.in +3 -0
  3. {cachetools-7.1.2/src/cachetools.egg-info → cachetools-7.1.4}/PKG-INFO +1 -1
  4. {cachetools-7.1.2 → cachetools-7.1.4}/pyproject.toml +4 -4
  5. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/__init__.py +1 -4
  6. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/func.pyi +20 -19
  7. {cachetools-7.1.2 → cachetools-7.1.4/src/cachetools.egg-info}/PKG-INFO +1 -1
  8. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/SOURCES.txt +1 -0
  9. cachetools-7.1.4/tests/test_threading.py +126 -0
  10. {cachetools-7.1.2 → cachetools-7.1.4}/tox.ini +5 -7
  11. cachetools-7.1.2/tests/test_threading.py +0 -62
  12. {cachetools-7.1.2 → cachetools-7.1.4}/LICENSE +0 -0
  13. {cachetools-7.1.2 → cachetools-7.1.4}/README.rst +0 -0
  14. {cachetools-7.1.2 → cachetools-7.1.4}/docs/conf.py +0 -0
  15. {cachetools-7.1.2 → cachetools-7.1.4}/docs/index.rst +0 -0
  16. {cachetools-7.1.2 → cachetools-7.1.4}/setup.cfg +0 -0
  17. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/__init__.pyi +0 -0
  18. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/_cached.py +0 -0
  19. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/_cachedmethod.py +0 -0
  20. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/func.py +0 -0
  21. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/keys.py +0 -0
  22. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/keys.pyi +0 -0
  23. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools/py.typed +0 -0
  24. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/dependency_links.txt +0 -0
  25. {cachetools-7.1.2 → cachetools-7.1.4}/src/cachetools.egg-info/top_level.txt +0 -0
  26. {cachetools-7.1.2 → cachetools-7.1.4}/tests/__init__.py +0 -0
  27. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cache.py +0 -0
  28. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cached.py +0 -0
  29. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_cachedmethod.py +0 -0
  30. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_classmethod.py +0 -0
  31. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_fifo.py +0 -0
  32. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_func.py +0 -0
  33. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_keys.py +0 -0
  34. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_lfu.py +0 -0
  35. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_lru.py +0 -0
  36. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_rr.py +0 -0
  37. {cachetools-7.1.2 → cachetools-7.1.4}/tests/test_tlru.py +0 -0
  38. {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
 
@@ -0,0 +1,3 @@
1
+ prune .github/
2
+ exclude .gitignore
3
+ exclude .readthedocs.yaml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachetools
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Extensible memoizing collections and decorators
5
5
  Author-email: Thomas Kemmer <tkemmer@computer.org>
6
6
  Maintainer-email: Thomas Kemmer <tkemmer@computer.org>
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["setuptools >= 61.0.0", "wheel"]
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 = false
53
- reportOptionalContextManager = false
54
- reportOptionalMemberAccess = false
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.2"
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[..., _R]
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: Any, **kwargs: Any) -> _R: ...
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[..., _R]], _cachetools_cache_wrapper[_R]]: ...
25
+ ) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
25
26
  @overload
26
27
  def fifo_cache(
27
- maxsize: Callable[..., _R], typed: bool = False
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[..., _R]], _cachetools_cache_wrapper[_R]]: ...
33
+ ) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
33
34
  @overload
34
35
  def lfu_cache(
35
- maxsize: Callable[..., _R], typed: bool = False
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[..., _R]], _cachetools_cache_wrapper[_R]]: ...
41
+ ) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
41
42
  @overload
42
43
  def lru_cache(
43
- maxsize: Callable[..., _R], typed: bool = False
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[..., _R]], _cachetools_cache_wrapper[_R]]: ...
51
+ ) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
51
52
  @overload
52
53
  def rr_cache(
53
- maxsize: Callable[..., _R],
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[..., _R]], _cachetools_cache_wrapper[_R]]: ...
64
+ ) -> Callable[[Callable[_P, _R]], _cachetools_cache_wrapper[_P, _R]]: ...
64
65
  @overload
65
66
  def ttl_cache(
66
- maxsize: Callable[..., _R],
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]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachetools
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Extensible memoizing collections and decorators
5
5
  Author-email: Thomas Kemmer <tkemmer@computer.org>
6
6
  Maintainer-email: Thomas Kemmer <tkemmer@computer.org>
@@ -1,5 +1,6 @@
1
1
  CHANGELOG.rst
2
2
  LICENSE
3
+ MANIFEST.in
3
4
  README.rst
4
5
  pyproject.toml
5
6
  tox.ini
@@ -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,lint,py,pyright
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:format]
23
+ [testenv:ruff]
26
24
  deps =
27
25
  ruff
28
26
  commands =
29
- ruff format --diff -q {posargs}
27
+ ruff check {posargs}
30
28
  skip_install = true
31
29
 
32
- [testenv:lint]
30
+ [testenv:ruff-format]
33
31
  deps =
34
32
  ruff
35
33
  commands =
36
- ruff check {posargs}
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