Skip to content
Merged
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ Cachier also accepts several keyword arguments in the calls of the function it w
Ignore Cache
~~~~~~~~~~~~

You can have ``cachier`` ignore any existing cache for a specific function call by passing ``ignore_cache=True`` to the function call. The cache will neither be checked nor updated with the new return value.
You can have ``cachier`` ignore any existing cache for a specific function call by passing ``cachier__skip_cache=True`` to the function call. The cache will neither be checked nor updated with the new return value.

.. code-block:: python

Expand All @@ -245,17 +245,17 @@ You can have ``cachier`` ignore any existing cache for a specific function call
return first_num + second_num

def main():
print(sum(5, 3, ignore_cache=True))
print(sum(5, 3, cachier__skip_cache=True))

Overwrite Cache
~~~~~~~~~~~~~~~

You can have ``cachier`` overwrite an existing cache entry - if one exists - for a specific function call by passing ``overwrite_cache=True`` to the function call. The cache will not be checked but will be updated with the new return value.
You can have ``cachier`` overwrite an existing cache entry - if one exists - for a specific function call by passing ``cachier__overwrite_cache=True`` to the function call. The cache will not be checked but will be updated with the new return value.

Verbose Cache Call
~~~~~~~~~~~~~~~~~~

You can have ``cachier`` print out a detailed explanation of the logic of a specific call by passing ``verbose_cache=True`` to the function call. This can be useful if you are not sure why a certain function result is, or is not, returned.
You can have ``cachier`` print out a detailed explanation of the logic of a specific call by passing ``cachier__verbose=True`` to the function call. This can be useful if you are not sure why a certain function result is, or is not, returned.

Cache `None` Values
~~~~~~~~~~~~~~~~~~~
Expand Down
8 changes: 7 additions & 1 deletion cachier/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,15 @@ class Params(TypedDict):
}


def _update_with_defaults(param, name: str):
def _update_with_defaults(
param, name: str, func_kwargs: Optional[dict] = None
):
import cachier

