File tree Expand file tree Collapse file tree 3 files changed +50
-16
lines changed
Expand file tree Collapse file tree 3 files changed +50
-16
lines changed Original file line number Diff line number Diff line change @@ -723,10 +723,39 @@ class B(metaclass=UnhashableMeta): ...
723723
724724 self .assertEqual ((A | B ).__args__ , (A , B ))
725725 union1 = A | B
726+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
727+ hash (union1 )
728+
726729 union2 = int | B
730+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
731+ hash (union2 )
732+
727733 union3 = A | int
734+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
735+ hash (union3 )
736+
737+ def test_unhashable_becomes_hashable (self ):
738+ is_hashable = False
739+ class UnhashableMeta (type ):
740+ def __hash__ (self ):
741+ if is_hashable :
742+ return 1
743+ else :
744+ raise TypeError ("not hashable" )
745+
746+ class A (metaclass = UnhashableMeta ): ...
747+ class B (metaclass = UnhashableMeta ): ...
748+
749+ union = A | B
750+ self .assertEqual (union .__args__ , (A , B ))
751+
752+ with self .assertRaisesRegex (TypeError , "not hashable" ):
753+ hash (union )
754+
755+ is_hashable = True
728756
729- self .assertEqual (len ({union1 , union2 , union3 }), 3 )
757+ with self .assertRaisesRegex (TypeError , "union contains 2 unhashable elements" ):
758+ hash (union )
730759
731760 def test_instancecheck_and_subclasscheck (self ):
732761 for x in (int | str , typing .Union [int , str ]):
Original file line number Diff line number Diff line change @@ -2062,10 +2062,16 @@ class B(metaclass=UnhashableMeta): ...
20622062
20632063 self .assertEqual (Union [A , B ].__args__ , (A , B ))
20642064 union1 = Union [A , B ]
2065+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
2066+ hash (union1 )
2067+
20652068 union2 = Union [int , B ]
2066- union3 = Union [A , int ]
2069+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
2070+ hash (union2 )
20672071
2068- self .assertEqual (len ({union1 , union2 , union3 }), 3 )
2072+ union3 = Union [A , int ]
2073+ with self .assertRaisesRegex (TypeError , "unhashable type: 'UnhashableMeta'" ):
2074+ hash (union3 )
20692075
20702076 def test_repr (self ):
20712077 u = Union [Employee , int ]
Original file line number Diff line number Diff line change @@ -46,26 +46,25 @@ static Py_hash_t
4646union_hash (PyObject * self )
4747{
4848 unionobject * alias = (unionobject * )self ;
49- Py_hash_t hash ;
50- if (alias -> hashable_args ) {
51- hash = PyObject_Hash (alias -> hashable_args );
52- if (hash == -1 ) {
53- return -1 ;
54- }
55- }
56- else {
57- hash = 604 ;
58- }
59- // Mix in the ids of all the unhashable args.
49+ // If there are any unhashable args, treat this union as unhashable.
50+ // Otherwise, two unions might compare equal but have different hashes.
6051 if (alias -> unhashable_args ) {
52+ // Attempt to get an error from one of the values.
6153 assert (PyTuple_CheckExact (alias -> unhashable_args ));
6254 Py_ssize_t n = PyTuple_GET_SIZE (alias -> unhashable_args );
6355 for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
6456 PyObject * arg = PyTuple_GET_ITEM (alias -> unhashable_args , i );
65- hash ^= (Py_hash_t )arg ;
57+ Py_hash_t hash = PyObject_Hash (arg );
58+ if (hash == -1 ) {
59+ return -1 ;
60+ }
6661 }
62+ // The unhashable values somehow became hashable again. Still raise
63+ // an error.
64+ PyErr_Format (PyExc_TypeError , "union contains %d unhashable elements" , n );
65+ return -1 ;
6766 }
68- return hash ;
67+ return PyObject_Hash ( alias -> hashable_args ) ;
6968}
7069
7170static int
You can’t perform that action at this time.
0 commit comments