Skip to content

Commit f83fa0b

Browse files
gh-84489: Properly handle trailing spaces in Py_BuildValue() format strings (GH-21158)
The docs state that the space, tab, colon, and comma characters are ignored in Py_BuildValue() format strings. Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent f27b830 commit f83fa0b

File tree

3 files changed

+40
-35
lines changed

3 files changed

+40
-35
lines changed

Lib/test/test_capi/test_misc.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ def test_buildvalue(self):
319319
self.assertEqual(buildvalue('((OO))', 1, 2), ((1, 2),))
320320

321321
self.assertEqual(buildvalue(' \t,:'), None)
322+
self.assertEqual(buildvalue('O,', 1), 1)
322323
self.assertEqual(buildvalue(' O ', 1), 1)
323324
self.assertEqual(buildvalue('\tO\t', 1), 1)
324325
self.assertEqual(buildvalue('O,O', 1, 2), (1, 2))
@@ -327,17 +328,18 @@ def test_buildvalue(self):
327328
self.assertEqual(buildvalue('O O', 1, 2), (1, 2))
328329
self.assertEqual(buildvalue('O\tO', 1, 2), (1, 2))
329330
self.assertEqual(buildvalue('(O,O)', 1, 2), (1, 2))
330-
self.assertEqual(buildvalue('(O, O)', 1, 2), (1, 2))
331-
self.assertEqual(buildvalue(' ( O O) ', 1, 2), (1, 2))
332-
self.assertEqual(buildvalue('\t(\tO\tO)\t', 1, 2), (1, 2))
331+
self.assertEqual(buildvalue('(O, O,)', 1, 2), (1, 2))
332+
self.assertEqual(buildvalue(' ( O O ) ', 1, 2), (1, 2))
333+
self.assertEqual(buildvalue('\t(\tO\tO\t)\t', 1, 2), (1, 2))
333334
self.assertEqual(buildvalue('[O,O]', 1, 2), [1, 2])
334-
self.assertEqual(buildvalue('[O, O]', 1, 2), [1, 2])
335-
self.assertEqual(buildvalue(' [ O O] ', 1, 2), [1, 2])
335+
self.assertEqual(buildvalue('[O, O,]', 1, 2), [1, 2])
336+
self.assertEqual(buildvalue(' [ O O ] ', 1, 2), [1, 2])
337+
self.assertEqual(buildvalue(' [\tO\tO\t] ', 1, 2), [1, 2])
336338
self.assertEqual(buildvalue('{O:O}', 1, 2), {1: 2})
337339
self.assertEqual(buildvalue('{O:O,O:O}', 1, 2, 3, 4), {1: 2, 3: 4})
338-
self.assertEqual(buildvalue('{O: O, O: O}', 1, 2, 3, 4), {1: 2, 3: 4})
339-
self.assertEqual(buildvalue(' { O O O O} ', 1, 2, 3, 4), {1: 2, 3: 4})
340-
self.assertEqual(buildvalue('\t{\tO\tO\tO\tO}\t', 1, 2, 3, 4), {1: 2, 3: 4})
340+
self.assertEqual(buildvalue('{O: O, O: O,}', 1, 2, 3, 4), {1: 2, 3: 4})
341+
self.assertEqual(buildvalue(' { O O O O } ', 1, 2, 3, 4), {1: 2, 3: 4})
342+
self.assertEqual(buildvalue('\t{\tO\tO\tO\tO\t}\t', 1, 2, 3, 4), {1: 2, 3: 4})
341343

