Skip to content

Commit d9f9232

Browse files
bpo-44636: Collapse union of equal types (GH-27178)
The result of `int | int` is now `int`. Fix comparison of the union type with non-hashable objects. `int | str == {}` no longer raises a TypeError.
1 parent aeaa553 commit d9f9232

File tree

3 files changed

+23
-9
lines changed

3 files changed

+23
-9
lines changed

Lib/test/test_types.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ def test_or_types_operator(self):
623623
self.assertEqual(None | typing.List[int], typing.Union[None, typing.List[int]])
624624
self.assertEqual(str | float | int | complex | int, (int | str) | (float | complex))
625625
self.assertEqual(typing.Union[str, int, typing.List[int]], str | int | typing.List[int])
626-
self.assertEqual(int | int, int)
626+
self.assertIs(int | int, int)
627627
self.assertEqual(
628628
BaseException |
629629
bool |
@@ -651,6 +651,8 @@ def test_or_types_operator(self):
651651
3 | int
652652
with self.assertRaises(TypeError):
653653
Example() | int
654+
x = int | str
655+
self.assertNotEqual(x, {})
654656
with self.assertRaises(TypeError):
655657
(int | str) < typing.Union[str, int]
656658
with self.assertRaises(TypeError):
@@ -704,6 +706,8 @@ def test_or_type_operator_with_TypeVar(self):
704706
TV = typing.TypeVar('T')
705707
assert TV | str == typing.Union[TV, str]
706708
assert str | TV == typing.Union[str, TV]
709+
self.assertIs((int | TV)[int], int)
710+
self.assertIs((TV | int)[int], int)
707711

708712
def test_union_args(self):
709713
self.assertEqual((int | str).__args__, (int, str))
@@ -721,6 +725,7 @@ def test_union_parameter_chaining(self):
721725
self.assertEqual(list[int | list[T]][str], list[int | list[str]])
722726
self.assertEqual((list[T] | list[S]).__parameters__, (T, S))
723727
self.assertEqual((list[T] | list[S])[int, T], list[int] | list[T])
728+
self.assertEqual((list[T] | list[S])[int, int], list[int])
724729

725730
def test_or_type_operator_with_forward(self):
726731
T = typing.TypeVar('T')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Collapse union of equal types. E.g. the result of ``int | int`` is now ``int``. Fix comparison of the union type with non-hashable objects. E.g. ``int | str == {}`` no longer raises a TypeError.

Objects/unionobject.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,9 @@ union_richcompare(PyObject *a, PyObject *b, int op)
187187
}
188188
}
189189
} else {
190-
if (PySet_Add(b_set, b) == -1) {
191-
goto exit;
192-
}
190+
Py_DECREF(a_set);
191+
Py_DECREF(b_set);
192+
Py_RETURN_NOTIMPLEMENTED;
193193
}
194194
result = PyObject_RichCompare(a_set, b_set, op);
195195
exit:
@@ -551,17 +551,25 @@ _Py_Union(PyObject *args)
551551
}
552552
}
553553

554+
args = dedup_and_flatten_args(args);
555+
if (args == NULL) {
556+
return NULL;
557+
}
558+
if (PyTuple_GET_SIZE(args) == 1) {
559+
PyObject *result1 = PyTuple_GET_ITEM(args, 0);
560+
Py_INCREF(result1);
561+
Py_DECREF(args);
562+
return result1;
563+
}
564+
554565
result = PyObject_GC_New(unionobject, &_Py_UnionType);
555566
if (result == NULL) {
567+
Py_DECREF(args);
556568
return NULL;
557569
}
558570

559571
result->parameters = NULL;
560-
result->args = dedup_and_flatten_args(args);
572+
result->args = args;
561573
_PyObject_GC_TRACK(result);
562-
if (result->args == NULL) {
563-
Py_DECREF(result);
564-
return NULL;
565-
}
566574
return (PyObject*)result;
567575
}

0 commit comments

Comments
 (0)