summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsobolevn <mail@sobolevn.me>2024-08-30 07:36:51 (GMT)
committerGitHub <noreply@github.com>2024-08-30 07:36:51 (GMT)
commitf8a736b8e14ab839e1193cb1d3955b61c316d048 (patch)
treee47187056538c2135f4f6b21df752f8740ec1e3f
parent32c7dbb2bc58bee953622fc5ac24aad123f0d8f2 (diff)
downloadcpython-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.py14
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-08-29-13-18-18.gh-issue-123446.KWDrgq.rst2
-rw-r--r--Objects/typeobject.c58
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)) {