From 9e66aba99925eebacfe137d9deb0ef1fdbc2d5db Mon Sep 17 00:00:00 2001 From: Joannah Nanjekye <33177550+nanjekyejoannah@users.noreply.github.com> Date: Tue, 20 Aug 2019 11:46:36 -0300 Subject: [PATCH] bpo-15913: Implement PyBuffer_SizeFromFormat() (GH-13873) Implement PyBuffer_SizeFromFormat() function (previously documented but not implemented): call struct.calcsize(). --- Doc/c-api/buffer.rst | 6 ++- Include/cpython/abstract.h | 2 +- Lib/test/test_buffer.py | 12 ++++++ .../2019-06-06-20-52-38.bpo-15913.5Sg5cv.rst | 3 ++ Modules/_testcapimodule.c | 21 ++++++++++ Objects/abstract.c | 42 +++++++++++++++++++ 6 files changed, 83 insertions(+), 3 deletions(-) mode change 100644 => 100755 Doc/c-api/buffer.rst mode change 100644 => 100755 Include/cpython/abstract.h mode change 100644 => 100755 Lib/test/test_buffer.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-06-06-20-52-38.bpo-15913.5Sg5cv.rst mode change 100644 => 100755 Modules/_testcapimodule.c mode change 100644 => 100755 Objects/abstract.c diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst old mode 100644 new mode 100755 index fdb8bd19d21..2536c743b24 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -462,10 +462,12 @@ Buffer-related functions :c:func:`PyObject_GetBuffer`. -.. c:function:: Py_ssize_t PyBuffer_SizeFromFormat(const char *) +.. c:function:: Py_ssize_t PyBuffer_SizeFromFormat(const char *format) Return the implied :c:data:`~Py_buffer.itemsize` from :c:data:`~Py_buffer.format`. - This function is not yet implemented. + On error, raise an exception and return -1. + + .. versionadded:: 3.9 .. c:function:: int PyBuffer_IsContiguous(Py_buffer *view, char order) diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h old mode 100644 new mode 100755 index 62a113fc00e..04e4a9e7bd2 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -243,7 +243,7 @@ PyAPI_FUNC(void *) PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices); /* Return the implied itemsize of the data-format area from a struct-style description. */ -PyAPI_FUNC(int) PyBuffer_SizeFromFormat(const char *); +PyAPI_FUNC(Py_ssize_t) PyBuffer_SizeFromFormat(const char *format); /* Implementation in memoryobject.c */ PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, Py_buffer *view, diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py old mode 100644 new mode 100755 index 47413c03d66..5fa52bffc22 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -43,6 +43,11 @@ try: except ImportError: numpy_array = None +try: + import _testcapi +except ImportError: + _testcapi = None + SHORT_TEST = True @@ -4412,6 +4417,13 @@ class TestBufferProtocol(unittest.TestCase): x = ndarray([1,2,3], shape=[3], flags=ND_GETBUF_FAIL) self.assertRaises(BufferError, memoryview, x) + @support.cpython_only + def test_pybuffer_size_from_format(self): + # basic tests + for format in ('', 'ii', '3s'): + self.assertEqual(_testcapi.PyBuffer_SizeFromFormat(format), + struct.calcsize(format)) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-06-20-52-38.bpo-15913.5Sg5cv.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-06-20-52-38.bpo-15913.5Sg5cv.rst new file mode 100644 index 00000000000..0fbfcb37abb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-06-20-52-38.bpo-15913.5Sg5cv.rst @@ -0,0 +1,3 @@ +Implement :c:func:`PyBuffer_SizeFromFormat()` function (previously +documented but not implemented): call :func:`struct.calcsize`. +Patch by Joannah Nanjekye. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c old mode 100644 new mode 100755 index 8a6e741d281..84f2651641c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3363,6 +3363,26 @@ getbuffer_with_null_view(PyObject* self, PyObject *obj) Py_RETURN_NONE; } +/* PyBuffer_SizeFromFormat() */ +static PyObject * +test_PyBuffer_SizeFromFormat(PyObject *self, PyObject *args) +{ + const char *format; + Py_ssize_t result; + + if (!PyArg_ParseTuple(args, "s:test_PyBuffer_SizeFromFormat", + &format)) { + return NULL; + } + + result = PyBuffer_SizeFromFormat(format); + if (result == -1) { + return NULL; + } + + return PyLong_FromSsize_t(result); +} + /* Test that the fatal error from not having a current thread doesn't cause an infinite loop. Run via Lib/test/test_capi.py */ static PyObject * @@ -5153,6 +5173,7 @@ static PyMethodDef TestMethods[] = { {"test_pep3118_obsolete_write_locks", (PyCFunction)test_pep3118_obsolete_write_locks, METH_NOARGS}, #endif {"getbuffer_with_null_view", getbuffer_with_null_view, METH_O}, + {"PyBuffer_SizeFromFormat", test_PyBuffer_SizeFromFormat, METH_VARARGS}, {"test_buildvalue_N", test_buildvalue_N, METH_NOARGS}, {"get_args", get_args, METH_VARARGS}, {"get_kwargs", (PyCFunction)(void(*)(void))get_kwargs, METH_VARARGS|METH_KEYWORDS}, diff --git a/Objects/abstract.c b/Objects/abstract.c old mode 100644 new mode 100755 index f93d73fa757..3db56fab2c8 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -495,6 +495,48 @@ _Py_add_one_to_index_C(int nd, Py_ssize_t *index, const Py_ssize_t *shape) } } +Py_ssize_t +PyBuffer_SizeFromFormat(const char *format) +{ + PyObject *structmodule = NULL; + PyObject *calcsize = NULL; + PyObject *res = NULL; + PyObject *fmt = NULL; + Py_ssize_t itemsize = -1; + + structmodule = PyImport_ImportModule("struct"); + if (structmodule == NULL) { + return itemsize; + } + + calcsize = PyObject_GetAttrString(structmodule, "calcsize"); + if (calcsize == NULL) { + goto done; + } + + fmt = PyUnicode_FromString(format); + if (fmt == NULL) { + goto done; + } + + res = PyObject_CallFunctionObjArgs(calcsize, fmt, NULL); + if (res == NULL) { + goto done; + } + + itemsize = PyLong_AsSsize_t(res); + if (itemsize < 0) { + goto done; + } + +done: + Py_DECREF(structmodule); + Py_XDECREF(calcsize); + Py_XDECREF(fmt); + Py_XDECREF(res); + return itemsize; +} + int PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) {