From 2411d03ebad10492f19563a50a72e42ba837c724 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 29 Aug 2024 15:23:34 +0300 Subject: [PATCH 1/4] gh-123446: Fix empty function names in `TypeError`s in `typeobject` --- ...-08-29-13-18-18.gh-issue-123446.KWDrgq.rst | 2 ++ Objects/typeobject.c | 20 +++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst new file mode 100644 index 00000000000000..704bde9d01cac9 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst @@ -0,0 +1,2 @@ +Fix empty function name in :exc:`TypeError` when builtin magic methods are +used without the required args. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c3a8fb5567932b..eafcae4a3cdc56 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8759,7 +8759,7 @@ wrap_ternaryfunc(PyObject *self, PyObject *args, void *wrapped) /* Note: This wrapper only works for __pow__() */ - if (!PyArg_UnpackTuple(args, "", 1, 2, &other, &third)) + if (!PyArg_UnpackTuple(args, "__pow__", 1, 2, &other, &third)) return NULL; return (*func)(self, other, third); } @@ -8773,7 +8773,7 @@ wrap_ternaryfunc_r(PyObject *self, PyObject *args, void *wrapped) /* Note: This wrapper only works for __pow__() */ - if (!PyArg_UnpackTuple(args, "", 1, 2, &other, &third)) + if (!PyArg_UnpackTuple(args, "__rpow__", 1, 2, &other, &third)) return NULL; return (*func)(other, self, third); } @@ -8795,7 +8795,7 @@ wrap_indexargfunc(PyObject *self, PyObject *args, void *wrapped) PyObject* o; Py_ssize_t i; - if (!PyArg_UnpackTuple(args, "", 1, 1, &o)) + if (!PyArg_UnpackTuple(args, "__mul__", 1, 1, &o)) return NULL; i = PyNumber_AsSsize_t(o, PyExc_OverflowError); if (i == -1 && PyErr_Occurred()) @@ -8852,7 +8852,7 @@ wrap_sq_setitem(PyObject *self, PyObject *args, void *wrapped) int res; PyObject *arg, *value; - if (!PyArg_UnpackTuple(args, "", 2, 2, &arg, &value)) + if (!PyArg_UnpackTuple(args, "__setitem__", 2, 2, &arg, &value)) return NULL; i = getindex(self, arg); if (i == -1 && PyErr_Occurred()) @@ -8908,7 +8908,7 @@ wrap_objobjargproc(PyObject *self, PyObject *args, void *wrapped) int res; PyObject *key, *value; - if (!PyArg_UnpackTuple(args, "", 2, 2, &key, &value)) + if (!PyArg_UnpackTuple(args, "__setitem__", 2, 2, &key, &value)) return NULL; res = (*func)(self, key, value); if (res == -1 && PyErr_Occurred()) @@ -9005,7 +9005,7 @@ wrap_setattr(PyObject *self, PyObject *args, void *wrapped) int res; PyObject *name, *value; - if (!PyArg_UnpackTuple(args, "", 2, 2, &name, &value)) + if (!PyArg_UnpackTuple(args, "__setattr__", 2, 2, &name, &value)) return NULL; if (!hackcheck(self, func, "__setattr__")) return NULL; @@ -9115,7 +9115,7 @@ wrap_descr_get(PyObject *self, PyObject *args, void *wrapped) PyObject *obj; PyObject *type = NULL; - if (!PyArg_UnpackTuple(args, "", 1, 2, &obj, &type)) + if (!PyArg_UnpackTuple(args, "__get__", 1, 2, &obj, &type)) return NULL; if (obj == Py_None) obj = NULL; @@ -9136,7 +9136,7 @@ wrap_descr_set(PyObject *self, PyObject *args, void *wrapped) PyObject *obj, *value; int ret; - if (!PyArg_UnpackTuple(args, "", 2, 2, &obj, &value)) + if (!PyArg_UnpackTuple(args, "__set__", 2, 2, &obj, &value)) return NULL; ret = (*func)(self, obj, value); if (ret < 0) @@ -9165,7 +9165,7 @@ wrap_buffer(PyObject *self, PyObject *args, void *wrapped) { PyObject *arg = NULL; - if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) { + if (!PyArg_UnpackTuple(args, "__buffer__", 1, 1, &arg)) { return NULL; } Py_ssize_t flags = PyNumber_AsSsize_t(arg, PyExc_OverflowError); @@ -9186,7 +9186,7 @@ static PyObject * wrap_releasebuffer(PyObject *self, PyObject *args, void *wrapped) { PyObject *arg = NULL; - if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) { + if (!PyArg_UnpackTuple(args, "__release_buffer__", 1, 1, &arg)) { return NULL; } if (!PyMemoryView_Check(arg)) { From d4d0070192b74702a4a0e775b07dab6004fbb65f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 29 Aug 2024 21:30:08 +0300 Subject: [PATCH 2/4] Address review --- Objects/typeobject.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index eafcae4a3cdc56..cbb741b5320b24 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8678,6 +8678,27 @@ check_num_args(PyObject *ob, int n) return 0; } +static Py_ssize_t +check_num_varargs(PyObject *ob, int min, int max) +{ + // Returns the argument count on success or `-1` on error. + assert(min >= 0); + assert(min < max); + if (!PyTuple_CheckExact(ob)) { + PyErr_SetString(PyExc_SystemError, + "PyArg_UnpackTuple() argument list is not a tuple"); + return -1; + } + Py_ssize_t size = PyTuple_GET_SIZE(ob); + if (size >= min && size <= max) { + return size; + } + PyErr_Format( + PyExc_TypeError, + "expected %d or %d arguments, got %zd", min, max, PyTuple_GET_SIZE(ob)); + return -1; +} + /* Generic wrappers for overloadable 'operators' such as __getitem__ */ /* There's a wrapper *function* for each distinct function typedef used @@ -8759,8 +8780,15 @@ wrap_ternaryfunc(PyObject *self, PyObject *args, void *wrapped) /* Note: This wrapper only works for __pow__() */ - if (!PyArg_UnpackTuple(args, "__pow__", 1, 2, &other, &third)) + Py_ssize_t size = check_num_varargs(args, 1, 2); + if (size == -1) { return NULL; + } + other = PyTuple_GET_ITEM(args, 0); + if (size == 2) { + third = PyTuple_GET_ITEM(args, 1); + } + return (*func)(self, other, third); } @@ -8771,7 +8799,7 @@ wrap_ternaryfunc_r(PyObject *self, PyObject *args, void *wrapped) PyObject *other; PyObject *third = Py_None; - /* Note: This wrapper only works for __pow__() */ + /* Note: This wrapper only works for __rpow__() */ if (!PyArg_UnpackTuple(args, "__rpow__", 1, 2, &other, &third)) return NULL; @@ -8795,8 +8823,9 @@ wrap_indexargfunc(PyObject *self, PyObject *args, void *wrapped) PyObject* o; Py_ssize_t i; - if (!PyArg_UnpackTuple(args, "__mul__", 1, 1, &o)) + if (!check_num_args(args, 1)) return NULL; + o = PyTuple_GET_ITEM(args, 0); i = PyNumber_AsSsize_t(o, PyExc_OverflowError); if (i == -1 && PyErr_Occurred()) return NULL; From 5810386837e80974bb47d0d0efa7b9ffbba7adfa Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 29 Aug 2024 22:02:59 +0300 Subject: [PATCH 3/4] Add a test case --- Lib/test/test_descr.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index b0f86317bfecf6..e9b1ff321b330b 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4021,6 +4021,14 @@ def test_ipow_exception_text(self): y = x ** 2 self.assertIn('unsupported operand type(s) for **', str(cm.exception)) + def test_pow_wrapper_error_messages(self): + self.assertRaisesRegex(TypeError, + 'expected 1 or 2 arguments, got 0', + int().__pow__) + self.assertRaisesRegex(TypeError, + 'expected 1 or 2 arguments, got 3', + int().__pow__, 1, 2, 3) + def test_mutable_bases(self): # Testing mutable bases... From 41ac72fad8f4a7f72200cf62190ba503d9ebb865 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 30 Aug 2024 09:45:20 +0300 Subject: [PATCH 4/4] Address review --- Lib/test/test_descr.py | 6 ++++++ Objects/typeobject.c | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index e9b1ff321b330b..9d15ab3a96bad6 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4028,6 +4028,12 @@ def test_pow_wrapper_error_messages(self): self.assertRaisesRegex(TypeError, 'expected 1 or 2 arguments, got 3', int().__pow__, 1, 2, 3) + self.assertRaisesRegex(TypeError, + 'expected 1 or 2 arguments, got 0', + int().__rpow__) + self.assertRaisesRegex(TypeError, + 'expected 1 or 2 arguments, got 3', + int().__rpow__, 1, 2, 3) def test_mutable_bases(self): # Testing mutable bases... diff --git a/Objects/typeobject.c b/Objects/typeobject.c index cbb741b5320b24..d9c6b992bc747c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8679,11 +8679,11 @@ check_num_args(PyObject *ob, int n) } static Py_ssize_t -check_num_varargs(PyObject *ob, int min, int max) +check_pow_args(PyObject *ob) { // Returns the argument count on success or `-1` on error. - assert(min >= 0); - assert(min < max); + int min = 1; + int max = 2; if (!PyTuple_CheckExact(ob)) { PyErr_SetString(PyExc_SystemError, "PyArg_UnpackTuple() argument list is not a tuple"); @@ -8780,7 +8780,7 @@ wrap_ternaryfunc(PyObject *self, PyObject *args, void *wrapped) /* Note: This wrapper only works for __pow__() */ - Py_ssize_t size = check_num_varargs(args, 1, 2); + Py_ssize_t size = check_pow_args(args); if (size == -1) { return NULL; } @@ -8801,8 +8801,15 @@ wrap_ternaryfunc_r(PyObject *self, PyObject *args, void *wrapped) /* Note: This wrapper only works for __rpow__() */ - if (!PyArg_UnpackTuple(args, "__rpow__", 1, 2, &other, &third)) + Py_ssize_t size = check_pow_args(args); + if (size == -1) { return NULL; + } + other = PyTuple_GET_ITEM(args, 0); + if (size == 2) { + third = PyTuple_GET_ITEM(args, 1); + } + return (*func)(other, self, third); }