diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 158397af8cc..29d33681930 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -150,21 +150,11 @@ Unless otherwise stated, buffers are not NUL-terminated. any conversion. Raises :exc:`TypeError` if the object is not a Unicode object. The C variable may also be declared as :ctype:`PyObject\*`. -``w`` (:class:`bytearray` or read-write character buffer) [char \*] - Similar to ``y``, but accepts any object which implements the read-write buffer - interface. The caller must determine the length of the buffer by other means, - or use ``w#`` instead. Only single-segment buffer objects are accepted; - :exc:`TypeError` is raised for all others. - ``w*`` (:class:`bytearray` or read-write byte-oriented buffer) [Py_buffer] - This is to ``w`` what ``y*`` is to ``y``. - -``w#`` (:class:`bytearray` or read-write character buffer) [char \*, int] - Like ``y#``, but accepts any object which implements the read-write buffer - interface. The :ctype:`char \*` variable is set to point to the first byte - of the buffer, and the :ctype:`int` is set to the length of the buffer. - Only single-segment buffer objects are accepted; :exc:`TypeError` is raised - for all others. + This format accepts any object which implements the read-write buffer + interface. It fills a :ctype:`Py_buffer` structure provided by the caller. + The buffer may contain embedded null bytes. The caller have to call + :cfunc:`PyBuffer_Release` when it is done with the buffer. ``es`` (:class:`str`) [const char \*encoding, char \*\*buffer] This variant on ``s`` is used for encoding Unicode into a character buffer. diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 14f9215e8b9..f2d50b51086 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -173,7 +173,8 @@ that may require changes to your code: * bytearray objects cannot be used anymore as filenames: convert them to bytes -* "t#" format of PyArg_Parse*() functions has been removed: use "s#" or "s*" - instead +* PyArg_Parse*() functions: + + * "t#" format has been removed: use "s#" or "s*" instead + * "w" and "w#" formats has been removed: use "w*" instead -* Stub diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 5ade2b43e6c..d751c1ac243 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -375,6 +375,16 @@ class Bytes_TestCase(unittest.TestCase): self.assertRaises(TypeError, getargs_y_hash, memoryview(b'memoryview')) self.assertRaises(TypeError, getargs_y_hash, None) + def test_w_star(self): + # getargs_w_star() modifies first and last byte + from _testcapi import getargs_w_star + self.assertRaises(TypeError, getargs_w_star, 'abc\xe9') + self.assertRaises(TypeError, getargs_w_star, b'bytes') + self.assertRaises(TypeError, getargs_w_star, b'nul:\0') + self.assertEqual(getargs_w_star(bytearray(b'bytearray')), b'[ytearra]') + self.assertEqual(getargs_w_star(memoryview(b'memoryview')), b'[emoryvie]') + self.assertRaises(TypeError, getargs_w_star, None) + class Unicode_TestCase(unittest.TestCase): def test_u(self): diff --git a/Misc/NEWS b/Misc/NEWS index c6a8c6ebbb5..1073cd6b579 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,9 @@ Core and Builtins error handlers, the decoder only supports "strict" and "ignore" error handlers. Patch written by Mark Hammond. +- Issue #8850: Remove "w" and "w#" formats from PyArg_Parse*() functions, use + "w*" format instead. Add tests for "w*" format. + - Issue #8592: PyArg_Parse*() functions raise a TypeError for "y", "u" and "Z" formats if the string contains a null byte/character. Write unit tests for string formats. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7bcc1d8cecb..acbff344fe3 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1385,6 +1385,28 @@ test_widechar(PyObject *self) Py_RETURN_NONE; } +static PyObject * +getargs_w_star(PyObject *self, PyObject *args) +{ + Py_buffer buffer; + PyObject *result; + char *str; + + if (!PyArg_ParseTuple(args, "w*:getargs_w_star", &buffer)) + return NULL; + + if (2 <= buffer.len) { + str = buffer.buf; + str[0] = '['; + str[buffer.len-1] = ']'; + } + + result = PyBytes_FromStringAndSize(buffer.buf, buffer.len); + PyBuffer_Release(&buffer); + return result; +} + + static PyObject * test_empty_argparse(PyObject *self) { @@ -2227,6 +2249,7 @@ static PyMethodDef TestMethods[] = { {"getargs_u_hash", getargs_u_hash, METH_VARARGS}, {"getargs_Z", getargs_Z, METH_VARARGS}, {"getargs_Z_hash", getargs_Z_hash, METH_VARARGS}, + {"getargs_w_star", getargs_w_star, METH_VARARGS}, {"codec_incrementalencoder", (PyCFunction)codec_incrementalencoder, METH_VARARGS}, {"codec_incrementaldecoder", diff --git a/Python/getargs.c b/Python/getargs.c index 41b4af56cf4..7b929481d25 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1231,58 +1231,28 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, } - case 'w': { /* memory buffer, read-write access */ + case 'w': { /* "w*": memory buffer, read-write access */ void **p = va_arg(*p_va, void **); - PyBufferProcs *pb = arg->ob_type->tp_as_buffer; - Py_ssize_t count; - int temp=-1; - Py_buffer view; - if (pb && pb->bf_releasebuffer && *format != '*') - /* Buffer must be released, yet caller does not use - the Py_buffer protocol. */ - return converterr("pinned buffer", arg, msgbuf, bufsize); + if (*format != '*') + return converterr( + "invalid use of 'w' format character", + arg, msgbuf, bufsize); + format++; - - if (pb && pb->bf_getbuffer && *format == '*') { - /* Caller is interested in Py_buffer, and the object - supports it directly. */ - format++; - if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); - return converterr("read-write buffer", arg, msgbuf, bufsize); - } - if (addcleanup(p, freelist, cleanup_buffer)) { - return converterr( - "(cleanup problem)", - arg, msgbuf, bufsize); - } - if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C')) - return converterr("contiguous buffer", arg, msgbuf, bufsize); - break; + /* Caller is interested in Py_buffer, and the object + supports it directly. */ + if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) { + PyErr_Clear(); + return converterr("read-write buffer", arg, msgbuf, bufsize); } - - /* Here we have processed w*, only w and w# remain. */ - if (pb == NULL || - pb->bf_getbuffer == NULL || - ((temp = PyObject_GetBuffer(arg, &view, - PyBUF_SIMPLE)) != 0) || - view.readonly == 1) { - if (temp==0) { - PyBuffer_Release(&view); - } - return converterr("single-segment read-write buffer", - arg, msgbuf, bufsize); - } - - if ((count = view.len) < 0) - return converterr("(unspecified)", arg, msgbuf, bufsize); - *p = view.buf; - if (*format == '#') { - FETCH_SIZE; - STORE_SIZE(count); - format++; + if (addcleanup(p, freelist, cleanup_buffer)) { + return converterr( + "(cleanup problem)", + arg, msgbuf, bufsize); } + if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C')) + return converterr("contiguous buffer", arg, msgbuf, bufsize); break; }