Skip to content

Commit

Permalink
Allow caching of None values
Browse files Browse the repository at this point in the history
  • Loading branch information
jall authored and shaypal5 committed Jun 30, 2023
1 parent 50fb9da commit cb94fb7
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 8 deletions.
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ The following parameters will only be applied to decorators defined after `set_d

These parameters can be changed at any time and they will apply to all decorators:

* `allow_none`
* `caching_enabled`
* `stale_after`
* `next_time`
Expand Down Expand Up @@ -256,6 +257,10 @@ 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.

Cache `None` Values
~~~~~~~~~~~~~~~~~~~

By default, ``cachier`` does not cache ``None`` values. You can override this behaviour by passing ``allow_none=True`` to the function call.


Cachier Cores
Expand Down
14 changes: 13 additions & 1 deletion cachier/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Params(TypedDict):
pickle_reload: bool
separate_files: bool
wait_for_calc_timeout: int
allow_none: bool


_default_params: Params = {
Expand All @@ -120,6 +121,7 @@ class Params(TypedDict):
'pickle_reload': True,
'separate_files': False,
'wait_for_calc_timeout': 0,
'allow_none': False,
}


Expand All @@ -134,6 +136,7 @@ def cachier(
pickle_reload: Optional[bool] = None,
separate_files: Optional[bool] = None,
wait_for_calc_timeout: Optional[int] = None,
allow_none: Optional[bool] = None,
):
"""A persistent, stale-free memoization decorator.
Expand Down Expand Up @@ -187,6 +190,9 @@ def cachier(
True, any process trying to read the same entry will wait a maximum of
seconds specified in this parameter. 0 means wait forever.
Once the timeout expires the calculation will be triggered.
allow_none: bool, optional
Allows storing None values in the cache. If False, functions returning
None will not be cached and are recalculated every call.
"""
# Check for deprecated parameters
if hash_params is not None:
Expand Down Expand Up @@ -234,6 +240,12 @@ def _cachier_decorator(func):

@wraps(func)
def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
nonlocal allow_none
_allow_none = (
allow_none
if allow_none is not None else
_default_params['allow_none']
)
# print('Inside general wrapper for {}.'.format(func.__name__))
ignore_cache = kwds.pop('ignore_cache', False)
overwrite_cache = kwds.pop('overwrite_cache', False)
Expand All @@ -251,7 +263,7 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
return _calc_entry(core, key, func, args, kwds)
if entry is not None: # pylint: disable=R0101
_print('Entry found.')
if entry.get('value', None) is not None:
if (_allow_none or entry.get('value', None) is not None):
_print('Cached result found.')
local_stale_after = stale_after if stale_after is not None else _default_params['stale_after'] # noqa: E501
local_next_time = next_time if next_time is not None else _default_params['next_time'] # noqa: E501
Expand Down
1 change: 1 addition & 0 deletions tests/test_core_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
def test_get_default_params():
params = get_default_params()
assert tuple(sorted(params)) == (
'allow_none',
'backend',
'cache_dir',
'caching_enabled',
Expand Down
41 changes: 37 additions & 4 deletions tests/test_defaults.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import datetime
import os
import pytest
import queue
import random
import tempfile
import threading
import time

import cachier

from tests.test_mongo_core import _test_mongetter, MONGO_DELTA
import pytest

import cachier
from tests.test_mongo_core import MONGO_DELTA, _test_mongetter

_default_params = cachier.get_default_params().copy()

Expand Down Expand Up @@ -125,6 +124,40 @@ def global_test_2(arg_1, arg_2):
assert len(os.listdir(cache_dir_2)) == 1


def test_allow_none_default_param():
cachier.set_default_params(
allow_none=True,
separate_files=True,
verbose_cache=True,
)
allow_count = 0
disallow_count = 0

@cachier.cachier(cache_dir=tempfile.mkdtemp())
def allow_none():
nonlocal allow_count
allow_count += 1
return None

@cachier.cachier(cache_dir=tempfile.mkdtemp(), allow_none=False)
def disallow_none():
nonlocal disallow_count
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


parametrize_keys = 'backend,mongetter'
parametrize_values = [
pytest.param('pickle', None, marks=pytest.mark.pickle),
Expand Down
41 changes: 38 additions & 3 deletions tests/test_general.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
"""Non-core-specific tests for cachier."""

from __future__ import print_function

import functools
import os
import queue
import subprocess # nosec: B404
import threading
from random import random
from time import sleep, time

import pytest

import cachier
from cachier.core import (
MAX_WORKERS_ENVAR_NAME,
DEFAULT_MAX_WORKERS,
MAX_WORKERS_ENVAR_NAME,
_get_executor,
_max_workers,
_set_max_workers,
_get_executor
)
from tests.test_mongo_core import (
_test_mongetter,
MONGO_DELTA_LONG,
_test_mongetter,
)


Expand Down Expand Up @@ -240,3 +243,35 @@ def get_random():
result_4 = get_random()
assert result_1 == result_2 == result_4
assert result_1 != result_3


def test_none_not_cached_by_default():
count = 0

@cachier.cachier()
def do_operation():
nonlocal count
count += 1
return None

do_operation.clear_cache()
assert count == 0
do_operation()
do_operation()
assert count == 2


def test_allow_caching_none():
count = 0

@cachier.cachier(allow_none=True)
def do_operation():
nonlocal count
count += 1
return None

do_operation.clear_cache()
assert count == 0
do_operation()
do_operation()
assert count == 1

0 comments on commit cb94fb7

Please sign in to comment.