342344
self.assertRaises(SystemError, buildvalue, 'O', NULL)
343345
self.assertRaises(SystemError, buildvalue, '(O)', NULL)
@@ -378,6 +380,12 @@ def test_buildvalue_ints(self):
378380
self.assertEqual(buildvalue('C', sys.maxunicode), chr(sys.maxunicode))
379381
self.assertRaises(ValueError, buildvalue, 'C', -1)
380382
self.assertRaises(ValueError, buildvalue, 'C', sys.maxunicode+1)
383+
384+
# gh-84489
385+
self.assertRaises(ValueError, buildvalue, '(C )i', -1, 2)
386+
self.assertRaises(ValueError, buildvalue, '[C ]i', -1, 2)
387+
self.assertRaises(ValueError, buildvalue, '{Ci }i', -1, 2, 3)
388+
381389
def test_buildvalue_N(self):
382390
_testcapi.test_buildvalue_N()
383391

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Properly handle trailing spaces before closing parenthesis in :c:func:`Py_BuildValue` format strings.

Python/modsupport.c

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,24 @@ static PyObject *do_mklist(const char**, va_list *, char, Py_ssize_t);
8888
static PyObject *do_mkdict(const char**, va_list *, char, Py_ssize_t);
8989
static PyObject *do_mkvalue(const char**, va_list *);
9090

91+
static int
92+
check_end(const char **p_format, char endchar)
93+
{
94+
const char *f = *p_format;
95+
while (*f != endchar) {
96+
if (*f != ' ' && *f != '\t' && *f != ',' && *f != ':') {
97+
PyErr_SetString(PyExc_SystemError,
98+
"Unmatched paren in format");
99+
return 0;
100+
}
101+
f++;
102+
}
103+
if (endchar) {
104+
f++;
105+
}
106+
*p_format = f;
107+
return 1;
108+
}
91109

92110
static void
93111
do_ignore(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
@@ -108,14 +126,9 @@ do_ignore(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
108126
}
109127
}
110128
Py_XDECREF(v);
111-
if (**p_format != endchar) {
112-
PyErr_SetString(PyExc_SystemError,
113-
"Unmatched paren in format");
129+
if (!check_end(p_format, endchar)) {
114130
return;
115131
}
116-
if (endchar) {
117-
++*p_format;
118-
}
119132
}
120133

121134
static PyObject *
@@ -157,14 +170,10 @@ do_mkdict(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
157170
Py_DECREF(k);
158171
Py_DECREF(v);
159172
}
160-
if (**p_format != endchar) {
173+
if (!check_end(p_format, endchar)) {
161174
Py_DECREF(d);
162-
PyErr_SetString(PyExc_SystemError,
163-
"Unmatched paren in format");
164175
return NULL;
165176
}
166-
if (endchar)
167-
++*p_format;
168177
return d;
169178
}
170179

@@ -191,14 +200,10 @@ do_mklist(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
191200
}
192201
PyList_SET_ITEM(v, i, w);
193202
}
194-
if (**p_format != endchar) {
203+
if (!check_end(p_format, endchar)) {
195204
Py_DECREF(v);
196-
PyErr_SetString(PyExc_SystemError,
197-
"Unmatched paren in format");
198205
return NULL;
199206
}
200-
if (endchar)
201-
++*p_format;
202207
return v;
203208
}
204209

@@ -221,14 +226,9 @@ do_mkstack(PyObject **stack, const char **p_format, va_list *p_va,
221226
}
222227
stack[i] = w;
223228
}
224-
if (**p_format != endchar) {
225-
PyErr_SetString(PyExc_SystemError,
226-
"Unmatched paren in format");
229+
if (!check_end(p_format, endchar)) {
227230
goto error;
228231
}
229-
if (endchar) {
230-
++*p_format;
231-
}
232232
return 0;
233233

234234
error:
@@ -261,14 +261,10 @@ do_mktuple(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
261261
}
262262
PyTuple_SET_ITEM(v, i, w);
263263
}
264-
if (**p_format != endchar) {
264+
if (!check_end(p_format, endchar)) {
265265
Py_DECREF(v);
266-
PyErr_SetString(PyExc_SystemError,
267-
"Unmatched paren in format");
268266
return NULL;
269267
}
270-
if (endchar)
271-
++*p_format;
272268
return v;
273269
}
274270

0 commit comments

Comments
 (0)