Skip to content

Commit 975e7d6

Browse files
committed
Make cache_hash part of the Hashability Enum
1 parent 4f60db6 commit 975e7d6

File tree

3 files changed

+30
-23
lines changed

3 files changed

+30
-23
lines changed

src/attr/_make.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class Hashability(enum.Enum):
109109
"""
110110

111111
HASHABLE = "hashable" # write a __hash__
112+
HASHABLE_CACHED = "hashable_cache" # write a __hash__ and cache the hash
112113
UNHASHABLE = "unhashable" # set __hash__ to None
113114
LEAVE_ALONE = "leave_alone" # don't touch __hash__
114115

@@ -142,13 +143,19 @@ class ClassProps(NamedTuple):
142143
eq: bool
143144
order: bool
144145
hash: Hashability
145-
cache_hash: bool
146146
match_args: bool
147147
str: bool
148148
getstate_setstate: bool
149149
on_setattr: Callable[[str, Any], Any]
150150
field_transformer: Callable[[Attribute], Attribute]
151151

152+
@property
153+
def is_hashable(self):
154+
return (
155+
self.hash is Hashability.HASHABLE
156+
or self.hash is Hashability.HASHABLE_CACHED
157+
)
158+
152159

153160
def attrib(
154161
default=NOTHING,
@@ -730,7 +737,7 @@ def __init__(
730737
self._slots = props.is_slotted
731738
self._frozen = props.is_frozen
732739
self._weakref_slot = props.has_weakref_slot
733-
self._cache_hash = props.cache_hash
740+
self._cache_hash = props.hash is Hashability.HASHABLE_CACHED
734741
self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
735742
self._pre_init_has_args = False
736743
if self._has_pre_init:
@@ -1490,14 +1497,22 @@ def wrap(cls):
14901497
if is_exc:
14911498
hashability = Hashability.LEAVE_ALONE
14921499
elif hash is True:
1493-
hashability = Hashability.HASHABLE
1500+
hashability = (
1501+
Hashability.HASHABLE_CACHED
1502+
if cache_hash
1503+
else Hashability.HASHABLE
1504+
)
14941505
elif hash is False:
14951506
hashability = Hashability.LEAVE_ALONE
14961507
elif hash is None:
14971508
if auto_detect is True and _has_own_attribute(cls, "__hash__"):
14981509
hashability = Hashability.LEAVE_ALONE
14991510
elif eq is True and is_frozen is True:
1500-
hashability = Hashability.HASHABLE
1511+
hashability = (
1512+
Hashability.HASHABLE_CACHED
1513+
if cache_hash
1514+
else Hashability.HASHABLE
1515+
)
15011516
elif eq is False:
15021517
hashability = Hashability.LEAVE_ALONE
15031518
else:
@@ -1506,12 +1521,8 @@ def wrap(cls):
15061521
msg = "Invalid value for hash. Must be True, False, or None."
15071522
raise TypeError(msg)
15081523

1509-
if hashability is not Hashability.HASHABLE and cache_hash:
1510-
msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
1511-
raise TypeError(msg)
1512-
15131524
if kw_only:
1514-
kwo = KeywordOnly.YES if not force_kw_only else KeywordOnly.FORCE
1525+
kwo = KeywordOnly.FORCE if force_kw_only else KeywordOnly.YES
15151526
else:
15161527
kwo = KeywordOnly.NO
15171528

@@ -1538,7 +1549,6 @@ def wrap(cls):
15381549
match_args=match_args,
15391550
kw_only=kwo,
15401551
has_weakref_slot=weakref_slot,
1541-
cache_hash=cache_hash,
15421552
str=str,
15431553
getstate_setstate=_determine_whether_to_implement(
15441554
cls,
@@ -1551,6 +1561,10 @@ def wrap(cls):
15511561
field_transformer=field_transformer,
15521562
)
15531563

1564+
if not props.is_hashable and cache_hash:
1565+
msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
1566+
raise TypeError(msg)
1567+
15541568
builder = _ClassBuilder(
15551569
cls,
15561570
these,
@@ -1573,7 +1587,7 @@ def wrap(cls):
15731587
if not frozen:
15741588
builder.add_setattr()
15751589

1576-
if props.hash is Hashability.HASHABLE:
1590+
if props.is_hashable:
15771591
builder.add_hash()
15781592
elif props.hash is Hashability.UNHASHABLE:
15791593
builder.make_unhashable()

tests/test_make.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,11 @@ class C:
540540
repr=True,
541541
eq=True,
542542
order=True,
543-
hash=Hashability.HASHABLE,
543+
hash=Hashability.HASHABLE_CACHED,
544544
match_args=False,
545545
kw_only=KeywordOnly.FORCE,
546546
has_weakref_slot=True,
547547
collect_by_mro=False,
548-
cache_hash=True,
549548
str=True,
550549
getstate_setstate=True,
551550
on_setattr=None,
@@ -578,7 +577,6 @@ class CDef:
578577
kw_only=KeywordOnly.NO,
579578
has_weakref_slot=True,
580579
collect_by_mro=False,
581-
cache_hash=False,
582580
str=False,
583581
getstate_setstate=False,
584582
on_setattr=None,
@@ -2018,12 +2016,11 @@ class C:
20182016
repr=True,
20192017
eq=True,
20202018
order=False,
2021-
hash=False,
2019+
hash=Hashability.UNHASHABLE,
20222020
match_args=True,
20232021
kw_only=KeywordOnly.NO,
20242022
has_weakref_slot=False,
20252023
collect_by_mro=True,
2026-
cache_hash=False,
20272024
str=False,
20282025
getstate_setstate=True,
20292026
on_setattr=None,
@@ -2054,12 +2051,11 @@ class C:
20542051
repr=True,
20552052
eq=True,
20562053
order=False,
2057-
hash=False,
2054+
hash=Hashability.UNHASHABLE,
20582055
match_args=True,
20592056
kw_only=KeywordOnly.NO,
20602057
has_weakref_slot=False,
20612058
collect_by_mro=True,
2062-
cache_hash=False,
20632059
str=False,
20642060
getstate_setstate=True,
20652061
on_setattr=None,
@@ -2156,12 +2152,11 @@ def our_hasattr(obj, name, /) -> bool:
21562152
repr=True,
21572153
eq=True,
21582154
order=False,
2159-
hash=False,
2155+
hash=Hashability.UNHASHABLE,
21602156
match_args=True,
21612157
kw_only=KeywordOnly.NO,
21622158
has_weakref_slot=True,
21632159
collect_by_mro=True,
2164-
cache_hash=False,
21652160
str=False,
21662161
getstate_setstate=True,
21672162
on_setattr=None,

tests/test_next_gen.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,10 @@ class C:
485485
repr=True,
486486
eq=True,
487487
order=True,
488-
hash=Hashability.HASHABLE,
488+
hash=Hashability.HASHABLE_CACHED,
489489
match_args=False,
490490
has_weakref_slot=True,
491491
collect_by_mro=True,
492-
cache_hash=True,
493492
str=True,
494493
getstate_setstate=False, # because slots=False
495494
on_setattr=None,
@@ -521,7 +520,6 @@ class C:
521520
kw_only=KeywordOnly.NO,
522521
has_weakref_slot=True,
523522
collect_by_mro=True,
524-
cache_hash=False,
525523
str=False,
526524
getstate_setstate=True,
527525
on_setattr=None,

0 commit comments

Comments
 (0)