v0.150.0
 1from __future__ import annotations
 2
 3from typing import Any
 4
 5from plain.utils.itercompat import is_iterable
 6
 7
 8def make_hashable(value: Any) -> Any:
 9    """
10    Attempt to make value hashable or raise a TypeError if it fails.
11
12    The returned value should generate the same hash for equal values.
13    """
14    if isinstance(value, dict):
15        return tuple(
16            [
17                (key, make_hashable(nested_value))
18                for key, nested_value in sorted(value.items())
19            ]
20        )
21    # Try hash to avoid converting a hashable iterable (e.g. string, frozenset)
22    # to a tuple.
23    try:
24        hash(value)
25    except TypeError:
26        if is_iterable(value):
27            return tuple(map(make_hashable, value))
28        # Non-hashable, non-iterable.
29        raise
30    return value