Skip to content

Hashing

coola.hashing

Provide deterministic hashing functions.

These are content hashes for identity and caching, not for security: do not use them for passwords, signatures or integrity against an attacker.

Hashes are deterministic across calls, processes and machines, and follow these rules:

  • Every hash is domain-separated by a type tag, so values of different types never share a hash even if their text form is the same (1, 1.0, True and "1" are all distinct).
  • Ordered containers (list, tuple, OrderedDict) are order-sensitive and unordered containers (dict, set) are not, like in coola equality. Each container family has its own tag, so a list and a tuple with the same items hash differently.
  • Container hashes are built only from the hashes of their children, resolved through a HasherRegistry.
  • Unsupported types raise KeyError unless ignore_unhashable is set.

coola.hashing.BaseHasher

Bases: ABC, Generic[T]

Base class for type-specific hashers.

This abstract base class defines the interface for hashers that compute a hash value for a specific data type. Each concrete hasher implementation handles a specific data type and knows how to hash its nested elements recursively.

Notes

Subclasses must implement the hash method to define how their specific type should be hashed.

Example
>>> from coola.hashing import StrHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = StrHasher()
>>> hasher
StrHasher()
>>> len(hasher.hash([1, 2, 3], registry=registry))
64

coola.hashing.BaseHasher.hash abstractmethod

