Issue #8850: Remove "w" and "w#" formats from PyArg_Parse*() functions, use
"w*" format instead. Add tests for "w*" format.
diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst
index 158397a..29d3368 100644
--- a/Doc/c-api/arg.rst
+++ b/Doc/c-api/arg.rst
@@ -150,21 +150,11 @@
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 14f9215..f2d50b5 100644
--- a/Doc/whatsnew/3.2.rst
+++ b/Doc/whatsnew/3.2.rst
@@ -173,7 +173,8 @@
* 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:
-* Stub
+ * "t#" format has been removed: use "s#" or "s*" instead
+ * "w" and "w#" formats has been removed: use "w*" instead
+
diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py
index 5ade2b4..d751c1a 100644
--- a/Lib/test/test_getargs2.py
+++ b/Lib/test/test_getargs2.py
@@ -375,6 +375,16 @@
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 c6a8c6e..1073cd6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -29,6 +29,9 @@
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 7bcc1d8..acbff34 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -1386,6 +1386,28 @@
}
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)
{
/* Test that formats can begin with '|'. See issue #4720. */
@@ -2227,6 +2249,7 @@
{"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 41b4af5..7b92948 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -1231,58 +1231,28 @@
}
- 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 (addcleanup(p, freelist, cleanup_buffer)) {
+ return converterr(
+ "(cleanup problem)",
+ 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 (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
+ return converterr("contiguous buffer", arg, msgbuf, bufsize);
break;
}