Skip to content
2 changes: 2 additions & 0 deletions src/cachier/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class Params:
cleanup_interval: timedelta = timedelta(days=1)
entry_size_limit: Optional[int] = None
allow_non_static_methods: bool = False
key_prefix: str = "cachier"
func_prefix: str = "."


_global_params = Params()
Expand Down
5 changes: 5 additions & 0 deletions src/cachier/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def cachier(
mongetter: Optional[Mongetter] = None,
sql_engine: Optional[Union[str, Any, Callable[[], Any]]] = None,
redis_client: Optional["RedisClient"] = None,
key_prefix: Optional[str] = None,
s3_bucket: Optional[str] = None,
Comment on lines 222 to 230

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

included in docstring 6cd8e5d

s3_prefix: str = "cachier",
s3_client: Optional["S3Client"] = None,
Expand Down Expand Up @@ -280,6 +281,8 @@ def cachier(
redis_client : redis.Redis or callable, optional
Redis client instance or callable returning a Redis client.
Used for the Redis backend.
key_prefix : str, optional
Key prefix applied to all redis keys. Defaults to ``"cachier"``.
s3_bucket : str, optional
The S3 bucket name for cache storage. Required when using the S3 backend.
s3_prefix : str, optional
Expand Down Expand Up @@ -357,6 +360,7 @@ def cachier(
backend = _update_with_defaults(backend, "backend")
mongetter = _update_with_defaults(mongetter, "mongetter")
size_limit_bytes = parse_bytes(_update_with_defaults(entry_size_limit, "entry_size_limit"))
key_prefix = _update_with_defaults(key_prefix, "key_prefix")

# Create metrics object if enabled
cache_metrics = None
Expand Down Expand Up @@ -407,6 +411,7 @@ def cachier(
wait_for_calc_timeout=wait_for_calc_timeout,
entry_size_limit=size_limit_bytes,
metrics=cache_metrics,
key_prefix=key_prefix,
)
Comment on lines 359 to 415

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added tests here 1108356

elif backend == "s3":
core = _S3Core(
Expand Down
3 changes: 2 additions & 1 deletion src/cachier/cores/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def _get_func_str(func: Callable) -> str:
__name__, but at runtime the decorated functions always do.

"""
return f".{func.__module__}.{func.__name__}"
func_prefix = _update_with_defaults(None, "func_prefix")
return f"{func_prefix}{func.__module__}.{func.__name__}"


class _BaseCore(metaclass=abc.ABCMeta):
Expand Down
30 changes: 30 additions & 0 deletions tests/redis_tests/test_async_redis_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,36 @@ def _func(x: int) -> int:
return core


@pytest.mark.redis
@pytest.mark.asyncio
async def test_async_redis_key_prefix_passed_to_client():
pytest.importorskip("redis")

class PrefixCapturingRedis(_AsyncInMemoryRedis):
def __init__(self):
super().__init__()
self.keys_used: list[str] = []

async def hset(self, key: str, field=None, value=None, mapping=None, **kwargs):
self.keys_used.append(key)
await super().hset(key, field=field, value=value, mapping=mapping, **kwargs)

client = PrefixCapturingRedis()

async def get_redis_client():
return client

@cachier(backend="redis", redis_client=get_redis_client, key_prefix="custom-prefix")
async def async_cached_value(x: int) -> int:
return x + 1

result = await async_cached_value(1)
assert result == 2
assert client.keys_used, "Redis client was not called"
assert all(key.startswith("custom-prefix:") for key in client.keys_used)
assert all(stored_key.startswith("custom-prefix:") for stored_key in client._data)


@pytest.mark.redis
@pytest.mark.asyncio
async def test_async_redis_core_helpers_and_client_resolution():
Expand Down
25 changes: 25 additions & 0 deletions tests/redis_tests/test_redis_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ def _test_redis_caching(arg_1, arg_2):
assert val6 == val5


@pytest.mark.redis
def test_redis_key_prefix_passed_to_client():
pytest.importorskip("redis")

class PrefixCapturingRedis(_SyncInMemoryRedis):
def __init__(self):
super().__init__()
self.keys_used: list[str] = []

def hset(self, key: str, field=None, value=None, mapping=None, **kwargs):
self.keys_used.append(key)
return super().hset(key, field=field, value=value, mapping=mapping, **kwargs)

client = PrefixCapturingRedis()

@cachier(backend="redis", redis_client=client, key_prefix="custom-prefix")
def cached_value(x: int) -> int:
return x + 1

result = cached_value(1)
assert result == 2
assert client.keys_used, "Redis client was not called"
assert all(key.startswith("custom-prefix:") for key in client.keys_used)


@pytest.mark.redis
def test_redis_stale_after():
"""Testing Redis core stale_after functionality."""
Expand Down
18 changes: 17 additions & 1 deletion tests/test_base_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import pytest

from cachier.cores.base import RecalculationNeeded, _BaseCore
from cachier.config import get_global_params, set_global_params
from cachier.cores.base import RecalculationNeeded, _BaseCore, _get_func_str


class ConcreteCachingCore(_BaseCore):
Expand Down Expand Up @@ -82,6 +83,21 @@ def test_should_store_exception():
assert core._should_store("test_value") is True


def test_get_func_str_honors_global_func_prefix():
"""Test _get_func_str prepends the configured func_prefix."""
original_prefix = get_global_params().func_prefix
try:
set_global_params(func_prefix="custom-prefix-")

def dummy_function():
return None

expected = f"custom-prefix-{dummy_function.__module__}.{dummy_function.__name__}"
assert _get_func_str(dummy_function) == expected
finally:
set_global_params(func_prefix=original_prefix)


@pytest.mark.asyncio
async def test_base_core_async_default_wrappers():
"""Test async default wrappers delegate correctly to sync methods."""
Expand Down
2 changes: 2 additions & 0 deletions tests/test_core_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ def test_get_default_params():
"cleanup_interval",
"cleanup_stale",
"entry_size_limit",
"func_prefix",
"hash_func",
"key_prefix",
"mongetter",
"next_time",
"pickle_reload",
Expand Down