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