if func_kwargs:
kw_name = f"cachier__{name}"
if kw_name in func_kwargs:
return func_kwargs.pop(kw_name)
if param is None:
return cachier.config._default_params[name]
return param
Expand Down
45 changes: 33 additions & 12 deletions cachier/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import datetime
import inspect
import os
import warnings
from collections import OrderedDict
from concurrent.futures import ThreadPoolExecutor
from functools import wraps
Expand Down Expand Up @@ -93,6 +94,17 @@ def _convert_args_kwargs(
return OrderedDict(sorted(kwargs.items()))


def _pop_kwds_with_deprecation(kwds, name: str, default_value: bool):
if name in kwds:
warnings.warn(
f"`{name}` is deprecated and will be removed in a future release,"
" use `cachier__` alternative instead.",
DeprecationWarning,
stacklevel=2,
)
return kwds.pop(name, default_value)


def cachier(
hash_func: Optional[HashFunc] = None,
hash_params: Optional[HashFunc] = None,
Expand Down Expand Up @@ -205,18 +217,31 @@ def _cachier_decorator(func):
@wraps(func)
def func_wrapper(*args, **kwds):
nonlocal allow_none
_allow_none = _update_with_defaults(allow_none, "allow_none")
_allow_none = _update_with_defaults(allow_none, "allow_none", kwds)
# print('Inside general wrapper for {}.'.format(func.__name__))
ignore_cache = kwds.pop("ignore_cache", False)
overwrite_cache = kwds.pop("overwrite_cache", False)
verbose_cache = kwds.pop("verbose_cache", False)
ignore_cache = _pop_kwds_with_deprecation(
kwds, "ignore_cache", False
)
overwrite_cache = _pop_kwds_with_deprecation(
kwds, "overwrite_cache", False
)
verbose = _pop_kwds_with_deprecation(kwds, "verbose_cache", False)
ignore_cache = kwds.pop("cachier__skip_cache", ignore_cache)
overwrite_cache = kwds.pop(
"cachier__overwrite_cache", overwrite_cache
)
verbose = kwds.pop("cachier__verbose", verbose)
_stale_after = _update_with_defaults(
stale_after, "stale_after", kwds
)
_next_time = _update_with_defaults(next_time, "next_time", kwds)
# merge args expanded as kwargs and the original kwds
kwargs = _convert_args_kwargs(
func, _is_method=core.func_is_method, args=args, kwds=kwds
)

_print = lambda x: None # noqa: E731
if verbose_cache:
if verbose:
_print = print
if ignore_cache or not _default_params["caching_enabled"]:
return func(**kwargs)
Expand All @@ -229,25 +254,21 @@ def func_wrapper(*args, **kwds):
_print("Entry found.")
if _allow_none or entry.get("value", None) is not None:
_print("Cached result found.")
local_stale_after = _update_with_defaults(
stale_after, "stale_after"
)
local_next_time = _update_with_defaults(next_time, "next_time")
now = datetime.datetime.now()
if now - entry["time"] <= local_stale_after:
if now - entry["time"] <= _stale_after:
_print("And it is fresh!")
return entry["value"]
_print("But it is stale... :(")
if entry["being_calculated"]:
if local_next_time:
if _next_time:
_print("Returning stale.")
return entry["value"] # return stale val
_print("Already calc. Waiting on change.")
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
return _calc_entry(core, key, func, args, kwds)
if local_next_time:
if _next_time:
_print("Async calc and return stale")
try:
core.mark_entry_being_calculated(key)
Expand Down
54 changes: 50 additions & 4 deletions tests/test_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ def test_allow_none_default_param():
separate_files=True,
verbose_cache=True,
)
allow_count = 0
disallow_count = 0
allow_count = disallow_count = 0

@cachier.cachier(cache_dir=tempfile.mkdtemp())
def allow_none():
Expand All @@ -141,18 +140,20 @@ def disallow_none():
disallow_count += 1
return None

allow_none.clear_cache()
assert allow_count == 0
allow_none()
allow_none()
assert allow_count == 1

disallow_none.clear_cache()
assert disallow_count == 0
disallow_none()
disallow_none()
assert disallow_count == 2

disallow_none(cachier__allow_none=True)
disallow_none(cachier__allow_none=True)
assert disallow_count == 2


parametrize_keys = "backend,mongetter"
parametrize_values = [
Expand Down Expand Up @@ -245,3 +246,48 @@ def _calls_wait_for_calc_timeout_slow(res_queue):
res4 = _wait_for_calc_timeout_slow(1, 2)
# One of the cached values is returned
assert res1 == res4 or res2 == res4 or res3 == res4


def test_default_kwargs_handling():
count = 0

@cachier.cachier()
def dummy_func(a, b=2):
nonlocal count
count += 1
return a + b

dummy_func.clear_cache()
assert count == 0
assert dummy_func(1) == 3
assert dummy_func(a=1) == 3
assert dummy_func(a=1, b=2) == 3
assert count == 1


def test_deprecated_func_kwargs():
count = 0

@cachier.cachier()
def dummy_func(a, b=2):
nonlocal count
count += 1
return a + b

dummy_func.clear_cache()
assert count == 0
with pytest.deprecated_call(
match="`verbose_cache` is deprecated and will be removed"
):
assert dummy_func(1, verbose_cache=True) == 3
assert count == 1
with pytest.deprecated_call(
match="`ignore_cache` is deprecated and will be removed"
):
assert dummy_func(1, ignore_cache=True) == 3
assert count == 2
with pytest.deprecated_call(
match="`overwrite_cache` is deprecated and will be removed"
):
assert dummy_func(1, overwrite_cache=True) == 3
assert count == 3
17 changes: 0 additions & 17 deletions tests/test_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,23 +327,6 @@ def dummy_func(a, b):
assert count == 1


def test_default_kwargs_handling():
count = 0

@cachier.cachier()
def dummy_func(a, b=2):
nonlocal count
count += 1
return a + b

dummy_func.clear_cache()
assert count == 0
assert dummy_func(1) == 3
assert dummy_func(a=1) == 3
assert dummy_func(a=1, b=2) == 3
assert count == 1


@pytest.mark.parametrize("backend", ["memory", "pickle"])
def test_diff_functions_same_args(tmpdir, backend: str):
count_p = count_m = 0
Expand Down
12 changes: 6 additions & 6 deletions tests/test_memory_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_memory_core():
_takes_2_seconds.clear_cache()
_takes_2_seconds("a", "b")
start = time()
_takes_2_seconds("a", "b", verbose_cache=True)
_takes_2_seconds("a", "b", cachier__verbose=True)
end = time()
assert end - start < 1
_takes_2_seconds.clear_cache()
Expand All @@ -38,7 +38,7 @@ def test_memory_core_keywords():
_takes_2_seconds.clear_cache()
_takes_2_seconds("a", arg_2="b")
start = time()
_takes_2_seconds("a", arg_2="b", verbose_cache=True)
_takes_2_seconds("a", arg_2="b", cachier__verbose=True)
end = time()
assert end - start < 1
_takes_2_seconds.clear_cache()
Expand Down Expand Up @@ -111,7 +111,7 @@ def test_overwrite_cache():
int1 = _random_num()
int2 = _random_num()
assert int2 == int1
int3 = _random_num(overwrite_cache=True)
int3 = _random_num(cachier__overwrite_cache=True)
assert int3 != int1
int4 = _random_num()
assert int4 == int3
Expand All @@ -121,7 +121,7 @@ def test_overwrite_cache():
int1 = _random_num_with_arg("a")
int2 = _random_num_with_arg("a")
assert int2 == int1
int3 = _random_num_with_arg("a", overwrite_cache=True)
int3 = _random_num_with_arg("a", cachier__overwrite_cache=True)
assert int3 != int1
int4 = _random_num_with_arg("a")
assert int4 == int3
Expand All @@ -135,7 +135,7 @@ def test_ignore_cache():
int1 = _random_num()
int2 = _random_num()
assert int2 == int1
int3 = _random_num(ignore_cache=True)
int3 = _random_num(cachier__skip_cache=True)
assert int3 != int1
int4 = _random_num()
assert int4 != int3
Expand All @@ -146,7 +146,7 @@ def test_ignore_cache():
int1 = _random_num_with_arg("a")
int2 = _random_num_with_arg("a")
assert int2 == int1
int3 = _random_num_with_arg("a", ignore_cache=True)
int3 = _random_num_with_arg("a", cachier__skip_cache=True)
assert int3 != int1
int4 = _random_num_with_arg("a")
assert int4 != int3
Expand Down
8 changes: 4 additions & 4 deletions tests/test_mongo_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ def _test_mongo_caching(arg_1, arg_2):
val1 = _test_mongo_caching(1, 2)
val2 = _test_mongo_caching(1, 2)
assert val1 == val2
val3 = _test_mongo_caching(1, 2, ignore_cache=True)
val3 = _test_mongo_caching(1, 2, cachier__skip_cache=True)
assert val3 != val1
val4 = _test_mongo_caching(1, 2)
assert val4 == val1
val5 = _test_mongo_caching(1, 2, overwrite_cache=True)
val5 = _test_mongo_caching(1, 2, cachier__overwrite_cache=True)
assert val5 != val1
val6 = _test_mongo_caching(1, 2)
assert val6 == val5
Expand All @@ -137,11 +137,11 @@ def _test_mongo_caching(arg_1, arg_2):
val1 = _test_mongo_caching(1, arg_2=2)
val2 = _test_mongo_caching(1, arg_2=2)
assert val1 == val2
val3 = _test_mongo_caching(1, arg_2=2, ignore_cache=True)
val3 = _test_mongo_caching(1, arg_2=2, cachier__skip_cache=True)
assert val3 != val1
val4 = _test_mongo_caching(1, arg_2=2)
assert val4 == val1
val5 = _test_mongo_caching(1, arg_2=2, overwrite_cache=True)
val5 = _test_mongo_caching(1, arg_2=2, cachier__overwrite_cache=True)
assert val5 != val1
val6 = _test_mongo_caching(1, arg_2=2)
assert val6 == val5
Expand Down
16 changes: 8 additions & 8 deletions tests/test_pickle_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_pickle_core(reload, separate_files):
_takes_2_seconds_decorated.clear_cache()
_takes_2_seconds_decorated("a", "b")
start = time()
_takes_2_seconds_decorated("a", "b", verbose_cache=True)
_takes_2_seconds_decorated("a", "b", cachier__verbose=True)
end = time()
assert end - start < 1
_takes_2_seconds_decorated.clear_cache()
Expand All @@ -78,7 +78,7 @@ def test_pickle_core_keywords(separate_files):
_takes_2_seconds_decorated.clear_cache()
_takes_2_seconds_decorated("a", arg_2="b")
start = time()
_takes_2_seconds_decorated("a", arg_2="b", verbose_cache=True)
_takes_2_seconds_decorated("a", arg_2="b", cachier__verbose=True)
end = time()
assert end - start < 1
_takes_2_seconds_decorated.clear_cache()
Expand Down Expand Up @@ -168,7 +168,7 @@ def test_overwrite_cache(separate_files):
int1 = _random_num_decorated()
int2 = _random_num_decorated()
assert int2 == int1
int3 = _random_num_decorated(overwrite_cache=True)
int3 = _random_num_decorated(cachier__overwrite_cache=True)
assert int3 != int1
int4 = _random_num_decorated()
assert int4 == int3
Expand All @@ -178,7 +178,7 @@ def test_overwrite_cache(separate_files):
int1 = _random_num_with_arg_decorated("a")
int2 = _random_num_with_arg_decorated("a")
assert int2 == int1
int3 = _random_num_with_arg_decorated("a", overwrite_cache=True)
int3 = _random_num_with_arg_decorated("a", cachier__overwrite_cache=True)
assert int3 != int1
int4 = _random_num_with_arg_decorated("a")
assert int4 == int3
Expand All @@ -199,7 +199,7 @@ def test_ignore_cache(separate_files):
int1 = _random_num_decorated()
int2 = _random_num_decorated()
assert int2 == int1
int3 = _random_num_decorated(ignore_cache=True)
int3 = _random_num_decorated(cachier__skip_cache=True)
assert int3 != int1
int4 = _random_num_decorated()
assert int4 != int3
Expand All @@ -210,7 +210,7 @@ def test_ignore_cache(separate_files):
int1 = _random_num_with_arg_decorated("a")
int2 = _random_num_with_arg_decorated("a")
assert int2 == int1
int3 = _random_num_with_arg_decorated("a", ignore_cache=True)
int3 = _random_num_with_arg_decorated("a", cachier__skip_cache=True)
assert int3 != int1
int4 = _random_num_with_arg_decorated("a")
assert int4 != int3
Expand Down Expand Up @@ -342,7 +342,7 @@ def _bad_cache(arg_1, arg_2):

def _calls_bad_cache(bad_cache_func, res_queue, trash_cache, separate_files):
try:
res = bad_cache_func(0.13, 0.02, verbose_cache=True)
res = bad_cache_func(0.13, 0.02, cachier__verbose=True)
if trash_cache:
with open(_BAD_CACHE_FPATHS[separate_files], "w") as cache_file:
cache_file.seek(0)
Expand Down Expand Up @@ -566,7 +566,7 @@ def test_pickle_core_custom_cache_dir(separate_files):
_takes_2_seconds_custom_dir_decorated.clear_cache()
_takes_2_seconds_custom_dir_decorated("a", "b")
start = time()
_takes_2_seconds_custom_dir_decorated("a", "b", verbose_cache=True)
_takes_2_seconds_custom_dir_decorated("a", "b", cachier__verbose=True)
end = time()
assert end - start < 1
_takes_2_seconds_custom_dir_decorated.clear_cache()
Expand Down