I'm trying to cache the results of a method that performs an HTTP query. The method receives the endpoint (a string) and a payload (a dictionary). The dictionary is not hashable, so it cannot be used with the standard functools.cache
.
I tried to follow the path shown in Using @functools.lru_cache with dictionary arguments, and it is working. But pylance type checker is complaining quite a lot, and I don't know how to get a clean linting.
The code used for testing this issue is:
from functools import cache, wraps
from typing import Any, Collection, Hashable, Mapping
from immutabledict import immutabledict
def deep_freeze(thing: Any) -> Any:
if thing is None or isinstance(thing, str):
return thing
if isinstance(thing, Mapping):
return immutabledict({k: deep_freeze(v) for k, v in thing.items()})
if isinstance(thing, Collection):
return tuple(deep_freeze(i) for i in thing)
if not isinstance(thing, Hashable):
raise TypeError(f"Unfreezable type: '{type(thing)}'")
return thing
def deep_freeze_args(func):
@wraps(func)
def wrapped(*args, **kwargs):
return func(*deep_freeze(args), **deep_freeze(kwargs))
for cache_func in ["cache_info", "cache_clear", "cache_parameters"]:
if hasattr(func, cache_func):
setattr(wrapped, cache_func, getattr(func, cache_func))
return wrapped
@deep_freeze_args
@cache
def func_a(data):
return data
if __name__ == "__main__":
ext_data = {"a": "b", "c": 3}
func_a(ext_data)
deep_freeze
freezes recursively all the data found, using immutabledict
library to freeze the mappings and tuples for other collections.
deep_freeze_args
is the decorator que freezes the arguments before handling the result to the next level, that should be the cache.
func_a
is the test method, the one that contain the HTTP call. For testing, an empty method works equally.
The linter complains at the parameter in the last line:
Argument of type "dict[str, Unknown]" cannot be assigned to parameter "args" of type "Hashable" in function "__call__"
"dict[str, Unknown]" is incompatible with protocol "Hashable"
"__hash__" is an incompatible type
Type "None" cannot be assigned to type "(self: dict[str, Unknown]) -> int"
Any advise on how to get a clean linting?