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

Skip to content

Commit 25e8ec4

Browse files
author
Victor Stinner
committed
Issue #8850: Remove "w" and "w#" formats from PyArg_Parse*() functions, use
"w*" format instead. Add tests for "w*" format.
1 parent 21e0948 commit 25e8ec4

6 files changed

Lines changed: 61 additions & 64 deletions

File tree

Doc/c-api/arg.rst

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,11 @@ Unless otherwise stated, buffers are not NUL-terminated.
150150
any conversion. Raises :exc:`TypeError` if the object is not a Unicode
151151
object. The C variable may also be declared as :ctype:`PyObject\*`.
152152

153-
``w`` (:class:`bytearray` or read-write character buffer) [char \*]
154-
Similar to ``y``, but accepts any object which implements the read-write buffer
155-
interface. The caller must determine the length of the buffer by other means,
156-
or use ``w#`` instead. Only single-segment buffer objects are accepted;
157-
:exc:`TypeError` is raised for all others.
158-
159153
``w*`` (:class:`bytearray` or read-write byte-oriented buffer) [Py_buffer]
160-
This is to ``w`` what ``y*`` is to ``y``.
161-
162-
``w#`` (:class:`bytearray` or read-write character buffer) [char \*, int]
163-
Like ``y#``, but accepts any object which implements the read-write buffer
164-
interface. The :ctype:`char \*` variable is set to point to the first byte
165-
of the buffer, and the :ctype:`int` is set to the length of the buffer.
166-
Only single-segment buffer objects are accepted; :exc:`TypeError` is raised
167-
for all others.
154+
This format accepts any object which implements the read-write buffer
155+
interface. It fills a :ctype:`Py_buffer` structure provided by the caller.
156+
The buffer may contain embedded null bytes. The caller have to call
157+
:cfunc:`PyBuffer_Release` when it is done with the buffer.
168158

169159
``es`` (:class:`str`) [const char \*encoding, char \*\*buffer]
170160
This variant on ``s`` is used for encoding Unicode into a character buffer.

Doc/whatsnew/3.2.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ that may require changes to your code:
173173

174174
* bytearray objects cannot be used anymore as filenames: convert them to bytes
175175

176-
* "t#" format of PyArg_Parse*() functions has been removed: use "s#" or "s*"
177-
instead
176+
* PyArg_Parse*() functions:
177+
178+
* "t#" format has been removed: use "s#" or "s*" instead
179+
* "w" and "w#" formats has been removed: use "w*" instead
178180

179-
* Stub

Lib/test/test_getargs2.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,16 @@ def test_y_hash(self):
375375
self.assertRaises(TypeError, getargs_y_hash, memoryview(b'memoryview'))
376376
self.assertRaises(TypeError, getargs_y_hash, None)
377377

378+
def test_w_star(self):
379+
# getargs_w_star() modifies first and last byte
380+
from _testcapi import getargs_w_star
381+
self.assertRaises(TypeError, getargs_w_star, 'abc\xe9')
382+
self.assertRaises(TypeError, getargs_w_star, b'bytes')
383+
self.assertRaises(TypeError, getargs_w_star, b'nul:\0')
384+
self.assertEqual(getargs_w_star(bytearray(b'bytearray')), b'[ytearra]')
385+
self.assertEqual(getargs_w_star(memoryview(b'memoryview')), b'[emoryvie]')
386+
self.assertRaises(TypeError, getargs_w_star, None)
387+
378388

379389
class Unicode_TestCase(unittest.TestCase):
380390
def test_u(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ Core and Builtins
2929
error handlers, the decoder only supports "strict" and "ignore" error
3030
handlers. Patch written by Mark Hammond.
3131

32+
- Issue #8850: Remove "w" and "w#" formats from PyArg_Parse*() functions, use
33+
"w*" format instead. Add tests for "w*" format.
34+
3235
- Issue #8592: PyArg_Parse*() functions raise a TypeError for "y", "u" and "Z"
3336
formats if the string contains a null byte/character. Write unit tests for
3437
string formats.

Modules/_testcapimodule.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,28 @@ test_widechar(PyObject *self)
13851385
Py_RETURN_NONE;
13861386
}
13871387

