Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.9.1
current_version = 1.9.2
commit = False
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+)(?P<build>\d+))?
Expand Down
2 changes: 1 addition & 1 deletion nwastdlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#
"""The NWA-stdlib module."""

__version__ = "1.9.1"
__version__ = "1.9.2"

from nwastdlib.f import const, identity

Expand Down
25 changes: 16 additions & 9 deletions nwastdlib/asyncio_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def cached_result(
key_name: str | None = None,
expiry_seconds: int = 120,
serializer: SerializerProtocol = DefaultSerializer,
revalidate_fn: Callable[..., bool] | None = None,
) -> Callable:
"""Pass returned result objects from a async function call into redis.

Expand Down Expand Up @@ -150,6 +151,7 @@ def my_other_function...
key_name: When specified the total redis key_name will be "prefix:key_name".
expiry_seconds: expiration in seconds. Defaults to two minutes (120s).
serializer: Provide your own Serializer class or use the default pickle.
revalidate_fn: Callable function that returns bool whether to revalidate the cached result.

Returns:
decorator function
Expand All @@ -159,6 +161,8 @@ def my_other_function...
def cache_decorator(func: Callable) -> Callable:
@wraps(func)
async def func_wrapper(*args: tuple[Any], **kwargs: dict[str, Any]) -> Any:
from_cache = (not revalidate_fn(*args, **kwargs)) if revalidate_fn else True

python_major, python_minor = sys.version_info[:2]
if key_name:
cache_key = f"{prefix}:{python_major}.{python_minor}:{key_name}"
Expand All @@ -169,17 +173,20 @@ async def func_wrapper(*args: tuple[Any], **kwargs: dict[str, Any]) -> Any:
cache_key = f"{prefix}:{python_major}.{python_minor}:{func.__name__}{args_and_kwargs_string}"
logger.debug("Autogenerated a cache key", cache_key=cache_key)

logger.debug("Cache called with wrapper func", func_name=func.__name__, cache_key=cache_key)
if secret:
if (cached_val := await get_signed_cache_value(pool, secret, cache_key, serializer)) is not None:
logger.info("Cache contains secure key, serving from cache", func_name=func.__name__)
return cached_val
if from_cache:
logger.debug("Cache called with wrapper func", func_name=func.__name__, cache_key=cache_key)
if secret:
if (cached_val := await get_signed_cache_value(pool, secret, cache_key, serializer)) is not None:
logger.info("Cache contains secure key, serving from cache", func_name=func.__name__)
return cached_val
else:
if (cached_val := await get_cache_value(pool, cache_key, serializer)) is not None:
logger.info("Cache contains key, serving from cache", func_name=func.__name__)
return cached_val
logger.info("Cache doesn't contain key, calling real function", func_name=func.__name__)
else:
if (cached_val := await get_cache_value(pool, cache_key, serializer)) is not None:
logger.info("Cache contains key, serving from cache", func_name=func.__name__)
return cached_val
logger.info("ignoring cache, calling real function", func_name=func.__name__)

logger.info("Cache doesn't contain key, calling real function", func_name=func.__name__)
result = await func(*args, **kwargs)
if secret:
await set_signed_cache_value(pool, secret, cache_key, result, expiry_seconds, serializer)
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ strict_equality = True

show_error_codes = True
show_column_numbers = True
exclude = (benchmarks/*)
exclude = (benchmarks/*|tests/*)

;lineprecision_report = mypy-coverage

Expand Down
44 changes: 44 additions & 0 deletions tests/test_asyncio_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,47 @@ async def slow_function():
# # A new call should still serve 0: as it is cached now
result = await slow_function()
assert result == original_value


async def test_cache_decorator_with_revalidation_fn():
redis = FakeRedis()
value = 0

def revalidate_fn(*args, **kwargs):
return kwargs["revalidate_cache"]

@cached_result(redis, "test-suite", "SECRETNAME", "keyname", revalidate_fn=revalidate_fn)
async def slow_function(revalidate_cache: bool):
return value

result = await slow_function(revalidate_cache=False)
assert result == 0

# change the value so we can verify that the function was not called
value = 1

# A new call should still serve 0: as it is cached now
result = await slow_function(revalidate_cache=False)
assert result == 0


async def test_cache_decorator_with_revalidation_fn_no_cache():
redis = FakeRedis()
value = 0

def revalidate_fn(*args, **kwargs):
return kwargs["revalidate_cache"]

@cached_result(redis, "test-suite", "SECRETNAME", "keyname", revalidate_fn=revalidate_fn)
async def slow_function(revalidate_cache: bool):
return value

result = await slow_function(revalidate_cache=True)
assert result == 0

# change the value so we can verify that the function was called
value = 1

# A new call should serve 1: as it is not cached now
result = await slow_function(revalidate_cache=True)
assert result == 1