Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query decorated function if result is from cache #51

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
18 changes: 18 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ You can add a default, pickle-based, persistent cache to your function - meaning
"""Your function now has a persistent cache mapped by argument values!"""
return {'arg1': arg1, 'arg2': arg2}

Did the result come from the cache?
-----------------------------------

You can find out of the function returned a value from the cache or not, by
calling the ``is_from_cache()`` function:

.. code-block:: python

>>> foo(1, 2)
{'arg1': 1, 'arg2': 2}
>>> foo.is_from_cache()
False
>>> foo(1, 2)
{'arg1': 1, 'arg2': 2}
>>> foo.is_from_cache()
True


You can get the fully qualified path to the directory of cache files used by ``cachier`` (``~/.cachier`` by default) by calling the ``cache_dpath()`` function:

.. code-block:: python
Expand Down
13 changes: 13 additions & 0 deletions cachier/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,16 @@ def cachier(

def _cachier_decorator(func):
core.set_func(func)
__is_from_cache = False

@wraps(func)
def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
# 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)
nonlocal __is_from_cache
__is_from_cache = False
_print = lambda x: None # skipcq: FLK-E731 # noqa: E731
if verbose_cache:
_print = print
Expand All @@ -154,6 +157,7 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
if entry is not None: # pylint: disable=R0101
_print('Entry found.')
if entry.get('value', None) is not None:
__is_from_cache = True
_print('Cached result found.')
if stale_after:
now = datetime.datetime.now()
Expand All @@ -167,6 +171,7 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
__is_from_cache = False
return _calc_entry(
core, key, func, args, kwds
)
Expand All @@ -185,6 +190,7 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
finally:
core.mark_entry_not_calculated(key)
return entry['value']
__is_from_cache = False
_print('Calling decorated function and waiting')
return _calc_entry(core, key, func, args, kwds)
_print('And it is fresh!')
Expand All @@ -194,6 +200,7 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
__is_from_cache = False
return _calc_entry(core, key, func, args, kwds)
_print('No entry found. No current calc. Calling like a boss.')
return _calc_entry(core, key, func, args, kwds)
Expand All @@ -213,9 +220,15 @@ def cache_dpath():
except AttributeError:
return None

def is_from_cache():
"""Returns True if the result from the latest call is from the cache, False if not."""
nonlocal __is_from_cache
return __is_from_cache

func_wrapper.clear_cache = clear_cache
func_wrapper.clear_being_calculated = clear_being_calculated
func_wrapper.cache_dpath = cache_dpath
func_wrapper.is_from_cache = is_from_cache
return func_wrapper

return _cachier_decorator
25 changes: 25 additions & 0 deletions tests/test_pickle_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ def test_stale_after():
"""Testing the stale_after functionality."""
_stale_after_seconds.clear_cache()
val1 = _stale_after_seconds(1, 2)
assert not _stale_after_seconds.is_from_cache()
val2 = _stale_after_seconds(1, 2)
assert _stale_after_seconds.is_from_cache()
val3 = _stale_after_seconds(1, 3)
assert not _stale_after_seconds.is_from_cache()
assert val1 == val2
assert val1 != val3
sleep(3)
val4 = _stale_after_seconds(1, 2)
assert not _stale_after_seconds.is_from_cache()
assert val4 != val1
_stale_after_seconds.clear_cache()

Expand All @@ -85,15 +89,20 @@ def test_stale_after_next_time():
"""Testing the stale_after with next_time functionality."""
_stale_after_next_time.clear_cache()
val1 = _stale_after_next_time(1, 2)
assert not _stale_after_next_time.is_from_cache()
val2 = _stale_after_next_time(1, 2)
assert _stale_after_next_time.is_from_cache()
val3 = _stale_after_next_time(1, 3)
assert not _stale_after_next_time.is_from_cache()
assert val1 == val2
assert val1 != val3
sleep(SECONDS_IN_DELTA + 1)
val4 = _stale_after_next_time(1, 2)
assert _stale_after_next_time.is_from_cache()
assert val4 == val1
sleep(0.5)
val5 = _stale_after_next_time(1, 2)
assert _stale_after_next_time.is_from_cache()
assert val5 != val1
_stale_after_next_time.clear_cache()

Expand All @@ -113,21 +122,29 @@ def test_overwrite_cache():
"""Tests that the overwrite feature works correctly."""
_random_num.clear_cache()
int1 = _random_num()
assert not _random_num.is_from_cache()
int2 = _random_num()
assert _random_num.is_from_cache()
assert int2 == int1
int3 = _random_num(overwrite_cache=True)
assert not _random_num.is_from_cache()
assert int3 != int1
int4 = _random_num()
assert _random_num.is_from_cache()
assert int4 == int3
_random_num.clear_cache()

_random_num_with_arg.clear_cache()
int1 = _random_num_with_arg('a')
assert not _random_num_with_arg.is_from_cache()
int2 = _random_num_with_arg('a')
assert _random_num_with_arg.is_from_cache()
assert int2 == int1
int3 = _random_num_with_arg('a', overwrite_cache=True)
assert not _random_num_with_arg.is_from_cache()
assert int3 != int1
int4 = _random_num_with_arg('a')
assert _random_num_with_arg.is_from_cache()
assert int4 == int3
_random_num_with_arg.clear_cache()

Expand All @@ -136,22 +153,30 @@ def test_ignore_cache():
"""Tests that the ignore_cache feature works correctly."""
_random_num.clear_cache()
int1 = _random_num()
assert not _random_num.is_from_cache()
int2 = _random_num()
assert _random_num.is_from_cache()
assert int2 == int1
int3 = _random_num(ignore_cache=True)
assert not _random_num.is_from_cache()
assert int3 != int1
int4 = _random_num()
assert _random_num.is_from_cache()
assert int4 != int3
assert int4 == int1
_random_num.clear_cache()

_random_num_with_arg.clear_cache()
int1 = _random_num_with_arg('a')
assert not _random_num_with_arg.is_from_cache()
int2 = _random_num_with_arg('a')
assert _random_num_with_arg.is_from_cache()
assert int2 == int1
int3 = _random_num_with_arg('a', ignore_cache=True)
assert not _random_num_with_arg.is_from_cache()
assert int3 != int1
int4 = _random_num_with_arg('a')
assert _random_num_with_arg.is_from_cache()
assert int4 != int3
assert int4 == int1
_random_num_with_arg.clear_cache()
Expand Down