Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eb927e9

Browse files
llllllllllJoe Jevnikencukou
authored
gh-68114: Fix handling for removed PyArg_ParseTuple 'w' formatters (GH-8204)
Co-authored-by: Joe Jevnik <[email protected]> Co-authored-by: Petr Viktorin <[email protected]>
1 parent e17cd1f commit eb927e9

File tree

4 files changed

+146
-17
lines changed

4 files changed

+146
-17
lines changed

Lib/test/test_capi/test_getargs.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -856,20 +856,24 @@ def test_y_hash(self):
856856

857857
def test_w_star(self):
858858
# getargs_w_star() modifies first and last byte
859-
from _testcapi import getargs_w_star
860-
self.assertRaises(TypeError, getargs_w_star, 'abc\xe9')
861-
self.assertRaises(TypeError, getargs_w_star, b'bytes')
862-
self.assertRaises(TypeError, getargs_w_star, b'nul:\0')
863-
self.assertRaises(TypeError, getargs_w_star, memoryview(b'bytes'))
864-
buf = bytearray(b'bytearray')
865-
self.assertEqual(getargs_w_star(buf), b'[ytearra]')
866-
self.assertEqual(buf, bytearray(b'[ytearra]'))
867-
buf = bytearray(b'memoryview')
868-
self.assertEqual(getargs_w_star(memoryview(buf)), b'[emoryvie]')
869-
self.assertEqual(buf, bytearray(b'[emoryvie]'))
870-
self.assertRaises(TypeError, getargs_w_star, None)
871-
self.assertRaises(TypeError, getargs_w_star, NONCONTIG_WRITABLE)
872-
self.assertRaises(TypeError, getargs_w_star, NONCONTIG_READONLY)
859+
# getargs_w_star_opt() takes additional optional args: with one
860+
# argument it should behave the same as getargs_w_star
861+
from _testcapi import getargs_w_star, getargs_w_star_opt
862+
for func in (getargs_w_star, getargs_w_star_opt):
863+
with self.subTest(func=func):
864+
self.assertRaises(TypeError, func, 'abc\xe9')
865+
self.assertRaises(TypeError, func, b'bytes')
866+
self.assertRaises(TypeError, func, b'nul:\0')
867+
self.assertRaises(TypeError, func, memoryview(b'bytes'))
868+
buf = bytearray(b'bytearray')
869+
self.assertEqual(func(buf), b'[ytearra]')
870+
self.assertEqual(buf, bytearray(b'[ytearra]'))
871+
buf = bytearray(b'memoryview')
872+
self.assertEqual(func(memoryview(buf)), b'[emoryvie]')
873+
self.assertEqual(buf, bytearray(b'[emoryvie]'))
874+
self.assertRaises(TypeError, func, None)
875+
self.assertRaises(TypeError, func, NONCONTIG_WRITABLE)
876+
self.assertRaises(TypeError, func, NONCONTIG_READONLY)
873877

874878
def test_getargs_empty(self):
875879
from _testcapi import getargs_empty
@@ -1112,9 +1116,9 @@ def test_skipitem(self):
11121116
c = chr(i)
11131117

11141118
# skip parentheses, the error reporting is inconsistent about them
1115-
# skip 'e', it's always a two-character code
1119+
# skip 'e' and 'w', they're always two-character codes
11161120
# skip '|' and '$', they don't represent arguments anyway
1117-
if c in '()e|$':
1121+
if c in '()ew|$':
11181122
continue
11191123

11201124
# test the format unit when not skipped
@@ -1152,7 +1156,7 @@ def test_skipitem_with_suffix(self):
11521156
dict_b = {'b':1}
11531157
keywords = ["a", "b"]
11541158

1155-
supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w#', 'w*')
1159+
supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w*')
11561160
for c in string.ascii_letters:
11571161
for c2 in '#*':
11581162
f = c + c2
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed skipitem()'s handling of the old 'w' and 'w#' formatters. These are
2+
no longer supported and now raise an exception if used.

Modules/_testcapi/getargs.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,122 @@ getargs_w_star(PyObject *self, PyObject *args)
141141
return result;
142142
}
143143

