from __future__ import annotations
import hashlib
import json
import pickle
from abc import ABC
from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Optional
from ..utils import b64digest, get_callable_bytecode
if TYPE_CHECKING: # pragma: no cover
from redis.typing import KeyT
from ..typing import Hash
__all__ = (
"HashConfig",
"AbstractHashMixin",
"JsonMd5HashMixin",
"JsonMd5HexHashMixin",
"JsonMd5Base64HashMixin",
"JsonSha1HashMixin",
"JsonSha1HexHashMixin",
"JsonSha1Base64HashMixin",
"JsonSha256HashMixin",
"JsonSha256HexHashMixin",
"JsonSha256Base64HashMixin",
"JsonSha512HashMixin",
"JsonSha512HexHashMixin",
"JsonSha512Base64HashMixin",
"PickleMd5HashMixin",
"PickleMd5HexHashMixin",
"PickleMd5Base64HashMixin",
"PickleSha1HashMixin",
"PickleSha1HexHashMixin",
"PickleSha1Base64HashMixin",
"PickleSha256HashMixin",
"PickleSha256HexHashMixin",
"PickleSha256Base64HashMixin",
"PickleSha512HashMixin",
"PickleSha512HexHashMixin",
"PickleSha512Base64HashMixin",
)
[docs]
@dataclass(frozen=True)
class HashConfig:
"""A :func:`dataclasses.dataclass` Configurator for :class:`.AbstractHashMixin`"""
algorithm: str
"""name for hashing algorithm
The name must be supported by :mod:`hashlib`.
"""
serializer: Callable[[Any], bytes]
"""function to serialize function positional and keyword arguments."""
decoder: Optional[Callable[[Hash], KeyT]] = None
"""function to decode hash digest to member of a sorted/unsorted set and also field name of a hash map in redis.
Default is :data:`None`, means no decoding and to use the raw digest bytes directly.
"""
use_bytecode: bool = True
"""whether to use bytecode of the function to calculate hash.
.. versionadded:: 0.5
"""
[docs]
class AbstractHashMixin(ABC):
"""An abstract mixin class for hash function name, source code, and arguments.
.. inheritance-diagram:: AbstractHashMixin
:parts: 1
The hash result is used inside the redis (ordered) set and hash map in redis, aka the sub-key.
**Do NOT use the mixin class directly.**
Inherit it and override the :attr:`.__hash_config__` to define the algorithm and serializer.
Example:
::
class JsonMd5B64HashMixin(AbstractHashMixin):
__hash_config__ = HashConfig(
algorithm="md5",
serializer=lambda x: json.dumps(x).encode(),
decoder=lambda x: b64encode(x.digest()),
)
Attributes:
__hash_config__ (HashConfig): Configure of how to calculate hash for a function.
"""
__hash_config__: HashConfig
[docs]
def calc_hash(
self,
f: Optional[Callable] = None,
args: Optional[tuple[Any, ...]] = None,
kwds: Optional[dict[str, Any]] = None,
) -> KeyT:
"""Mixin method to overwrite :meth:`redis_func_cache.policies.abstract.AbstractPolicy.calc_hash`
All other mixin classes in the module inherit this mixin class, and their ``hash`` value are all return by the method.
They use different hash algorithms and serializers defined in the class attribute :attr:`.__hash_config__` to generate different ``hash`` value.
Args:
f: The function to calculate hash for.
args: The :term`sequence` arguments of the function.
kwds: The keyword arguments of the function.
Returns:
The hash value of the function.
Raises:
TypeError: If the function is not callable.
"""
if not callable(f):
raise TypeError("Can not calculate hash for a non-callable object")
conf = self.__hash_config__
hash = hashlib.new(conf.algorithm)
hash.update(f"{f.__module__}:{f.__qualname__}".encode())
if conf.use_bytecode:
hash.update(get_callable_bytecode(f))
if args is not None:
hash.update(conf.serializer(args))
if kwds is not None:
hash.update(conf.serializer(kwds))
if conf.decoder is None:
return hash.digest()
return conf.decoder(hash)
[docs]
class JsonMd5HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the MD5 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: JsonMd5HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="md5", serializer=lambda x: json.dumps(x).encode())
[docs]
class JsonMd5HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the MD5 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: JsonMd5HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="md5", serializer=lambda x: json.dumps(x).encode(), decoder=lambda x: x.hexdigest()
)
[docs]
class JsonMd5Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the MD5 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: JsonMd5Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="md5",
serializer=lambda x: json.dumps(x).encode(),
decoder=b64digest,
)
[docs]
class JsonSha1HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA1 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: JsonSha1HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha1", serializer=lambda x: json.dumps(x).encode())
[docs]
class JsonSha1HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA1 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: JsonSha1HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha1", serializer=lambda x: json.dumps(x).encode(), decoder=lambda x: x.hexdigest()
)
[docs]
class JsonSha1Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA1 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: JsonSha1Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha1",
serializer=lambda x: json.dumps(x).encode(),
decoder=b64digest,
)
[docs]
class JsonSha256HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA256 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: JsonSha256HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha256", serializer=lambda x: json.dumps(x).encode())
[docs]
class JsonSha256HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA256 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: JsonSha256HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha256", serializer=lambda x: json.dumps(x).encode(), decoder=lambda x: x.hexdigest()
)
[docs]
class JsonSha256Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA256 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: JsonSha256Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha256",
serializer=lambda x: json.dumps(x).encode(),
decoder=b64digest,
)
[docs]
class JsonSha512HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA512 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: JsonSha512HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha512", serializer=lambda x: json.dumps(x).encode())
[docs]
class JsonSha512HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA512 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: JsonSha512HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha512", serializer=lambda x: json.dumps(x).encode(), decoder=lambda x: x.hexdigest()
)
[docs]
class JsonSha512Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`json` module,
then calculates the SHA512 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: JsonSha512Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(
algorithm="sha512",
serializer=lambda x: json.dumps(x).encode(),
decoder=b64digest,
)
[docs]
class PickleMd5HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the MD5 hash value,
and finally returns the digest as bytes.
It is the default hash mixin.
.. inheritance-diagram:: PickleMd5HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="md5", serializer=pickle.dumps)
[docs]
class PickleMd5HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the MD5 hash value, and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: PickleMd5HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="md5", serializer=pickle.dumps, decoder=lambda x: x.hexdigest())
[docs]
class PickleMd5Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the MD5 hash value, and finally returns the base64 encoded digest.
.. inheritance-diagram:: PickleMd5Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="md5", serializer=pickle.dumps, decoder=b64digest)
[docs]
class PickleSha1HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA1 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: PickleSha1HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha1", serializer=pickle.dumps)
[docs]
class PickleSha1HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA1 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: PickleSha1HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha1", serializer=pickle.dumps, decoder=lambda x: x.hexdigest())
[docs]
class PickleSha1Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA1 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: PickleSha1Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha1", serializer=pickle.dumps, decoder=b64digest)
[docs]
class PickleSha256HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA256 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: PickleSha256HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha256", serializer=pickle.dumps)
[docs]
class PickleSha256HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA256 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: PickleSha256HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha256", serializer=pickle.dumps, decoder=lambda x: x.hexdigest())
[docs]
class PickleSha256Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA256 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: PickleSha256Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha256", serializer=pickle.dumps, decoder=b64digest)
[docs]
class PickleSha512HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA512 hash value,
and finally returns the digest as bytes.
.. inheritance-diagram:: PickleSha512HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha512", serializer=pickle.dumps)
[docs]
class PickleSha512HexHashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA512 hash value,
and finally returns the hexadecimal representation of the digest.
.. inheritance-diagram:: PickleSha512HexHashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha512", serializer=pickle.dumps, decoder=lambda x: x.hexdigest())
[docs]
class PickleSha512Base64HashMixin(AbstractHashMixin):
"""
Serializes the function name, source code, and arguments using the :mod:`pickle` module,
then calculates the SHA512 hash value,
and finally returns the base64 encoded digest.
.. inheritance-diagram:: PickleSha512Base64HashMixin
:parts: 1
"""
__hash_config__ = HashConfig(algorithm="sha512", serializer=pickle.dumps, decoder=b64digest)