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