hash(
    data: T,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Hash the given data structure recursively.

This method traverses the data structure and computes a hash value for it. The registry is used to resolve appropriate hashers for nested data types encountered during traversal.

Parameters:

Name Type Description Default
data T

The data structure to hash. Must be of the type T that this hasher handles.

required
registry HasherRegistry

The hasher registry used to look up hashers for nested data structures of different types.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive, since each byte of the digest encodes as two hex characters.

64
ignore_unhashable bool

If True, nested objects for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error. Only relevant for hashers that recurse into nested data via the registry.

False

Returns:

Type Description
str

A string representing the hash of the input data.

Example
>>> from coola.hashing import StrHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = StrHasher()
>>> len(hasher.hash("Meowwww", registry=registry))
64

coola.hashing.BytesHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[bytes | bytearray]

Hasher for bytes and bytearray objects.

This hasher computes the hash of the bytes namespaced by the type tag (bytes or bytearray), so a bytes and a bytearray with the same content hash differently.

Example
>>> from coola.hashing import BytesHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = BytesHasher()
>>> hasher
BytesHasher()
>>> len(hasher.hash(b"hello", registry=registry))
64
>>> len(hasher.hash(b"hello", registry=registry, length=16))
16

coola.hashing.BytesHasher.hash

hash(
    data: bytes | bytearray,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of bytes.

Parameters:

Name Type Description Default
data bytes | bytearray

The input bytes to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since bytes are hashed directly with no need to dispatch to another hasher for nested/composite data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_bytes for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.DataclassHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[Any]

Hasher for dataclass instances.

The hash combines the qualified class name with the hash of a mapping from field name to field value, with every value hashed through the registry. Because dataclasses have no common base class, this hasher is not registered by default: register it for each dataclass type with register_hashers.

Example
>>> from dataclasses import dataclass
>>> from coola.hashing import DataclassHasher, get_default_registry
>>> @dataclass
... class Point:
...     x: int
...     y: int
...
>>> registry = get_default_registry()
>>> hasher = DataclassHasher()
>>> hasher
DataclassHasher()
>>> len(hasher.hash(Point(1, 2), registry=registry))
64

coola.hashing.DataclassHasher.hash

hash(
    data: Any,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a dataclass instance.

Parameters:

Name Type Description Default
data Any

The dataclass instance to hash.

required
registry HasherRegistry

The hasher registry used to hash the field values.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
ignore_unhashable bool

If True, field values for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
TypeError

If data is not a dataclass instance.

KeyError

If a field value has a type for which no hasher is registered in registry and ignore_unhashable is False.

ValueError

If length is not an even number between 2 and 128.

coola.hashing.DatetimeHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[date | time]

Hasher for :class:datetime.date, :class:datetime.datetime and :class:datetime.time objects.

This hasher converts the object to its ISO 8601 string representation via isoformat() and hashes that string under a tag made of the concrete class name.

Since :class:datetime.datetime is a subclass of :class:datetime.date, both types are handled by this hasher. The class name is part of the tag, so a date and a datetime never produce the same hash, even for the same calendar day.

Aware datetimes are converted to UTC first, so equal instants in different time zones hash the same, like they compare equal. Naive and aware datetimes differ. An aware time is not normalised: its offset is part of the ISO string.

Example
>>> from datetime import date
>>> from coola.hashing import DatetimeHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = DatetimeHasher()
>>> hasher
DatetimeHasher()
>>> len(hasher.hash(date(2021, 1, 1), registry=registry))
64

coola.hashing.DatetimeHasher.hash

hash(
    data: date | time,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a date/datetime.

Parameters:

Name Type Description Default
data date | time

The date or datetime to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since the ISO string is hashed directly with no need to dispatch to another hasher for nested data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.EnumHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[Enum]

Hasher for :class:enum.Enum members.

The hash depends on the qualified class name and the member name, not on the member value. IntEnum and StrEnum members are therefore not collapsed into int and str.

Example
>>> from enum import Enum
>>> from coola.hashing import EnumHasher, HasherRegistry
>>> class Color(Enum):
...     RED = 1
...
>>> registry = HasherRegistry()
>>> hasher = EnumHasher()
>>> hasher
EnumHasher()
>>> len(hasher.hash(Color.RED, registry=registry))
64

coola.hashing.EnumHasher.hash

hash(
    data: Enum,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of an enum member.

Parameters:

Name Type Description Default
data Enum

The enum member to hash.

required
registry HasherRegistry

The hasher registry. Unused; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.HashableHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[SupportsHash]

Hasher for objects that implement their own hash method.

This hasher delegates to the object's own hash method, passing through the registry so the object can recursively hash its nested data via registry the same way any other hasher would. It works for any object matching the SupportsHash protocol, regardless of its concrete type.

Example
>>> from coola.hashing import HashableHasher, get_default_registry
>>> class MyClass:
...     def __init__(self, value):
...         self._value = value
...     def hash(self, registry=None, length=64, ignore_unhashable=False):
...         if registry is None:
...             registry = get_default_registry()
...         return registry.hash(
...             self._value, length=length, ignore_unhashable=ignore_unhashable
...         )
...
>>> registry = get_default_registry()
>>> hasher = HashableHasher()
>>> hasher
HashableHasher()
>>> len(hasher.hash(MyClass(42), registry=registry))
64

coola.hashing.HashableHasher.hash

hash(
    data: SupportsHash,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash by delegating to the object's own hash method.

Parameters:

Name Type Description Default
data SupportsHash

The object to hash. Must implement the SupportsHash protocol, i.e. expose a hash method accepting registry, length, and ignore_unhashable.

required
registry HasherRegistry

The hasher registry, forwarded to data.hash so the object can recursively hash its nested data.

required
length int

The desired length of the returned hex string. Forwarded to data.hash. Defaults to 64.

64
ignore_unhashable bool

If True, nested values for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error. Forwarded to data.hash.

False

Returns:

Type Description
str

A string representing the hash of data: the value

str

returned by data.hash, namespaced by the qualified

str

class name of data.

Raises:

Type Description
AttributeError

If data does not implement a hash method.

coola.hashing.Hasher

Bases: Protocol[T_contra]

Structural interface of a type-specific hasher.

Any object with a compatible hash method can be registered without subclassing :class:BaseHasher, which remains available as a convenience base class.

coola.hashing.HasherRegistry

Bases: BaseTypeDispatchRegistry[Hasher[Any]]

Registry that manages and dispatches hashers based on data type.

This registry maintains a mapping from Python types to hasher instances and uses the Method Resolution Order (MRO) for type lookup. When hashing data, it automatically selects the most specific registered hasher for the data's type, falling back to parent types or a default hasher if needed.

Parameters:

Name Type Description Default
initial_state dict[type, V] | None

Optional initial mapping of types to hashers. If provided, the state is copied to prevent external mutations.

None
Example

Basic usage with a sequence hasher:

>>> from coola.hashing import HasherRegistry, SequenceHasher, StrHasher
>>> registry = HasherRegistry({object: StrHasher(), list: SequenceHasher()})
>>> registry
HasherRegistry(
  (state): TypeRegistry(
      (<class 'object'>): StrHasher()
      (<class 'list'>): SequenceHasher(tag='sequence')
    )
)
>>> len(registry.hash([1, 2, 3]))
64

Registering custom hashers:

>>> from coola.hashing import HasherRegistry, SequenceHasher, StrHasher
>>> registry = HasherRegistry({object: StrHasher()})
>>> registry.register(list, SequenceHasher())
>>> len(registry.hash([1, 2, 3]))
64

Working with nested structures:

>>> from coola.hashing import get_default_registry
>>> registry = get_default_registry()
>>> data = {"a": [1, 2], "b": [3, 4]}
>>> len(registry.hash(data))
64

coola.hashing.HasherRegistry.find_hasher

find_hasher(data_type: type) -> Hasher[Any]

Type-specific alias for :meth:find: find the appropriate hasher for a given type.

See :meth:BaseTypeDispatchRegistry.find for the full behavior description (MRO resolution, caching, and the KeyError on no match).

Parameters:

Name Type Description Default
data_type type

The Python type to find a hasher for.

required

Returns:

Type Description
Hasher[Any]

The most specific registered hasher for this type, resolved

Hasher[Any]

via MRO, or the default hasher if no match is found.

Example
>>> from collections.abc import Sequence
>>> from coola.hashing import HasherRegistry, SequenceHasher, StrHasher
>>> registry = HasherRegistry({object: StrHasher()})
>>> registry.register(Sequence, SequenceHasher())
>>> hasher = registry.find_hasher(list)
>>> hasher
StrHasher()

coola.hashing.HasherRegistry.has_hasher

has_hasher(data_type: type) -> bool

Type-specific alias for :meth:has: check if a hasher is explicitly registered for the given type.

See :meth:BaseTypeDispatchRegistry.has for the full behavior description.

Parameters:

Name Type Description Default
data_type type

The type to check.

required

Returns:

Type Description
bool

True if a hasher is explicitly registered for this type, False otherwise.

Example
>>> from coola.hashing import HasherRegistry, SequenceHasher
>>> registry = HasherRegistry()
>>> registry.register(list, SequenceHasher())
>>> registry.has_hasher(list)
True
>>> registry.has_hasher(tuple)
False

coola.hashing.HasherRegistry.hash

hash(
    data: object,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Hash the given data by recursively traversing its structure.

This is the main entry point for hashing. It automatically:

  1. Determines the data's type.
  2. Finds the appropriate hasher via find_hasher.
  3. Delegates to that hasher's hash method, which recursively processes any nested structures.

Parameters:

Name Type Description Default
data object

The data to hash. Can be a nested structure such as a list, dict, or tuple.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive, since each byte of the digest encodes as two hex characters.

64
ignore_unhashable bool

If True, objects for which no hasher is registered (including their parent types via MRO) are replaced by a deterministic placeholder hash instead of raising a KeyError. This also applies to nested objects encountered while recursing into sequences or mappings. The placeholder only depends on the type, so any two unsupported objects of the same type hash the same; a UserWarning is emitted each time a placeholder is used. Defaults to False, which raises an error.

False

Returns:

Type Description
str

A string representing the hash of the input data.

Raises:

Type Description
KeyError

If no hasher is registered for data's type (or any of its parent types) and ignore_unhashable is False.

ValueError

If data contains itself (a self-referencing container).

Example
>>> from coola.hashing import get_default_registry
>>> registry = get_default_registry()
>>> len(registry.hash({"scores": [95, 87, 92], "name": "test"}))
64

coola.hashing.MappingHasher

Bases: InlineDisplayMixin, BaseHasher[Mapping[Any, Any]]

Hasher for mapping types.

This hasher hashes each key and value separately via the registry, concatenates the key and value hashes per item, sorts the items by key hash, concatenates all per-item strings, and hashes the result under a type tag.

Sorting by key hash (not by key) ensures that mappings with the same key-value pairs but different insertion orders produce the same hash, without requiring keys to be orderable. This matches coola equality, which ignores the order of the keys of a dict. Register the hasher with ordered=True for a mapping type whose order is meaningful, like OrderedDict: its equality is order-sensitive too.

The tag identifies the mapping family, so a dict and an OrderedDict with the same items hash differently.

Parameters:

Name Type Description Default
tag str

The type tag mixed into the hash. Defaults to "mapping".

'mapping'
ordered bool

If True, items are hashed in iteration order instead of being sorted. Defaults to False.

False
Example
>>> from collections.abc import Mapping
>>> from coola.hashing import MappingHasher, StrHasher, HasherRegistry
>>> registry = HasherRegistry({object: StrHasher(), Mapping: MappingHasher()})
>>> hasher = MappingHasher()
>>> hasher
MappingHasher(tag='mapping', ordered=False)
>>> len(hasher.hash({"a": 1, "b": 2}, registry=registry))
64

coola.hashing.MappingHasher.hash

hash(
    data: Mapping[Any, Any],
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a mapping.

Parameters:

Name Type Description Default
data Mapping[Any, Any]

The mapping to hash.

required
registry HasherRegistry

The hasher registry used to hash each key and value, and to recurse into any nested data structures.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
ignore_unhashable bool

If True, keys/values for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
KeyError

If a key or value has a type for which no hasher is registered in registry and ignore_unhashable is False.

ValueError

If length is not an even number between 2 and 128.

coola.hashing.PathHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[PurePath]

Hasher for pathlib.PurePath and pathlib.Path objects.

This hasher delegates to hash_path, which hashes the POSIX string representation of the path as written. It never touches the filesystem and does not depend on the current working directory, so hashes are portable across machines. A relative path and the absolute path of the same location therefore hash differently; use ResolvedPathHasher to hash the real location instead.

Example
>>> from pathlib import Path
>>> from coola.hashing import PathHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = PathHasher()
>>> hasher
PathHasher()
>>> len(hasher.hash(Path("/tmp/file.txt"), registry=registry))
64

coola.hashing.PathHasher.hash

hash(
    data: PurePath,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a path.

Parameters:

Name Type Description Default
data PurePath

The path to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since the path is hashed directly with no need to dispatch to another hasher for nested data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_path for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.PydanticModelHasher

Bases: InlineDisplayMixin, BaseHasher[BaseModel]

Hasher for pydantic BaseModel objects.

This hasher computes a stable content hash of a pydantic model from its qualified class name and its fields, with every field value hashed through the registry - see hash_pydantic_model for details. Nested models found directly in fields or in plain containers (dict, list, tuple, set, frozenset) are hashed with the same on_secret policy.

Parameters:

Name Type Description Default
on_secret Literal['reveal', 'exclude', 'error']

How SecretStr/SecretBytes fields are handled when hashing - one of "reveal", "exclude", or "error". See unwrap_secrets for the semantics of each option. Defaults to "error" so secret fields are never silently included/excluded without an explicit decision.

'error'
Example
>>> from pydantic import BaseModel
>>> from coola.hashing import PydanticModelHasher, get_default_registry
>>> class Point(BaseModel):
...     x: int
...     y: int
...
>>> registry = get_default_registry()
>>> hasher = PydanticModelHasher()
>>> hasher
PydanticModelHasher(on_secret='error')
>>> len(hasher.hash(Point(x=1, y=2), registry=registry))
64

coola.hashing.PydanticModelHasher.hash

hash(
    data: BaseModel,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a pydantic model.

Parameters:

Name Type Description Default
data BaseModel

The pydantic model instance to hash.

required
registry HasherRegistry

The hasher registry used to hash the field values.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

If True, field values for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If on_secret="error" (the default) and data contains a SecretStr/SecretBytes field, or if length is not an even number between 2 and 128.

KeyError

If a field value has a type for which no hasher is registered in registry and ignore_unhashable is False.

coola.hashing.ReprHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[Any]

Hasher for objects whose repr() is a reliable canonical representation.

This hasher converts the object to its repr() string and hashes it under a type tag. Instances of bool, int, float and complex (including subclasses such as NumPy scalars) use the builtin repr and the builtin type name as tag, so they hash like the equivalent builtin value. Negative zero is normalised to zero, since -0.0 == 0.0. Enum members are hashed with hash_enum instead. It is preferable to StrHasher for numeric types (int, float, complex, bool) because repr() guarantees round-trip accuracy for floating point values, whereas str() may lose precision on some platforms.

Example
>>> from coola.hashing import ReprHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = ReprHasher()
>>> hasher
ReprHasher()
>>> len(hasher.hash(1234, registry=registry))
64

coola.hashing.ReprHasher.hash

hash(
    data: Any,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of repr(data).

Parameters:

Name Type Description Default
data Any

The object to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since the repr() string is hashed directly with no need to dispatch to another hasher for nested data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.ResolvedPathHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[Path]

Hasher for pathlib.Path objects that hashes the resolved location.

The path is resolved (following symlinks, collapsing ./.. segments, making it absolute) before hashing, so that two differently written but logically equivalent paths hash the same. The result depends on the filesystem and on the current working directory, so it is not portable across machines. If resolution fails, the unresolved path is hashed instead.

Example
>>> from pathlib import Path
>>> from coola.hashing import ResolvedPathHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = ResolvedPathHasher()
>>> hasher
ResolvedPathHasher()
>>> len(hasher.hash(Path("file.txt"), registry=registry))
64

coola.hashing.ResolvedPathHasher.hash

hash(
    data: Path,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of the resolved path.

Parameters:

Name Type Description Default
data Path

The path to hash.

required
registry HasherRegistry

The hasher registry. Unused; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_path for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.SequenceHasher

Bases: InlineDisplayMixin, BaseHasher[Sequence[Any]]

Hasher for sequence types.

This hasher computes the hash of each item in the sequence recursively using the registry, concatenates the intermediate hash strings, and then hashes the concatenated result under a type tag. Item order matters.

The tag identifies the sequence family, so sequences of different families with the same items (e.g. a list and a tuple) hash differently. Register one instance per family, e.g. SequenceHasher("list") for list. A named tuple is tagged with its class name and field names, so it differs from a plain tuple and from other named tuples.

Parameters:

Name Type Description Default
tag str

The type tag mixed into the hash. Defaults to "sequence".

'sequence'
Example
>>> from coola.hashing import SequenceHasher, StrHasher, HasherRegistry
>>> registry = HasherRegistry({object: StrHasher(), Sequence: SequenceHasher()})
>>> hasher = SequenceHasher()
>>> hasher
SequenceHasher(tag='sequence')
>>> len(hasher.hash([1, 2, 3], registry=registry))
64

coola.hashing.SequenceHasher.hash

hash(
    data: Sequence[Any],
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a sequence.

Parameters:

Name Type Description Default
data Sequence[Any]

The sequence to hash.

required
registry HasherRegistry

The hasher registry used to hash each item, and to recurse into any nested data structures.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
ignore_unhashable bool

If True, items for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
KeyError

If an item has a type for which no hasher is registered in registry and ignore_unhashable is False.

ValueError

If length is not an even number between 2 and 128.

coola.hashing.SetHasher

Bases: InlineDisplayMixin, BaseHasher[Set[Any]]

Hasher for set types.

This hasher hashes each item via the registry, sorts the item hashes, concatenates them, and hashes the result under a type tag. Sorting by item hash (not by item) makes the result independent of iteration order without requiring items to be orderable.

The tag identifies the set family, so a set and a frozenset with the same items hash differently, and neither collides with a list of the same items.

Parameters:

Name Type Description Default
tag str

The type tag mixed into the hash. Defaults to "set".

'set'
Example
>>> from coola.hashing import SetHasher, StrHasher, HasherRegistry
>>> registry = HasherRegistry({object: StrHasher()})
>>> hasher = SetHasher()
>>> hasher
SetHasher(tag='set')
>>> len(hasher.hash({1, 2, 3}, registry=registry))
64

coola.hashing.SetHasher.hash

hash(
    data: Set[Any],
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a set.

Parameters:

Name Type Description Default
data Set[Any]

The set to hash.

required
registry HasherRegistry

The hasher registry used to hash each item, and to recurse into any nested data structures.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
ignore_unhashable bool

If True, items for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
KeyError

If an item has a type for which no hasher is registered in registry and ignore_unhashable is False.

ValueError

If length is not an even number between 2 and 128.

coola.hashing.StrHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[Any]

Hasher for objects whose str() is a reliable canonical representation.

This hasher converts the object to its str() string and then computes the hash of that string. Unlike ReprHasher, which uses repr(), this hasher uses str(), which is more human-readable but does not guarantee round-trip accuracy for floating point values.

Example
>>> from coola.hashing import StrHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = StrHasher()
>>> hasher
StrHasher()
>>> len(hasher.hash("hello", registry=registry))
64

coola.hashing.StrHasher.hash

hash(
    data: Any,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of str(data).

Parameters:

Name Type Description Default
data Any

The object to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since the str() string is hashed directly with no need to dispatch to another hasher for nested data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.StringHasher

Bases: InlineDisplayMixin, NoArgsDisplayMixin, BaseHasher[str]

Hasher for string objects.

This hasher computes the hash of the string, namespaced by the str type tag, without any intermediate conversion. str subclasses hash like plain strings, except enum members, which hash through hash_enum.

Example
>>> from coola.hashing import StringHasher, HasherRegistry
>>> registry = HasherRegistry()
>>> hasher = StringHasher()
>>> hasher
StringHasher()
>>> len(hasher.hash("Meowwwwww", registry=registry))
64

coola.hashing.StringHasher.hash

hash(
    data: str,
    registry: HasherRegistry,
    *,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a deterministic hash of a string.

Parameters:

Name Type Description Default
data str

The input string to hash.

required
registry HasherRegistry

The hasher registry. Unused by this hasher since strings are hashed directly with no need to dispatch to another hasher for nested/composite data; accepted only to satisfy the common BaseHasher interface.

required
length int

The desired length of the returned hex string. See hash_string for constraints. Defaults to 64.

64
ignore_unhashable bool

Unused by this hasher; accepted only to satisfy the common BaseHasher interface.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.SupportsHash

Bases: Protocol

Implement a protocol to represent objects with a hash method.

coola.hashing.SupportsHash.hash

hash(
    registry: HasherRegistry | None = None,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Return a hash uniquely identifying this object.

Parameters:

Name Type Description Default
registry HasherRegistry | None

The registry used to recursively hash the object's nested data. If None, the default registry is used.

None
length int

The length of the returned hash.

64
ignore_unhashable bool

If True, unhashable values are replaced by a placeholder instead of raising an error.

False

Returns:

Type Description
str

The hash of this object.

coola.hashing.get_default_registry

get_default_registry() -> HasherRegistry

Return the default global hasher registry.

The registry is created on the first call and reused on all subsequent calls (singleton pattern). It is pre-configured with hashers for common Python built-in types.

Returns:

Type Description
HasherRegistry

A singleton HasherRegistry configured for common Python

HasherRegistry

built-in types.

Notes

Because the registry is a singleton, modifications to it (e.g. via register_hashers) affect all future calls to this function. If you need an isolated registry, create a new HasherRegistry instance directly.

Example
>>> from coola.hashing import get_default_registry
>>> registry = get_default_registry()
>>> len(registry.hash("meowwwwww"))
64

coola.hashing.hash_bytes

hash_bytes(value: bytes, length: int = 64) -> str

Generate a hexadecimal hash of bytes using BLAKE2b.

Parameters:

Name Type Description Default
value bytes

The input bytes to hash.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive, since each byte of the digest encodes as two hex characters. Defaults to 64 (32-byte digest).

64

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

Example
>>> from coola.hashing import hash_bytes
>>> len(hash_bytes(b"hello", length=16))
16

coola.hashing.hash_enum

hash_enum(member: Enum, length: int = 64) -> str

Compute a hash of an enum member.

The hash depends on the qualified name of the enum class and on the member name, not on the member value, so it is stable and does not collapse IntEnum/StrEnum members into int/str.

Parameters:

Name Type Description Default
member Enum

The enum member to hash.

required
length int

The desired length of the returned hex string. See hash_string for constraints.

64

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Example
>>> from enum import Enum
>>> from coola.hashing import hash_enum
>>> class Color(Enum):
...     RED = 1
...
>>> len(hash_enum(Color.RED))
64

coola.hashing.hash_object

hash_object(
    data: object,
    registry: HasherRegistry | None = None,
    length: int = 64,
    ignore_unhashable: bool = False,
) -> str

Compute a hash of a nested data structure.

The hash is deterministic and depends on the type of every value, on the order of ordered containers, and not on the order of unordered ones. For example [1, 2], (1, 2) and {1, 2} all hash differently, while {"a": 1, "b": 2} and {"b": 2, "a": 1} hash the same. An OrderedDict is the exception among mappings: the order of its keys matters, so two OrderedDict with the same items in a different order hash differently.

Parameters:

Name Type Description Default
data object

The data to hash. Can be a nested structure such as a list, dict, or tuple.

required
registry HasherRegistry | None

The registry used to resolve hashers for each data type. If None, the default registry is used.

None
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
ignore_unhashable bool

If True, objects for which no hasher is registered (including nested objects found while hashing sequences or mappings) are replaced by a deterministic placeholder hash instead of raising an error. The placeholder only depends on the type, so it is lossy, and a UserWarning is emitted each time one is used. Defaults to False, which raises a KeyError when an unhashable object is encountered.

False

Returns:

Type Description
str

A string representing the hash of the input data.

Raises:

Type Description
KeyError

If data (or a nested object within it) has a type for which no hasher is registered and ignore_unhashable is False.

ValueError

If length is not an even number between 2 and 128, or if data contains itself (a self-referencing container).

Example
>>> from coola.hashing import hash_object
>>> len(hash_object({"a": 1, "b": "abc"}))
64

coola.hashing.hash_path

hash_path(
    path: PurePath, length: int = 64, resolve: bool = False
) -> str

Compute a stable hash string for a path, based on its POSIX string representation.

By default the path is hashed as written, without any filesystem access. With resolve=True the path is resolved first (following symlinks, collapsing ./.. segments, making it absolute), so that two differently written but logically equivalent paths hash the same. If resolution fails (e.g. permission error, OS-level issue), falls back to hashing the unresolved path as given.

Uses as_posix() rather than str(path) so the hash is consistent across platforms (POSIX vs Windows separators).

Parameters:

Name Type Description Default
path PurePath

The path to hash.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
resolve bool

If True, resolve the path before hashing. This makes the hash depend on the filesystem and on the current working directory. Only supported for pathlib.Path. Defaults to False.

False

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

coola.hashing.hash_pydantic_model

hash_pydantic_model(
    model: BaseModel,
    *,
    length: int = 64,
    on_secret: Literal[
        "reveal", "exclude", "error"
    ] = "error",
    registry: HasherRegistry | None = None,
    ignore_unhashable: bool = False,
) -> str

Compute a stable content hash of a pydantic model.

The hash combines the qualified class name of the model, so two different model classes with the same field values hash differently, with the hash of a mapping from field name to field value (extra fields included). Every value is hashed through the registry, so it follows the same rules as any other data (type sensitivity, order-insensitive sets, ...), and the hash is stable regardless of field declaration order.

Parameters:

Name Type Description Default
model BaseModel

The pydantic model instance to hash.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive. Defaults to 64.

64
on_secret Literal['reveal', 'exclude', 'error']

How SecretStr/SecretBytes fields are handled - see unwrap_secrets for details. Defaults to "error" so secret fields are never silently included/excluded/masked without an explicit decision.

'error'
registry HasherRegistry | None

The registry used to hash the field values. If None, the default registry is used.

None
ignore_unhashable bool

If True, field values for which no hasher is registered are replaced by a deterministic placeholder hash instead of raising an error.

False

Returns:

Type Description
str

The hex digest of the hash, as a string.

Raises:

Type Description
ValueError

If on_secret="error" (the default) and the model contains a SecretStr/SecretBytes field, or if length is not an even number between 2 and 128.

KeyError

If a field value has a type for which no hasher is registered in registry and ignore_unhashable is False.

Example
>>> from pydantic import BaseModel, SecretStr
>>> from coola.hashing import hash_pydantic_model
>>> class Creds(BaseModel):
...     username: str
...     password: SecretStr
...
>>> creds = Creds(username="alice", password="hunter2")
>>> hash_pydantic_model(creds, on_secret="exclude") == hash_pydantic_model(
...     Creds(username="alice", password="different"), on_secret="exclude"
... )
True

coola.hashing.hash_string

hash_string(value: str, length: int = 64) -> str

Generate a hexadecimal hash of a string using BLAKE2b.

Parameters:

Name Type Description Default
value str

The input string to hash.

required
length int

The desired length of the returned hex string. Must be an even number between 2 and 128 inclusive, since each byte of the digest encodes as two hex characters. Defaults to 64 (32-byte digest).

64

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length characters.

Raises:

Type Description
ValueError

If length is not an even number between 2 and 128.

Example
>>> from coola.hashing import hash_string
>>> len(hash_string("hello", length=16))
16

coola.hashing.hash_tagged

hash_tagged(tag: str, value: str, length: int = 64) -> str

Compute a hash of value namespaced by a type tag.

The tag is mixed into the digest input so that values of different types that have the same textual form (e.g. 1 and '1', or an empty list and an empty string) do not collide.

Parameters:

Name Type Description Default
tag str

The type tag, e.g. "int" or "list".

required
value str

The text to hash.

required
length int

The desired length of the returned hex string. See hash_string for constraints.

64

Returns:

Type Description
str

A lowercase hexadecimal string of exactly length

str

characters.

coola.hashing.register_hashers

register_hashers(
    mapping: Mapping[type, Hasher[Any]],
    exist_ok: bool = False,
) -> None

Register custom hashers into the default global registry.

This function extends the singleton registry returned by get_default_registry with additional type-to-hasher mappings.

Parameters:

Name Type Description Default
mapping Mapping[type, Hasher[Any]]

Mapping of Python types to hasher instances.

required
exist_ok bool

If False (default), raises an error if any type is already registered. If True, overwrites existing registrations silently.

False

Raises:

Type Description
AlreadyRegisteredError

If any type is already registered and exist_ok is False.

Example
>>> from coola.hashing import register_hashers, StrHasher
>>> class MyType:
...     def __init__(self, value):
...         self.value = value
...
>>> register_hashers({MyType: StrHasher()})