bpo-33134: dataclasses: use function dispatch table for hash, instead… · python/cpython@01d618c (original) (raw)
`@@ -585,14 +585,24 @@ def _set_new_attribute(cls, name, value):
`
585
585
`return False
`
586
586
``
587
587
``
``
588
+
588
589
`# Decide if/how we're going to create a hash function. Key is
`
589
590
`# (unsafe_hash, eq, frozen, does-hash-exist). Value is the action to
`
590
``
`-
take.
`
591
``
`-
Actions:
`
592
``
`-
'': Do nothing.
`
593
``
`-
'none': Set hash to None.
`
594
``
`-
'add': Always add a generated __hash__function.
`
595
``
`-
'exception': Raise an exception.
`
``
591
`+
take. The common case is to do nothing, so instead of providing a
`
``
592
`+
function that is a no-op, use None to signify that.
`
``
593
+
``
594
`+
def _hash_set_none(cls, fields):
`
``
595
`+
return None
`
``
596
+
``
597
`+
def _hash_add(cls, fields):
`
``
598
`+
flds = [f for f in fields if (f.compare if f.hash is None else f.hash)]
`
``
599
`+
return _hash_fn(flds)
`
``
600
+
``
601
`+
def _hash_exception(cls, fields):
`
``
602
`+
Raise an exception.
`
``
603
`+
raise TypeError(f'Cannot overwrite attribute hash '
`
``
604
`+
f'in class {cls.name}')
`
``
605
+
596
606
`#
`
597
607
`# +-------------------------------------- unsafe_hash?
`
598
608
`# | +------------------------------- eq?
`
`@@ -602,22 +612,22 @@ def _set_new_attribute(cls, name, value):
`
602
612
`# | | | | +------- action
`
603
613
`# | | | | |
`
604
614
`# v v v v v
`
605
``
`-
_hash_action = {(False, False, False, False): (''),
`
606
``
`-
(False, False, False, True ): (''),
`
607
``
`-
(False, False, True, False): (''),
`
608
``
`-
(False, False, True, True ): (''),
`
609
``
`-
(False, True, False, False): ('none'),
`
610
``
`-
(False, True, False, True ): (''),
`
611
``
`-
(False, True, True, False): ('add'),
`
612
``
`-
(False, True, True, True ): (''),
`
613
``
`-
(True, False, False, False): ('add'),
`
614
``
`-
(True, False, False, True ): ('exception'),
`
615
``
`-
(True, False, True, False): ('add'),
`
616
``
`-
(True, False, True, True ): ('exception'),
`
617
``
`-
(True, True, False, False): ('add'),
`
618
``
`-
(True, True, False, True ): ('exception'),
`
619
``
`-
(True, True, True, False): ('add'),
`
620
``
`-
(True, True, True, True ): ('exception'),
`
``
615
`+
_hash_action = {(False, False, False, False): None,
`
``
616
`+
(False, False, False, True ): None,
`
``
617
`+
(False, False, True, False): None,
`
``
618
`+
(False, False, True, True ): None,
`
``
619
`+
(False, True, False, False): _hash_set_none,
`
``
620
`+
(False, True, False, True ): None,
`
``
621
`+
(False, True, True, False): _hash_add,
`
``
622
`+
(False, True, True, True ): None,
`
``
623
`+
(True, False, False, False): _hash_add,
`
``
624
`+
(True, False, False, True ): _hash_exception,
`
``
625
`+
(True, False, True, False): _hash_add,
`
``
626
`+
(True, False, True, True ): _hash_exception,
`
``
627
`+
(True, True, False, False): _hash_add,
`
``
628
`+
(True, True, False, True ): _hash_exception,
`
``
629
`+
(True, True, True, False): _hash_add,
`
``
630
`+
(True, True, True, True ): _hash_exception,
`
621
631
` }
`
622
632
`# See https://bugs.python.org/issue32929#msg312829 for an if-statement
`
623
633
`# version of this table.
`
`@@ -774,7 +784,6 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
`
774
784
`'functools.total_ordering')
`
775
785
``
776
786
`if frozen:
`
777
``
`-
XXX: Which fields are frozen? InitVar? ClassVar? hashed-only?
`
778
787
`for fn in _frozen_get_del_attr(cls, field_list):
`
779
788
`if _set_new_attribute(cls, fn.name, fn):
`
780
789
`raise TypeError(f'Cannot overwrite attribute {fn.name} '
`
`@@ -785,23 +794,10 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
`
785
794
`bool(eq),
`
786
795
`bool(frozen),
`
787
796
`has_explicit_hash]
`
788
``
-
789
``
`-
No need to call _set_new_attribute here, since we already know if
`
790
``
`-
we're overwriting a hash or not.
`
791
``
`-
if hash_action == '':
`
792
``
`-
Do nothing.
`
793
``
`-
pass
`
794
``
`-
elif hash_action == 'none':
`
795
``
`-
cls.hash = None
`
796
``
`-
elif hash_action == 'add':
`
797
``
`-
flds = [f for f in field_list if (f.compare if f.hash is None else f.hash)]
`
798
``
`-
cls.hash = _hash_fn(flds)
`
799
``
`-
elif hash_action == 'exception':
`
800
``
`-
Raise an exception.
`
801
``
`-
raise TypeError(f'Cannot overwrite attribute hash '
`
802
``
`-
f'in class {cls.name}')
`
803
``
`-
else:
`
804
``
`-
assert False, f"can't get here: {hash_action}"
`
``
797
`+
if hash_action:
`
``
798
`+
No need to call _set_new_attribute here, since by the time
`
``
799
`+
we're here the overwriting is unconditional.
`
``
800
`+
cls.hash = hash_action(cls, field_list)
`
805
801
``
806
802
`if not getattr(cls, 'doc'):
`
807
803
`# Create a class doc-string.
`