]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44636: Collapse union of equal types (GH-27178)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 16 Jul 2021 09:49:33 +0000 (12:49 +0300)
committerGitHub <noreply@github.com>
Fri, 16 Jul 2021 09:49:33 +0000 (12:49 +0300)
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.

Lib/test/test_types.py
Misc/NEWS.d/next/Core and Builtins/2021-07-16-09-36-12.bpo-44636.ZWebi8.rst [new file with mode: 0644]
Objects/unionobject.c

index b35555878518d7a7ca86ee665813d3fc0a1533dc..d940f1aaaf383c39f58e62564532d7baf78ffeae 100644 (file)
@@ -623,7 +623,7 @@ class TypesTests(unittest.TestCase):
         self.assertEqual(None | typing.List[int], typing.Union[None, typing.List[int]])
         self.assertEqual(str | float | int | complex | int, (int | str) | (float | complex))
         self.assertEqual(typing.Union[str, int, typing.List[int]], str | int | typing.List[int])
-        self.assertEqual(int | int, int)
+        self.assertIs(int | int, int)
         self.assertEqual(
             BaseException |
             bool |
@@ -651,6 +651,8 @@ class TypesTests(unittest.TestCase):
             3 | int
         with self.assertRaises(TypeError):
             Example() | int
+        x = int | str
+        self.assertNotEqual(x, {})
         with self.assertRaises(TypeError):
             (int | str) < typing.Union[str, int]
         with self.assertRaises(TypeError):
@@ -704,6 +706,8 @@ class TypesTests(unittest.TestCase):
         TV = typing.TypeVar('T')
         assert TV | str == typing.Union[TV, str]
         assert str | TV == typing.Union[str, TV]
+        self.assertIs((int | TV)[int], int)
+        self.assertIs((TV | int)[int], int)
 
     def test_union_args(self):
         self.assertEqual((int | str).__args__, (int, str))
@@ -721,6 +725,7 @@ class TypesTests(unittest.TestCase):
         self.assertEqual(list[int | list[T]][str], list[int | list[str]])
         self.assertEqual((list[T] | list[S]).__parameters__, (T, S))
         self.assertEqual((list[T] | list[S])[int, T], list[int] | list[T])
+        self.assertEqual((list[T] | list[S])[int, int], list[int])
 
     def test_or_type_operator_with_forward(self):
         T = typing.TypeVar('T')
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-16-09-36-12.bpo-44636.ZWebi8.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-16-09-36-12.bpo-44636.ZWebi8.rst
new file mode 100644 (file)
index 0000000..1a053ae
--- /dev/null
@@ -0,0 +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.
index 85092e667a21d418dfbaf9237972c03dbc21f0ef..dad26c32b29d1536ddfea7c5f8e3889b72ef9639 100644 (file)
@@ -187,9 +187,9 @@ union_richcompare(PyObject *a, PyObject *b, int op)
             }
         }
     } else {
-        if (PySet_Add(b_set, b) == -1) {
-            goto exit;
-        }
+        Py_DECREF(a_set);
+        Py_DECREF(b_set);
+        Py_RETURN_NOTIMPLEMENTED;
     }
     result = PyObject_RichCompare(a_set, b_set, op);
 exit:
@@ -551,17 +551,25 @@ _Py_Union(PyObject *args)
         }
     }
 
+    args = dedup_and_flatten_args(args);
+    if (args == NULL) {
+        return NULL;
+    }
+    if (PyTuple_GET_SIZE(args) == 1) {
+        PyObject *result1 = PyTuple_GET_ITEM(args, 0);
+        Py_INCREF(result1);
+        Py_DECREF(args);
+        return result1;
+    }
+
     result = PyObject_GC_New(unionobject, &_Py_UnionType);
     if (result == NULL) {
+        Py_DECREF(args);
         return NULL;
     }
 
     result->parameters = NULL;
-    result->args = dedup_and_flatten_args(args);
+    result->args = args;
     _PyObject_GC_TRACK(result);
-    if (result->args == NULL) {
-        Py_DECREF(result);
-        return NULL;
-    }
     return (PyObject*)result;
 }