1388+
static PyObject *
1389+
getargs_w_star(PyObject *self, PyObject *args)
1390+
{
1391+
Py_buffer buffer;
1392+
PyObject *result;
1393+
char *str;
1394+
1395+
if (!PyArg_ParseTuple(args, "w*:getargs_w_star", &buffer))
1396+
return NULL;
1397+
1398+
if (2 <= buffer.len) {
1399+
str = buffer.buf;
1400+
str[0] = '[';
1401+
str[buffer.len-1] = ']';
1402+
}
1403+
1404+
result = PyBytes_FromStringAndSize(buffer.buf, buffer.len);
1405+
PyBuffer_Release(&buffer);
1406+
return result;
1407+
}
1408+
1409+
13881410
static PyObject *
13891411
test_empty_argparse(PyObject *self)
13901412
{
@@ -2227,6 +2249,7 @@ static PyMethodDef TestMethods[] = {
22272249
{"getargs_u_hash", getargs_u_hash, METH_VARARGS},
22282250
{"getargs_Z", getargs_Z, METH_VARARGS},
22292251
{"getargs_Z_hash", getargs_Z_hash, METH_VARARGS},
2252+
{"getargs_w_star", getargs_w_star, METH_VARARGS},
22302253
{"codec_incrementalencoder",
22312254
(PyCFunction)codec_incrementalencoder, METH_VARARGS},
22322255
{"codec_incrementaldecoder",

Python/getargs.c

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,58 +1231,28 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
12311231
}
12321232

12331233

1234-
case 'w': { /* memory buffer, read-write access */
1234+
case 'w': { /* "w*": memory buffer, read-write access */
12351235
void **p = va_arg(*p_va, void **);
1236-
PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
1237-
Py_ssize_t count;
1238-
int temp=-1;
1239-
Py_buffer view;
1240-
1241-
if (pb && pb->bf_releasebuffer && *format != '*')
1242-
/* Buffer must be released, yet caller does not use
1243-
the Py_buffer protocol. */
1244-
return converterr("pinned buffer", arg, msgbuf, bufsize);
12451236

1237+
if (*format != '*')
1238+
return converterr(
1239+
"invalid use of 'w' format character",
1240+
arg, msgbuf, bufsize);
1241+
format++;
12461242

1247-
if (pb && pb->bf_getbuffer && *format == '*') {
1248-
/* Caller is interested in Py_buffer, and the object
1249-
supports it directly. */
1250-
format++;
1251-
if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) {
1252-
PyErr_Clear();
1253-
return converterr("read-write buffer", arg, msgbuf, bufsize);
1254-
}
1255-
if (addcleanup(p, freelist, cleanup_buffer)) {
1256-
return converterr(
1257-
"(cleanup problem)",
1258-
arg, msgbuf, bufsize);
1259-
}
1260-
if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
1261-
return converterr("contiguous buffer", arg, msgbuf, bufsize);
1262-
break;
1263-
}
1264-
1265-
/* Here we have processed w*, only w and w# remain. */
1266-
if (pb == NULL ||
1267-
pb->bf_getbuffer == NULL ||
1268-
((temp = PyObject_GetBuffer(arg, &view,
1269-
PyBUF_SIMPLE)) != 0) ||
1270-
view.readonly == 1) {
1271-
if (temp==0) {
1272-
PyBuffer_Release(&view);
1273-
}
1274-
return converterr("single-segment read-write buffer",
1275-
arg, msgbuf, bufsize);
1243+
/* Caller is interested in Py_buffer, and the object
1244+
supports it directly. */
1245+
if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) {
1246+
PyErr_Clear();
1247+
return converterr("read-write buffer", arg, msgbuf, bufsize);
12761248
}
1277-
1278-
if ((count = view.len) < 0)
1279-
return converterr("(unspecified)", arg, msgbuf, bufsize);
1280-
*p = view.buf;
1281-
if (*format == '#') {
1282-
FETCH_SIZE;
1283-
STORE_SIZE(count);
1284-
format++;
1249+
if (addcleanup(p, freelist, cleanup_buffer)) {
1250+
return converterr(
1251+
"(cleanup problem)",
1252+
arg, msgbuf, bufsize);
12851253
}
1254+
if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
1255+
return converterr("contiguous buffer", arg, msgbuf, bufsize);
12861256
break;
12871257
}
12881258

0 commit comments

Comments
 (0)