diff --git a/.ignore b/.ignore index 2cdb865e..1c0f7f39 100644 --- a/.ignore +++ b/.ignore @@ -1,6 +1,5 @@ -*/versioneer.py versioneer.py -*versioneer.py *.ipynb -.venv -.venv/* +.venv/ +.idea/ +.vscode/ diff --git a/cachier/core.py b/cachier/core.py index 79becf7c..bebb20ca 100644 --- a/cachier/core.py +++ b/cachier/core.py @@ -89,6 +89,17 @@ def _default_hash_func(args, kwds): return hash.hexdigest() +def _convert_args_kwargs(func, _is_method: bool, args: tuple, kwds: dict) -> dict: + """Convert mix of positional and keyword arguments to aggregated kwargs.""" + args_as_kw = dict( + zip(func.__code__.co_varnames[1:], args[1:]) + if _is_method else + zip(func.__code__.co_varnames, args) + ) + # merge args expanded as kwargs and the original kwds + return dict(**args_as_kw, **kwds) + + class MissingMongetter(ValueError): """Thrown when the mongetter keyword argument is missing.""" @@ -252,23 +263,23 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911 ignore_cache = kwds.pop('ignore_cache', False) overwrite_cache = kwds.pop('overwrite_cache', False) verbose_cache = kwds.pop('verbose_cache', False) + # 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 # skipcq: FLK-E731 # noqa: E731 if verbose_cache: _print = print if ignore_cache or not _default_params['caching_enabled']: - return func(*args, **kwds) - if core.func_is_method: - key, entry = core.get_entry(args[1:], kwds) - else: - key, entry = core.get_entry(args, kwds) + return func(**kwargs) + key, entry = core.get_entry(tuple(), kwargs) if overwrite_cache: return _calc_entry(core, key, func, args, kwds) if entry is not None: # pylint: disable=R0101 _print('Entry found.') 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 + local_stale_after = stale_after or _default_params['stale_after'] # noqa: E501 + local_next_time = next_time or _default_params['next_time'] # noqa: E501 now = datetime.datetime.now() if now - entry['time'] > local_stale_after: _print('But it is stale... :(') @@ -280,20 +291,13 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911 try: return core.wait_on_entry_calc(key) except RecalculationNeeded: - return _calc_entry( - core, key, func, args, kwds - ) + return _calc_entry(core, key, func, args, kwds) if local_next_time: _print('Async calc and return stale') try: core.mark_entry_being_calculated(key) _get_executor().submit( - _function_thread, - core, - key, - func, - args, - kwds, + _function_thread, core, key, func, args, kwds, ) finally: core.mark_entry_not_calculated(key) @@ -334,7 +338,9 @@ def precache_value(*args, value_to_cache, **kwds): value : any entry to be written into the cache """ - return core.precache_value(args, kwds, value_to_cache) + # merge args expanded as kwargs and the original kwds + kwargs = _convert_args_kwargs(func, _is_method=core.func_is_method, args=args, kwds=kwds) + return core.precache_value(tuple(), kwargs, value_to_cache) func_wrapper.clear_cache = clear_cache func_wrapper.clear_being_calculated = clear_being_calculated diff --git a/tests/test_general.py b/tests/test_general.py index 6ddf8510..551e9214 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -275,3 +275,21 @@ def do_operation(): do_operation() do_operation() assert count == 1 + + +def test_identical_inputs(): + count = 0 + + @cachier.cachier() + def func(a: int, b: int = 2, c: int = 3): + nonlocal count + count += 1 + return a + b + c + + func.clear_cache() + assert count == 0 + func(1, 2, 3) + func(1, 2, c=3) + func(1, b=2, c=3) + func(a=1, b=2, c=3) + assert count == 1