@@ -585,14 +585,24 @@ def _set_new_attribute(cls, name, value):
585585 return False
586586
587587
588+
588589# Decide if/how we're going to create a hash function. Key is
589590# (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+
596606#
597607# +-------------------------------------- unsafe_hash?
598608# | +------------------------------- eq?
@@ -602,22 +612,22 @@ def _set_new_attribute(cls, name, value):
602612# | | | | +------- action
603613# | | | | |
604614# 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 ,
621631 }
622632# See https://bugs.python.org/issue32929#msg312829 for an if-statement
623633# version of this table.
@@ -774,7 +784,6 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
774784 'functools.total_ordering' )
775785
776786 if frozen :
777- # XXX: Which fields are frozen? InitVar? ClassVar? hashed-only?
778787 for fn in _frozen_get_del_attr (cls , field_list ):
779788 if _set_new_attribute (cls , fn .__name__ , fn ):
780789 raise TypeError (f'Cannot overwrite attribute { fn .__name__ } '
@@ -785,23 +794,10 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
785794 bool (eq ),
786795 bool (frozen ),
787796 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 )
805801
806802 if not getattr (cls , '__doc__' ):
807803 # Create a class doc-string.
0 commit comments