diff options
author | sobolevn <mail@sobolevn.me> | 2024-08-30 07:36:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-30 07:36:51 (GMT) |
commit | f8a736b8e14ab839e1193cb1d3955b61c316d048 (patch) | |
tree | e47187056538c2135f4f6b21df752f8740ec1e3f | |
parent | 32c7dbb2bc58bee953622fc5ac24aad123f0d8f2 (diff) | |
download | cpython-f8a736b8e14ab839e1193cb1d3955b61c316d048.zip cpython-f8a736b8e14ab839e1193cb1d3955b61c316d048.tar.gz cpython-f8a736b8e14ab839e1193cb1d3955b61c316d048.tar.bz2 |
gh-123446: Fix empty function names in `TypeError`s in `typeobject` (#123470)
-rw-r--r-- | Lib/test/test_descr.py | 14 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst | 2 | ||||
-rw-r--r-- | Objects/typeobject.c | 58 |
3 files changed, 63 insertions, 11 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index b0f8631..9d15ab3 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4021,6 +4021,20 @@ class ClassPropertiesAndMethods(unittest.TestCase): 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) + 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/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 0000000..704bde9 --- /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 c3a8fb5..d9c6b99 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_pow_args(PyObject *ob) +{ + // Returns the argument count on success or `-1` on error. + int min = 1; + int max = 2; + 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, "", 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)(self, other, third); } @@ -8771,10 +8799,17 @@ 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, "", 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); } @@ -8795,8 +8830,9 @@ wrap_indexargfunc(PyObject *self, PyObject *args, void *wrapped) PyObject* o; Py_ssize_t i; - if (!PyArg_UnpackTuple(args, "", 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; @@ -8852,7 +8888,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 +8944,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 +9041,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 +9151,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 +9172,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 +9201,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 +9222,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)) { |