144+
static PyObject *
145+
getargs_w_star_opt(PyObject *self, PyObject *args)
146+
{
147+
Py_buffer buffer;
148+
Py_buffer buf2;
149+
int number = 1;
150+
151+
if (!PyArg_ParseTuple(args, "w*|w*i:getargs_w_star",
152+
&buffer, &buf2, &number)) {
153+
return NULL;
154+
}
155+
156+
if (2 <= buffer.len) {
157+
char *str = buffer.buf;
158+
str[0] = '[';
159+
str[buffer.len-1] = ']';
160+
}
161+
162+
PyObject *result = PyBytes_FromStringAndSize(buffer.buf, buffer.len);
163+
PyBuffer_Release(&buffer);
164+
return result;
165+
}
166+
167+
/* Test the old w and w# codes that no longer work */
168+
static PyObject *
169+
test_w_code_invalid(PyObject *self, PyObject *arg)
170+
{
171+
static const char * const keywords[] = {"a", "b", "c", "d", NULL};
172+
char *formats_3[] = {"O|w#$O",
173+
"O|w$O",
174+
"O|w#O",
175+
"O|wO",
176+
NULL};
177+
char *formats_4[] = {"O|w#O$O",
178+
"O|wO$O",
179+
"O|Ow#O",
180+
"O|OwO",
181+
"O|Ow#$O",
182+
"O|Ow$O",
183+
NULL};
184+
size_t n;
185+
PyObject *args;
186+
PyObject *kwargs;
187+
PyObject *tmp;
188+
189+
if (!(args = PyTuple_Pack(1, Py_None))) {
190+
return NULL;
191+
}
192+
193+
kwargs = PyDict_New();
194+
if (!kwargs) {
195+
Py_DECREF(args);
196+
return NULL;
197+
}
198+
199+
if (PyDict_SetItemString(kwargs, "c", Py_None)) {
200+
Py_DECREF(args);
201+
Py_XDECREF(kwargs);
202+
return NULL;
203+
}
204+
205+
for (n = 0; formats_3[n]; ++n) {
206+
if (PyArg_ParseTupleAndKeywords(args, kwargs, formats_3[n],
207+
(char**) keywords,
208+
&tmp, &tmp, &tmp)) {
209+
Py_DECREF(args);
210+
Py_DECREF(kwargs);
211+
PyErr_Format(PyExc_AssertionError,
212+
"test_w_code_invalid_suffix: %s",
213+
formats_3[n]);
214+
return NULL;
215+
}
216+
else {
217+
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
218+
Py_DECREF(args);
219+
Py_DECREF(kwargs);
220+
return NULL;
221+
}
222+
PyErr_Clear();
223+
}
224+
}
225+
226+
if (PyDict_DelItemString(kwargs, "c") ||
227+
PyDict_SetItemString(kwargs, "d", Py_None)) {
228+
229+
Py_DECREF(kwargs);
230+
Py_DECREF(args);
231+
return NULL;
232+
}
233+
234+
for (n = 0; formats_4[n]; ++n) {
235+
if (PyArg_ParseTupleAndKeywords(args, kwargs, formats_4[n],
236+
(char**) keywords,
237+
&tmp, &tmp, &tmp, &tmp)) {
238+
Py_DECREF(args);
239+
Py_DECREF(kwargs);
240+
PyErr_Format(PyExc_AssertionError,
241+
"test_w_code_invalid_suffix: %s",
242+
formats_4[n]);
243+
return NULL;
244+
}
245+
else {
246+
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
247+
Py_DECREF(args);
248+
Py_DECREF(kwargs);
249+
return NULL;
250+
}
251+
PyErr_Clear();
252+
}
253+
}
254+
255+
Py_DECREF(args);
256+
Py_DECREF(kwargs);
257+
Py_RETURN_NONE;
258+
}
259+
144260
static PyObject *
145261
getargs_empty(PyObject *self, PyObject *args, PyObject *kwargs)
146262
{
@@ -684,6 +800,7 @@ static PyMethodDef test_methods[] = {
684800
{"getargs_s_star", getargs_s_star, METH_VARARGS},
685801
{"getargs_tuple", getargs_tuple, METH_VARARGS},
686802
{"getargs_w_star", getargs_w_star, METH_VARARGS},
803+
{"getargs_w_star_opt", getargs_w_star_opt, METH_VARARGS},
687804
{"getargs_empty", _PyCFunction_CAST(getargs_empty), METH_VARARGS|METH_KEYWORDS},
688805
{"getargs_y", getargs_y, METH_VARARGS},
689806
{"getargs_y_hash", getargs_y_hash, METH_VARARGS},
@@ -693,6 +810,7 @@ static PyMethodDef test_methods[] = {
693810
{"getargs_z_star", getargs_z_star, METH_VARARGS},
694811
{"parse_tuple_and_keywords", parse_tuple_and_keywords, METH_VARARGS},
695812
{"gh_99240_clear_args", gh_99240_clear_args, METH_VARARGS},
813+
{"test_w_code_invalid", test_w_code_invalid, METH_NOARGS},
696814
{NULL},
697815
};
698816

Python/getargs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,11 @@ skipitem(const char **p_format, va_list *p_va, int flags)
26412641
if (p_va != NULL) {
26422642
(void) va_arg(*p_va, char **);
26432643
}
2644+
if (c == 'w' && *format != '*')
2645+
{
2646+
/* after 'w', only '*' is allowed */
2647+
goto err;
2648+
}
26442649
if (*format == '#') {
26452650
if (p_va != NULL) {
26462651
(void) va_arg(*p_va, Py_ssize_t *);

0 commit comments

Comments
 (0)