From 2e278301f86bde39a288c04a314eca36c735fea2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 17 Jul 2021 21:04:46 +0300 Subject: [PATCH 1/3] bpo-44633: Fix parameter substitution of the union type with wrong types. A TypeError is now raised instead of returning NotImplemented. --- Lib/test/test_types.py | 6 ++ .../2021-07-17-21-04-04.bpo-44633.5-zKeI.rst | 2 + Objects/unionobject.c | 59 +++++++++++-------- 3 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 2d0e33f03c3912..f9d2284aabd702 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -755,6 +755,12 @@ def test_union_parameter_chaining(self): self.assertEqual((list[T] | list[S])[int, T], list[int] | list[T]) self.assertEqual((list[T] | list[S])[int, int], list[int]) + def test_union_parameter_substitution_errors(self): + T = typing.TypeVar("T") + x = int | T + with self.assertRaises(TypeError): + x[42] + def test_or_type_operator_with_forward(self): T = typing.TypeVar('T') ForwardAfter = T | 'Forward' diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst new file mode 100644 index 00000000000000..b07331c834187c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst @@ -0,0 +1,2 @@ +Parameter substitution of the union type with wrong types raises now +``TypeError`` instead of returning ``NotImplemented``. diff --git a/Objects/unionobject.c b/Objects/unionobject.c index b3a65068626189..b870c9ee811634 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -5,6 +5,9 @@ #include "structmember.h" +static PyObject *make_union(PyObject *); + + typedef struct { PyObject_HEAD PyObject *args; @@ -337,13 +340,25 @@ is_unionable(PyObject *obj) } PyObject * -_Py_union_type_or(PyObject* self, PyObject* param) +_Py_union_type_or(PyObject* self, PyObject* other) { - PyObject *tuple = PyTuple_Pack(2, self, param); + int r = is_unionable(self); + if (r > 0) { + r = is_unionable(other); + } + if (r < 0) { + return NULL; + } + if (!r) { + Py_RETURN_NOTIMPLEMENTED; + } + + PyObject *tuple = PyTuple_Pack(2, self, other); if (tuple == NULL) { return NULL; } - PyObject *new_union = _Py_Union(tuple); + + PyObject *new_union = make_union(tuple); Py_DECREF(tuple); return new_union; } @@ -471,7 +486,22 @@ union_getitem(PyObject *self, PyObject *item) return NULL; } - PyObject *res = _Py_Union(newargs); + // Check arguments are unionable. + Py_ssize_t nargs = PyTuple_GET_SIZE(newargs); + for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(newargs, iarg); + int is_arg_unionable = is_unionable(arg); + if (is_arg_unionable <= 0) { + Py_DECREF(newargs); + if (is_arg_unionable == 0) { + PyErr_Format(PyExc_TypeError, + "Each union arg must be a type, got %.100R", arg); + } + return NULL; + } + } + + PyObject *res = make_union(newargs); Py_DECREF(newargs); return res; @@ -527,30 +557,13 @@ PyTypeObject _Py_UnionType = { .tp_getset = union_properties, }; -PyObject * -_Py_Union(PyObject *args) +static PyObject * +make_union(PyObject *args) { assert(PyTuple_CheckExact(args)); unionobject* result = NULL; - // Check arguments are unionable. - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { - PyObject *arg = PyTuple_GET_ITEM(args, iarg); - if (arg == NULL) { - return NULL; - } - int is_arg_unionable = is_unionable(arg); - if (is_arg_unionable < 0) { - return NULL; - } - if (!is_arg_unionable) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - } - args = dedup_and_flatten_args(args); if (args == NULL) { return NULL; From 19fd606d050945571d4b91e178658010b312ea72 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 18 Jul 2021 10:47:38 +0300 Subject: [PATCH 2/3] Update Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> --- .../Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst index b07331c834187c..507a68b65d4c37 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-07-17-21-04-04.bpo-44633.5-zKeI.rst @@ -1,2 +1,2 @@ -Parameter substitution of the union type with wrong types raises now +Parameter substitution of the union type with wrong types now raises ``TypeError`` instead of returning ``NotImplemented``. From 567d3b856a64e28c7e6967da97447802ad392e2d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 18 Jul 2021 11:20:22 +0300 Subject: [PATCH 3/3] Update Objects/unionobject.c Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> --- Objects/unionobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 8124689b6640e2..c0c9a24bcc204a 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -455,7 +455,7 @@ union_getitem(PyObject *self, PyObject *item) Py_DECREF(newargs); if (is_arg_unionable == 0) { PyErr_Format(PyExc_TypeError, - "Each union arg must be a type, got %.100R", arg); + "Each union argument must be a type, got %.100R", arg); } return NULL; }