From afb33906fe201750e6f85d5d22dd2641fb29cc77 Mon Sep 17 00:00:00 2001 From: Daniel Chin Date: Fri, 28 Mar 2025 18:28:26 +0400 Subject: [PATCH 1/6] add caller_with_freshness_threshold --- src/cachier/core.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cachier/core.py b/src/cachier/core.py index 30fcb800..312c38f7 100644 --- a/src/cachier/core.py +++ b/src/cachier/core.py @@ -13,7 +13,7 @@ from collections import OrderedDict from concurrent.futures import ThreadPoolExecutor from datetime import datetime, timedelta -from functools import wraps +from functools import wraps, partial from typing import Any, Optional, Union from warnings import warn @@ -214,8 +214,7 @@ def cachier( def _cachier_decorator(func): core.set_func(func) - @wraps(func) - def func_wrapper(*args, **kwds): + def _call(max_age: timedelta | None, *args, **kwds): nonlocal allow_none _allow_none = _update_with_defaults(allow_none, "allow_none", kwds) # print('Inside general wrapper for {}.'.format(func.__name__)) @@ -260,7 +259,7 @@ def func_wrapper(*args, **kwds): if _allow_none or entry.value is not None: _print("Cached result found.") now = datetime.now() - if now - entry.time <= _stale_after: + if now - entry.time <= min(_stale_after, max_age): _print("And it is fresh!") return entry.value _print("But it is stale... :(") @@ -294,6 +293,8 @@ def func_wrapper(*args, **kwds): _print("No entry found. No current calc. Calling like a boss.") return _calc_entry(core, key, func, args, kwds) + func_wrapper = wraps(func)(partial(_call, None)) + def _clear_cache(): """Clear the cache.""" core.clear_cache() @@ -320,11 +321,15 @@ def _precache_value(*args, value_to_cache, **kwds): # noqa: D417 func, _is_method=core.func_is_method, args=args, kwds=kwds ) return core.precache_value((), kwargs, value_to_cache) - + + def _caller_with_freshness_threshold(max_age: timedelta): + return wraps(func)(partial(_call, max_age)) + func_wrapper.clear_cache = _clear_cache func_wrapper.clear_being_calculated = _clear_being_calculated func_wrapper.cache_dpath = _cache_dpath func_wrapper.precache_value = _precache_value + func_wrapper.caller_with_freshness_threshold = _caller_with_freshness_threshold return func_wrapper return _cachier_decorator From e402614ba957b3cccf61d9b4f06c2f2c5221cbdc Mon Sep 17 00:00:00 2001 From: Daniel Chin Date: Fri, 28 Mar 2025 18:41:57 +0400 Subject: [PATCH 2/6] fix type --- src/cachier/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cachier/core.py b/src/cachier/core.py index 312c38f7..3a692d76 100644 --- a/src/cachier/core.py +++ b/src/cachier/core.py @@ -214,7 +214,7 @@ def cachier( def _cachier_decorator(func): core.set_func(func) - def _call(max_age: timedelta | None, *args, **kwds): + def _call(max_age: timedelta, *args, **kwds): nonlocal allow_none _allow_none = _update_with_defaults(allow_none, "allow_none", kwds) # print('Inside general wrapper for {}.'.format(func.__name__)) @@ -293,7 +293,7 @@ def _call(max_age: timedelta | None, *args, **kwds): _print("No entry found. No current calc. Calling like a boss.") return _calc_entry(core, key, func, args, kwds) - func_wrapper = wraps(func)(partial(_call, None)) + func_wrapper = wraps(func)(partial(_call, timedelta.max)) def _clear_cache(): """Clear the cache.""" From 418f052694a08a3fbb6c0f1a3b67217886755b14 Mon Sep 17 00:00:00 2001 From: Daniel Chin Date: Fri, 28 Mar 2025 18:42:03 +0400 Subject: [PATCH 3/6] simple test passed --- tests/test_call_with_freshness_threshold.py | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/test_call_with_freshness_threshold.py diff --git a/tests/test_call_with_freshness_threshold.py b/tests/test_call_with_freshness_threshold.py new file mode 100644 index 00000000..48146f61 --- /dev/null +++ b/tests/test_call_with_freshness_threshold.py @@ -0,0 +1,23 @@ +import time +from datetime import timedelta + +import cachier + +def test_call_with_freshness_threshold(): + @cachier.cachier() + def test_func(a, b): + print('Computing...') + return a + b + + print(f'{test_func(1, 2) = }') + print(f'{test_func(1, 2) = }') + caller_with_freshness_threshold = test_func.caller_with_freshness_threshold( + timedelta(seconds=0.5), + ) + print(f'{caller_with_freshness_threshold(1, 2) = }') + print(f'{time.sleep(1.0) = }') + print(f'{test_func(1, 2) = }') + print(f'{caller_with_freshness_threshold(1, 2) = }') + +if __name__ == '__main__': + test_call_with_freshness_threshold() From e2aca2dd1a4a6b23fd94d410952b67e58d53aa33 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:52:30 +0000 Subject: [PATCH 4/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/cachier/core.py | 10 ++++---- tests/test_call_with_freshness_threshold.py | 26 ++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/cachier/core.py b/src/cachier/core.py index 3a692d76..e57c7725 100644 --- a/src/cachier/core.py +++ b/src/cachier/core.py @@ -13,7 +13,7 @@ from collections import OrderedDict from concurrent.futures import ThreadPoolExecutor from datetime import datetime, timedelta -from functools import wraps, partial +from functools import partial, wraps from typing import Any, Optional, Union from warnings import warn @@ -321,15 +321,17 @@ def _precache_value(*args, value_to_cache, **kwds): # noqa: D417 func, _is_method=core.func_is_method, args=args, kwds=kwds ) return core.precache_value((), kwargs, value_to_cache) - + def _caller_with_freshness_threshold(max_age: timedelta): return wraps(func)(partial(_call, max_age)) - + func_wrapper.clear_cache = _clear_cache func_wrapper.clear_being_calculated = _clear_being_calculated func_wrapper.cache_dpath = _cache_dpath func_wrapper.precache_value = _precache_value - func_wrapper.caller_with_freshness_threshold = _caller_with_freshness_threshold + func_wrapper.caller_with_freshness_threshold = ( + _caller_with_freshness_threshold + ) return func_wrapper return _cachier_decorator diff --git a/tests/test_call_with_freshness_threshold.py b/tests/test_call_with_freshness_threshold.py index 48146f61..e99a7c0f 100644 --- a/tests/test_call_with_freshness_threshold.py +++ b/tests/test_call_with_freshness_threshold.py @@ -3,21 +3,25 @@ import cachier + def test_call_with_freshness_threshold(): @cachier.cachier() def test_func(a, b): - print('Computing...') + print("Computing...") return a + b - - print(f'{test_func(1, 2) = }') - print(f'{test_func(1, 2) = }') - caller_with_freshness_threshold = test_func.caller_with_freshness_threshold( - timedelta(seconds=0.5), + + print(f"{test_func(1, 2) = }") + print(f"{test_func(1, 2) = }") + caller_with_freshness_threshold = ( + test_func.caller_with_freshness_threshold( + timedelta(seconds=0.5), + ) ) - print(f'{caller_with_freshness_threshold(1, 2) = }') - print(f'{time.sleep(1.0) = }') - print(f'{test_func(1, 2) = }') - print(f'{caller_with_freshness_threshold(1, 2) = }') + print(f"{caller_with_freshness_threshold(1, 2) = }") + print(f"{time.sleep(1.0) = }") + print(f"{test_func(1, 2) = }") + print(f"{caller_with_freshness_threshold(1, 2) = }") + -if __name__ == '__main__': +if __name__ == "__main__": test_call_with_freshness_threshold() From c6fbdcda841d23afefb9498e2c166767f699997a Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Wed, 28 May 2025 14:36:36 +0200 Subject: [PATCH 5/6] Apply suggestions from code review --- src/cachier/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cachier/core.py b/src/cachier/core.py index e57c7725..957cf42a 100644 --- a/src/cachier/core.py +++ b/src/cachier/core.py @@ -214,7 +214,7 @@ def cachier( def _cachier_decorator(func): core.set_func(func) - def _call(max_age: timedelta, *args, **kwds): + def _call(*args, max_age: Optional[timedelta] = None, **kwds): nonlocal allow_none _allow_none = _update_with_defaults(allow_none, "allow_none", kwds) # print('Inside general wrapper for {}.'.format(func.__name__)) @@ -259,7 +259,8 @@ def _call(max_age: timedelta, *args, **kwds): if _allow_none or entry.value is not None: _print("Cached result found.") now = datetime.now() - if now - entry.time <= min(_stale_after, max_age): + stale_delta = min(_stale_after, max_age) if max_age is not None else _stale_after + if now - entry.time <= stale_delta: _print("And it is fresh!") return entry.value _print("But it is stale... :(") From 3523c652ca6a0d38a09e32e6825b527bbb30138d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 12:37:34 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/cachier/core.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cachier/core.py b/src/cachier/core.py index 957cf42a..16f77e1e 100644 --- a/src/cachier/core.py +++ b/src/cachier/core.py @@ -259,7 +259,11 @@ def _call(*args, max_age: Optional[timedelta] = None, **kwds): if _allow_none or entry.value is not None: _print("Cached result found.") now = datetime.now() - stale_delta = min(_stale_after, max_age) if max_age is not None else _stale_after + stale_delta = ( + min(_stale_after, max_age) + if max_age is not None + else _stale_after + ) if now - entry.time <= stale_delta: _print("And it is fresh!") return entry.value