From 166f99d4b7993c01eb6bc0d20f80733f49be2987 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 1 Nov 2020 22:54:47 +0800 Subject: [PATCH 01/15] Allow union of genericalias objects --- Include/internal/pycore_unionobject.h | 1 + Lib/test/test_types.py | 13 +++++++++++++ Objects/genericaliasobject.c | 2 ++ Objects/unionobject.c | 7 ++++--- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_unionobject.h b/Include/internal/pycore_unionobject.h index fa8ba6ed944c1a..6b9efb618eddc6 100644 --- a/Include/internal/pycore_unionobject.h +++ b/Include/internal/pycore_unionobject.h @@ -10,6 +10,7 @@ extern "C" { PyAPI_FUNC(PyObject *) _Py_Union(PyObject *args); PyAPI_DATA(PyTypeObject) _Py_UnionType; +PyAPI_DATA(PyNumberMethods) _Py_union_as_number; #ifdef __cplusplus } diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 75c5eee42dc543..b5d125b7eeb189 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -713,6 +713,19 @@ def test_or_type_repr(self): assert repr(int | None) == "int | None" assert repr(int | typing.GenericAlias(list, int)) == "int | list[int]" + def test_or_type_operator_with_genericalias(self): + a = list[int] + b = list[str] + c = dict[float, str] + # equivalence with typing.Union + self.assertEqual(a | b | c, typing.Union[a, b, c]) + # de-duplicate + self.assertEqual(a | c | b | b | a | c, a | b | c) + # order shouldn't matter + self.assertEqual(a | b, b | a) + self.assertEqual(repr(a | b | c), + "list[int] | list[str] | dict[float, str]") + def test_ellipsis_type(self): self.assertIsInstance(Ellipsis, types.EllipsisType) diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 6508c69cbf7e36..6b2dd822d9c18b 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_object.h" +#include "pycore_unionobject.h" // _Py_Union() #include "structmember.h" // PyMemberDef typedef struct { @@ -586,6 +587,7 @@ PyTypeObject Py_GenericAliasType = { .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, + .tp_as_number = &_Py_union_as_number, // allow X | Y of GenericAlias objs .tp_as_mapping = &ga_as_mapping, .tp_hash = ga_hash, .tp_call = ga_call, diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 1b7f8ab51a4ce2..361898a87443b6 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -237,7 +237,8 @@ dedup_and_flatten_args(PyObject* args) PyObject* i_element = PyTuple_GET_ITEM(args, i); for (Py_ssize_t j = i + 1; j < arg_length; j++) { PyObject* j_element = PyTuple_GET_ITEM(args, j); - if (i_element == j_element) { + // RichCompare to also deduplicate GenericAlias types + if (PyObject_RichCompareBool(i_element, j_element, Py_EQ) == 1) { is_duplicate = 1; } } @@ -403,7 +404,7 @@ static PyMethodDef union_methods[] = { {"__subclasscheck__", union_subclasscheck, METH_O}, {0}}; -static PyNumberMethods union_as_number = { +PyNumberMethods _Py_union_as_number = { .nb_or = (binaryfunc)type_or, // Add __or__ function }; @@ -423,7 +424,7 @@ PyTypeObject _Py_UnionType = { .tp_members = union_members, .tp_methods = union_methods, .tp_richcompare = union_richcompare, - .tp_as_number = &union_as_number, + .tp_as_number = &_Py_union_as_number, .tp_repr = union_repr, }; From 9fc320a949a2562eabd5743e5a801c11a5c211d5 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:31:23 +0800 Subject: [PATCH 02/15] only use slower richcompare for GA objects --- Objects/unionobject.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 361898a87443b6..8e394c48ee18f8 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -237,10 +237,18 @@ dedup_and_flatten_args(PyObject* args) PyObject* i_element = PyTuple_GET_ITEM(args, i); for (Py_ssize_t j = i + 1; j < arg_length; j++) { PyObject* j_element = PyTuple_GET_ITEM(args, j); - // RichCompare to also deduplicate GenericAlias types - if (PyObject_RichCompareBool(i_element, j_element, Py_EQ) == 1) { - is_duplicate = 1; + // RichCompare to also deduplicate GenericAlias types (slower) + if (Py_TYPE(i_element) == &Py_GenericAliasType) { + if (PyObject_RichCompareBool(i_element, j_element, Py_EQ) == 1) { + is_duplicate = 1; + } } + else { + if (i_element == j_element) { + is_duplicate = 1; + } + } + } if (!is_duplicate) { Py_INCREF(i_element); From fd1a214458512bc17d56e9e79a6d07d4a1e4086e Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:36:08 +0800 Subject: [PATCH 03/15] Add news --- .../2020-11-01-23-34-56.bpo-42233.zOSzja.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst new file mode 100644 index 00000000000000..d60629192e61af --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst @@ -0,0 +1,4 @@ +Allow `GenericAlias` objects to use :ref:`union type expressions `. +This allows expressions like ``list[int] | dict[float,str]`` where previously a +``TypeError`` would have been thrown. This also fixes union type expressions +not de-duplicating `GenericAlias` objects. (Contributed by Ken Jin in :bpo:`42233`.) From 7bb6fab093e4d22cf0ae780ab17ba56429bf9911 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:49:44 +0800 Subject: [PATCH 04/15] fix doc errors --- .../Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst index d60629192e61af..75c36db955fda4 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst @@ -1,4 +1,5 @@ Allow `GenericAlias` objects to use :ref:`union type expressions `. This allows expressions like ``list[int] | dict[float,str]`` where previously a ``TypeError`` would have been thrown. This also fixes union type expressions -not de-duplicating `GenericAlias` objects. (Contributed by Ken Jin in :bpo:`42233`.) +not de-duplicating `GenericAlias` objects. (Contributed by Ken Jin in +:issue:`42233`.) From 548cd78aa20942a7f07218772adc34911c33fa40 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:51:06 +0800 Subject: [PATCH 05/15] fix error in comments --- Objects/genericaliasobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 6b2dd822d9c18b..9a5bb1cd1e64e7 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -2,7 +2,7 @@ #include "Python.h" #include "pycore_object.h" -#include "pycore_unionobject.h" // _Py_Union() +#include "pycore_unionobject.h" // _Py_union_as_number #include "structmember.h" // PyMemberDef typedef struct { From 6430371b6566f1662b846a3b37528ca1888e3ab5 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 2 Nov 2020 02:37:27 +0800 Subject: [PATCH 06/15] expose func rather than struct --- Include/internal/pycore_unionobject.h | 2 +- Objects/genericaliasobject.c | 6 +++++- Objects/typeobject.c | 14 ++------------ Objects/unionobject.c | 10 +++++----- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Include/internal/pycore_unionobject.h b/Include/internal/pycore_unionobject.h index 6b9efb618eddc6..0dce7c70a5c334 100644 --- a/Include/internal/pycore_unionobject.h +++ b/Include/internal/pycore_unionobject.h @@ -10,7 +10,7 @@ extern "C" { PyAPI_FUNC(PyObject *) _Py_Union(PyObject *args); PyAPI_DATA(PyTypeObject) _Py_UnionType; -PyAPI_DATA(PyNumberMethods) _Py_union_as_number; +PyAPI_FUNC(PyObject *) _Py_union_type_or(PyTypeObject* self, PyObject* param); #ifdef __cplusplus } diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 9a5bb1cd1e64e7..43edb197180e1b 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -574,6 +574,10 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Py_GenericAlias(origin, arguments); } +static PyNumberMethods union_as_number = { + .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function +}; + // TODO: // - argument clinic? // - __doc__? @@ -587,7 +591,7 @@ PyTypeObject Py_GenericAliasType = { .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, - .tp_as_number = &_Py_union_as_number, // allow X | Y of GenericAlias objs + .tp_as_number = &union_as_number, // allow X | Y of GenericAlias objs .tp_as_mapping = &ga_as_mapping, .tp_hash = ga_hash, .tp_call = ga_call, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bd1587ace876f5..f1a755fb3b8de5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6,7 +6,7 @@ #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() -#include "pycore_unionobject.h" // _Py_Union() +#include "pycore_unionobject.h" // _Py_Union(), _Py_union_type_or #include "frameobject.h" #include "structmember.h" // PyMemberDef @@ -3747,19 +3747,9 @@ type_is_gc(PyTypeObject *type) return type->tp_flags & Py_TPFLAGS_HEAPTYPE; } -static PyObject * -type_or(PyTypeObject* self, PyObject* param) { - PyObject *tuple = PyTuple_Pack(2, self, param); - if (tuple == NULL) { - return NULL; - } - PyObject *new_union = _Py_Union(tuple); - Py_DECREF(tuple); - return new_union; -} static PyNumberMethods type_as_number = { - .nb_or = (binaryfunc)type_or, // Add __or__ function + .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function }; PyTypeObject PyType_Type = { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 8e394c48ee18f8..9534fcd1237dac 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -299,8 +299,8 @@ is_unionable(PyObject *obj) type == &_Py_UnionType); } -static PyObject * -type_or(PyTypeObject* self, PyObject* param) +PyObject * +_Py_union_type_or(PyTypeObject* self, PyObject* param) { PyObject *tuple = PyTuple_Pack(2, self, param); if (tuple == NULL) { @@ -412,8 +412,8 @@ static PyMethodDef union_methods[] = { {"__subclasscheck__", union_subclasscheck, METH_O}, {0}}; -PyNumberMethods _Py_union_as_number = { - .nb_or = (binaryfunc)type_or, // Add __or__ function +static PyNumberMethods union_as_number = { + .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function }; PyTypeObject _Py_UnionType = { @@ -432,7 +432,7 @@ PyTypeObject _Py_UnionType = { .tp_members = union_members, .tp_methods = union_methods, .tp_richcompare = union_richcompare, - .tp_as_number = &_Py_union_as_number, + .tp_as_number = &union_as_number, .tp_repr = union_repr, }; From 891041121ab26150fd4e3ea0e8db41ce9aaee86c Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 2 Nov 2020 12:29:38 +0800 Subject: [PATCH 07/15] minor nit -- only use slower richcompare when both operands are genericalias --- Objects/unionobject.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 9534fcd1237dac..b6dcad098a4820 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -238,7 +238,9 @@ dedup_and_flatten_args(PyObject* args) for (Py_ssize_t j = i + 1; j < arg_length; j++) { PyObject* j_element = PyTuple_GET_ITEM(args, j); // RichCompare to also deduplicate GenericAlias types (slower) - if (Py_TYPE(i_element) == &Py_GenericAliasType) { + if (Py_TYPE(i_element) == &Py_GenericAliasType && + Py_TYPE(j_element) == &Py_GenericAliasType) + { if (PyObject_RichCompareBool(i_element, j_element, Py_EQ) == 1) { is_duplicate = 1; } From 1c904d7da7b01f16377b3aea98bfe723f06856e6 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 2 Nov 2020 18:12:53 +0800 Subject: [PATCH 08/15] rst typos --- .../2020-11-01-23-34-56.bpo-42233.zOSzja.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst index 75c36db955fda4..9b0cdcb5ac1040 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst @@ -1,5 +1,5 @@ -Allow `GenericAlias` objects to use :ref:`union type expressions `. +Allow ``GenericAlias`` objects to use :ref:`union type expressions `. This allows expressions like ``list[int] | dict[float,str]`` where previously a -``TypeError`` would have been thrown. This also fixes union type expressions -not de-duplicating `GenericAlias` objects. (Contributed by Ken Jin in +``TypeError`` would have been thrown. This also fixes union type expressions +not de-duplicating ``GenericAlias`` objects. (Contributed by Ken Jin in :issue:`42233`.) From 84e809a26350e98db87e83a2a07d268a04f64688 Mon Sep 17 00:00:00 2001 From: kj <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 Nov 2020 12:33:13 +0800 Subject: [PATCH 09/15] change struct name --- Objects/genericaliasobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 43edb197180e1b..28ea487a44f55f 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -574,7 +574,7 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Py_GenericAlias(origin, arguments); } -static PyNumberMethods union_as_number = { +static PyNumberMethods ga_as_number = { .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function }; @@ -591,7 +591,7 @@ PyTypeObject Py_GenericAliasType = { .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, - .tp_as_number = &union_as_number, // allow X | Y of GenericAlias objs + .tp_as_number = &ga_as_number, // allow X | Y of GenericAlias objs .tp_as_mapping = &ga_as_mapping, .tp_hash = ga_hash, .tp_call = ga_call, From aef6cb47e3324e3ef3f8fb2d5ab15a40d6d6fff1 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 8 Nov 2020 13:03:46 +0800 Subject: [PATCH 10/15] Update unionobject.c --- Objects/unionobject.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index b6dcad098a4820..a1b2facbc6d6f2 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -240,10 +240,23 @@ dedup_and_flatten_args(PyObject* args) // RichCompare to also deduplicate GenericAlias types (slower) if (Py_TYPE(i_element) == &Py_GenericAliasType && Py_TYPE(j_element) == &Py_GenericAliasType) - { - if (PyObject_RichCompareBool(i_element, j_element, Py_EQ) == 1) { + { + int same = PyObject_RichCompareBool(i_element, j_element, + Py_EQ); + if (same == 1) { is_duplicate = 1; } + else if (same == -1) { + PyErr_Format(PyExc_TypeError, + "Could not compare objects of type '%s' and '%s'" + " at indexes %d and %d respectively." + " Their __eq__ methods may be invalid. ", + Py_TYPE(i_element)->tp_name, + Py_TYPE(j_element)->tp_name, + i, j); + Py_DECREF(args); + return NULL; + } } else { if (i_element == j_element) { From ef589f92f5e4a623b098c5a702bf774fec03b3b9 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 8 Nov 2020 21:37:55 +0800 Subject: [PATCH 11/15] clean up code --- Objects/unionobject.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index a1b2facbc6d6f2..000e1a938b0546 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -237,33 +237,25 @@ dedup_and_flatten_args(PyObject* args) PyObject* i_element = PyTuple_GET_ITEM(args, i); for (Py_ssize_t j = i + 1; j < arg_length; j++) { PyObject* j_element = PyTuple_GET_ITEM(args, j); + int is_ga = Py_TYPE(i_element) == &Py_GenericAliasType && + Py_TYPE(j_element) == &Py_GenericAliasType; // RichCompare to also deduplicate GenericAlias types (slower) - if (Py_TYPE(i_element) == &Py_GenericAliasType && - Py_TYPE(j_element) == &Py_GenericAliasType) - { - int same = PyObject_RichCompareBool(i_element, j_element, - Py_EQ); - if (same == 1) { - is_duplicate = 1; - } - else if (same == -1) { - PyErr_Format(PyExc_TypeError, - "Could not compare objects of type '%s' and '%s'" - " at indexes %d and %d respectively." - " Their __eq__ methods may be invalid. ", - Py_TYPE(i_element)->tp_name, - Py_TYPE(j_element)->tp_name, - i, j); - Py_DECREF(args); - return NULL; - } + int is_same = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ) + : i_element == j_element; + // Should only happen if RichCompare fails + if (is_same < 0) { + PyErr_Format(PyExc_TypeError, + "Could not compare objects of type '%s' and '%s'" + " at indexes %d and %d respectively." + " Their __eq__ methods may be invalid. ", + Py_TYPE(i_element)->tp_name, Py_TYPE(j_element)->tp_name, + i, j); + Py_DECREF(args); + return NULL; } - else { - if (i_element == j_element) { - is_duplicate = 1; - } - } - + is_duplicate = is_same; + if (is_duplicate) + break; } if (!is_duplicate) { Py_INCREF(i_element); From 6cc85b8f43aad7c295e18b8c8fbc2169c973cdaa Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 8 Nov 2020 23:47:26 +0800 Subject: [PATCH 12/15] remove unused variable --- Objects/unionobject.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 000e1a938b0546..1c950e8b96b4ba 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -240,10 +240,10 @@ dedup_and_flatten_args(PyObject* args) int is_ga = Py_TYPE(i_element) == &Py_GenericAliasType && Py_TYPE(j_element) == &Py_GenericAliasType; // RichCompare to also deduplicate GenericAlias types (slower) - int is_same = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ) + is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ) : i_element == j_element; // Should only happen if RichCompare fails - if (is_same < 0) { + if (is_duplicate < 0) { PyErr_Format(PyExc_TypeError, "Could not compare objects of type '%s' and '%s'" " at indexes %d and %d respectively." @@ -253,7 +253,6 @@ dedup_and_flatten_args(PyObject* args) Py_DECREF(args); return NULL; } - is_duplicate = is_same; if (is_duplicate) break; } From abe9a8d0841e325c9ded272a7af1a518d7f4393b Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 9 Nov 2020 09:19:59 +0800 Subject: [PATCH 13/15] remove extraneous error raising --- Objects/unionobject.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 1c950e8b96b4ba..9b79787f427108 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -244,12 +244,6 @@ dedup_and_flatten_args(PyObject* args) : i_element == j_element; // Should only happen if RichCompare fails if (is_duplicate < 0) { - PyErr_Format(PyExc_TypeError, - "Could not compare objects of type '%s' and '%s'" - " at indexes %d and %d respectively." - " Their __eq__ methods may be invalid. ", - Py_TYPE(i_element)->tp_name, Py_TYPE(j_element)->tp_name, - i, j); Py_DECREF(args); return NULL; } From 64d6851cd72c5acfe1330ac829cf0fa1e5496dcb Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 9 Nov 2020 11:01:40 +0800 Subject: [PATCH 14/15] 3rd round of changes implemented --- Lib/test/test_types.py | 9 +++++++++ .../2020-11-01-23-34-56.bpo-42233.zOSzja.rst | 2 +- Objects/typeobject.c | 2 +- Objects/unionobject.c | 5 +++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index b5d125b7eeb189..3058a02d6eeb4a 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -726,6 +726,15 @@ def test_or_type_operator_with_genericalias(self): self.assertEqual(repr(a | b | c), "list[int] | list[str] | dict[float, str]") + class BadType(type): + def __eq__(self, other): + return 1 / 0 + + bt = BadType('bt', (), {}) + # Comparison should fail and errors should propagate out for bad types. + with self.assertRaises(ZeroDivisionError): + list[int] | list[bt] + def test_ellipsis_type(self): self.assertIsInstance(Ellipsis, types.EllipsisType) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst index 9b0cdcb5ac1040..499bb324fb935e 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-01-23-34-56.bpo-42233.zOSzja.rst @@ -1,5 +1,5 @@ Allow ``GenericAlias`` objects to use :ref:`union type expressions `. -This allows expressions like ``list[int] | dict[float,str]`` where previously a +This allows expressions like ``list[int] | dict[float, str]`` where previously a ``TypeError`` would have been thrown. This also fixes union type expressions not de-duplicating ``GenericAlias`` objects. (Contributed by Ken Jin in :issue:`42233`.) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f1a755fb3b8de5..7d5e2afc7a63f9 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3749,7 +3749,7 @@ type_is_gc(PyTypeObject *type) static PyNumberMethods type_as_number = { - .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function + .nb_or = _Py_union_type_or, // Add __or__ function }; PyTypeObject PyType_Type = { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 9b79787f427108..2308bfc9f2a278 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -245,6 +245,7 @@ dedup_and_flatten_args(PyObject* args) // Should only happen if RichCompare fails if (is_duplicate < 0) { Py_DECREF(args); + Py_DECREF(new_args); return NULL; } if (is_duplicate) @@ -300,7 +301,7 @@ is_unionable(PyObject *obj) } PyObject * -_Py_union_type_or(PyTypeObject* self, PyObject* param) +_Py_union_type_or(PyObject* self, PyObject* param) { PyObject *tuple = PyTuple_Pack(2, self, param); if (tuple == NULL) { @@ -413,7 +414,7 @@ static PyMethodDef union_methods[] = { {0}}; static PyNumberMethods union_as_number = { - .nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function + .nb_or = _Py_union_type_or, // Add __or__ function }; PyTypeObject _Py_UnionType = { From 36c21556b5382154de70c2b3dcb19c127c4ec016 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 9 Nov 2020 11:10:09 +0800 Subject: [PATCH 15/15] fix header declaration --- Include/internal/pycore_unionobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_unionobject.h b/Include/internal/pycore_unionobject.h index 0dce7c70a5c334..4d82b6fbeae8b5 100644 --- a/Include/internal/pycore_unionobject.h +++ b/Include/internal/pycore_unionobject.h @@ -10,7 +10,7 @@ extern "C" { PyAPI_FUNC(PyObject *) _Py_Union(PyObject *args); PyAPI_DATA(PyTypeObject) _Py_UnionType; -PyAPI_FUNC(PyObject *) _Py_union_type_or(PyTypeObject* self, PyObject* param); +PyAPI_FUNC(PyObject *) _Py_union_type_or(PyObject* self, PyObject* param); #ifdef __cplusplus }