From c528a3aa36a00beea9645e1af7d020cbe1b01277 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Jun 2010 23:30:36 +0200 Subject: [PATCH] Issue #8850: Remove "w" and "w#" formats from PyArg_Parse*() functions Use "w*" format instead. Add tests for "w*" format. --- Doc/c-api/arg.rst | 18 +++---------- Doc/whatsnew/3.2.rst | 7 +++-- Lib/test/test_getargs2.py | 10 +++++++ Misc/NEWS | 3 ++ Modules/_testcapimodule.c | 23 ++++++++++++++++ Python/getargs.c | 64 ++++++++++++--------------------------------- 6 files changed, 61 insertions(+), 64 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 1c49c8b..aa19cea 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 14f9215..f2d50b5 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 7186f55..75f17f5 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 6855696..e07724d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 1? Core and Builtins ----------------- +- 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 @@ test_widechar(PyObject *self) } 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 @@ 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 20f4814..2416475 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1307,58 +1307,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; - } - - /* 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); + /* 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); } - - 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; } -- 1.6.2.5