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.

`