summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_capi/test_getargs.py28
-rw-r--r--Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst2
-rw-r--r--Modules/_testcapi/getargs.c14
-rw-r--r--Python/getargs.c2
4 files changed, 40 insertions, 6 deletions
diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py
index c964b1e..9b6aef2 100644
--- a/Lib/test/test_capi/test_getargs.py
+++ b/Lib/test/test_capi/test_getargs.py
@@ -1314,6 +1314,34 @@ class ParseTupleAndKeywords_Test(unittest.TestCase):
f"'{name2}' is an invalid keyword argument"):
parse((), {name2: 1, name3: 2}, '|OO', [name, name3])
+ def test_nested_tuple(self):
+ parse = _testcapi.parse_tuple_and_keywords
+
+ self.assertEqual(parse(((1, 2, 3),), {}, '(OOO)', ['a']), (1, 2, 3))
+ self.assertEqual(parse((1, (2, 3), 4), {}, 'O(OO)O', ['a', 'b', 'c']),
+ (1, 2, 3, 4))
+ parse(((1, 2, 3),), {}, '(iii)', ['a'])
+
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be sequence of length 2, not 3"):
+ parse(((1, 2, 3),), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be sequence of length 2, not 1"):
+ parse(((1,),), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item sequence, not int"):
+ parse((1,), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item sequence, not bytes"):
+ parse((b'ab',), {}, '(ii)', ['a'])
+
+ for f in 'es', 'et', 'es#', 'et#':
+ with self.assertRaises(LookupError): # empty encoding ""
+ parse((('a',),), {}, '(' + f + ')', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be sequence of length 1, not 0"):
+ parse(((),), {}, '(' + f + ')', ['a'])
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst
new file mode 100644
index 0000000..113119e
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst
@@ -0,0 +1,2 @@
+Fix support of format units "es", "et", "es#", and "et#" in nested tuples in
+:c:func:`PyArg_ParseTuple`-like functions.
diff --git a/Modules/_testcapi/getargs.c b/Modules/_testcapi/getargs.c
index e4cd155..33e8af7 100644
--- a/Modules/_testcapi/getargs.c
+++ b/Modules/_testcapi/getargs.c
@@ -71,18 +71,22 @@ parse_tuple_and_keywords(PyObject *self, PyObject *args)
if (result) {
int objects_only = 1;
+ int count = 0;
for (const char *f = sub_format; *f; f++) {
- if (Py_ISALNUM(*f) && strchr("OSUY", *f) == NULL) {
- objects_only = 0;
- break;
+ if (Py_ISALNUM(*f)) {
+ if (strchr("OSUY", *f) == NULL) {
+ objects_only = 0;
+ break;
+ }
+ count++;
}
}
if (objects_only) {
- return_value = PyTuple_New(size);
+ return_value = PyTuple_New(count);
if (return_value == NULL) {
goto exit;
}
- for (Py_ssize_t i = 0; i < size; i++) {
+ for (Py_ssize_t i = 0; i < count; i++) {
PyObject *arg = *(PyObject **)(buffers + i);
if (arg == NULL) {
arg = Py_None;
diff --git a/Python/getargs.c b/Python/getargs.c
index c0c2eb2..5ff8f74 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -477,7 +477,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
else if (c == ':' || c == ';' || c == '\0')
break;
- else if (level == 0 && Py_ISALPHA(c))
+ else if (level == 0 && Py_ISALPHA(c) && c != 'e')
n++;
}