diff --git a/Doc/c-api/abstract.rst b/Doc/c-api/abstract.rst index f5df09fa7fd..1823f9d70c7 100644 --- a/Doc/c-api/abstract.rst +++ b/Doc/c-api/abstract.rst @@ -24,3 +24,4 @@ but whose items have not been set to some non-\ ``NULL`` value yet. mapping.rst iter.rst buffer.rst + objbuffer.rst diff --git a/Doc/c-api/objbuffer.rst b/Doc/c-api/objbuffer.rst new file mode 100644 index 00000000000..6b82a642d7e --- /dev/null +++ b/Doc/c-api/objbuffer.rst @@ -0,0 +1,55 @@ +.. highlight:: c + +Old Buffer Protocol +------------------- + +.. deprecated:: 3.0 + +These functions were part of the "old buffer protocol" API in Python 2. +In Python 3, this protocol doesn't exist anymore but the functions are still +exposed to ease porting 2.x code. They act as a compatibility wrapper +around the :ref:`new buffer protocol `, but they don't give +you control over the lifetime of the resources acquired when a buffer is +exported. + +Therefore, it is recommended that you call :c:func:`PyObject_GetBuffer` +(or the ``y*`` or ``w*`` :ref:`format codes ` with the +:c:func:`PyArg_ParseTuple` family of functions) to get a buffer view over +an object, and :c:func:`PyBuffer_Release` when the buffer view can be released. + + +.. c:function:: int PyObject_AsCharBuffer(PyObject *obj, const char **buffer, Py_ssize_t *buffer_len) + + Returns a pointer to a read-only memory location usable as character-based + input. The *obj* argument must support the single-segment character buffer + interface. On success, returns ``0``, sets *buffer* to the memory location + and *buffer_len* to the buffer length. Returns ``-1`` and sets a + :exc:`TypeError` on error. + + +.. c:function:: int PyObject_AsReadBuffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len) + + Returns a pointer to a read-only memory location containing arbitrary data. + The *obj* argument must support the single-segment readable buffer + interface. On success, returns ``0``, sets *buffer* to the memory location + and *buffer_len* to the buffer length. Returns ``-1`` and sets a + :exc:`TypeError` on error. + + +.. c:function:: int PyObject_CheckReadBuffer(PyObject *o) + + Returns ``1`` if *o* supports the single-segment readable buffer interface. + Otherwise returns ``0``. This function always succeeds. + + Note that this function tries to get and release a buffer, and exceptions + which occur while calling corresponding functions will get suppressed. + To get error reporting use :c:func:`PyObject_GetBuffer()` instead. + + +.. c:function:: int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len) + + Returns a pointer to a writable memory location. The *obj* argument must + support the single-segment, character buffer interface. On success, + returns ``0``, sets *buffer* to the memory location and *buffer_len* to the + buffer length. Returns ``-1`` and sets a :exc:`TypeError` on error. + diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 8fd6c7bc7a2..9b5c00599f1 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1571,6 +1571,21 @@ PyOS_FSPath:PyObject*:path:0: PyObject_ASCII:PyObject*::+1: PyObject_ASCII:PyObject*:o:0: +PyObject_AsCharBuffer:int::: +PyObject_AsCharBuffer:PyObject*:obj:0: +PyObject_AsCharBuffer:const char**:buffer:: +PyObject_AsCharBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsReadBuffer:int::: +PyObject_AsReadBuffer:PyObject*:obj:0: +PyObject_AsReadBuffer:const void**:buffer:: +PyObject_AsReadBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsWriteBuffer:int::: +PyObject_AsWriteBuffer:PyObject*:obj:0: +PyObject_AsWriteBuffer:void**:buffer:: +PyObject_AsWriteBuffer:Py_ssize_t*:buffer_len:: + PyObject_Bytes:PyObject*::+1: PyObject_Bytes:PyObject*:o:0: @@ -1606,6 +1621,9 @@ PyObject_CallObject:PyObject*:args:0: PyObject_CheckBuffer:int::: PyObject_CheckBuffer:PyObject*:obj:0: +PyObject_CheckReadBuffer:int::: +PyObject_CheckReadBuffer:PyObject*:o:0: + PyObject_DelAttr:int::: PyObject_DelAttr:PyObject*:o:0: PyObject_DelAttr:PyObject*:attr_name:0: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index c7dde01340a..4938459d317 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -462,7 +462,10 @@ function,PyOS_strtoul,3.2, function,PyOS_vsnprintf,3.2, type,PyObject,3.2, function,PyObject_ASCII,3.2, +function,PyObject_AsCharBuffer,3.2, function,PyObject_AsFileDescriptor,3.2, +function,PyObject_AsReadBuffer,3.2, +function,PyObject_AsWriteBuffer,3.2, function,PyObject_Bytes,3.2, function,PyObject_Call,3.2, function,PyObject_CallFunction,3.2, @@ -472,6 +475,7 @@ function,PyObject_CallMethodObjArgs,3.2, function,PyObject_CallNoArgs,3.10, function,PyObject_CallObject,3.2, function,PyObject_Calloc,3.7, +function,PyObject_CheckReadBuffer,3.2, function,PyObject_ClearWeakRefs,3.2, function,PyObject_DelItem,3.2, function,PyObject_DelItemString,3.2, diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 63c7c2ac4ed..52901506362 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -2113,11 +2113,6 @@ Deprecated Removed ------- -* ``PyObject_AsCharBuffer()``, ``PyObject_AsReadBuffer()``, ``PyObject_CheckReadBuffer()``, - and ``PyObject_AsWriteBuffer()`` are removed. Please migrate to new buffer protocol; - :c:func:`PyObject_GetBuffer` and :c:func:`PyBuffer_Release`. - (Contributed by Inada Naoki in :issue:`41103`.) - * Removed ``Py_UNICODE_str*`` functions manipulating ``Py_UNICODE*`` strings. (Contributed by Inada Naoki in :issue:`41123`.) diff --git a/Include/abstract.h b/Include/abstract.h index 1af1487deec..929861e4a50 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -309,6 +309,53 @@ PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, const char *key); PyAPI_FUNC(int) PyObject_DelItem(PyObject *o, PyObject *key); +/* === Old Buffer API ============================================ */ + +/* FIXME: usage of these should all be replaced in Python itself + but for backwards compatibility we will implement them. + Their usage without a corresponding "unlock" mechanism + may create issues (but they would already be there). */ + +/* Takes an arbitrary object which must support the (character, single segment) + buffer interface and returns a pointer to a read-only memory location + useable as character based input for subsequent processing. + + Return 0 on success. buffer and buffer_len are only set in case no error + occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + Py_ssize_t *buffer_len); + +/* Checks whether an arbitrary object supports the (character, single segment) + buffer interface. + + Returns 1 on success, 0 on failure. */ +Py_DEPRECATED(3.0) PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *obj); + +/* Same as PyObject_AsCharBuffer() except that this API expects (readable, + single segment) buffer interface and returns a pointer to a read-only memory + location which can contain arbitrary data. + + 0 is returned on success. buffer and buffer_len are only set in case no + error occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + Py_ssize_t *buffer_len); + +/* Takes an arbitrary object which must support the (writable, single segment) + buffer interface and returns a pointer to a writable memory location in + buffer of size 'buffer_len'. + + Return 0 on success. buffer and buffer_len are only set in case no error + occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *obj, + void **buffer, + Py_ssize_t *buffer_len); + + /* === New Buffer API ============================================ */ /* Takes an arbitrary object and returns the result of calling diff --git a/Misc/NEWS.d/next/C API/2021-07-29-16-04-28.bpo-41103.hiKKcF.rst b/Misc/NEWS.d/next/C API/2021-07-29-16-04-28.bpo-41103.hiKKcF.rst new file mode 100644 index 00000000000..af06654ba20 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2021-07-29-16-04-28.bpo-41103.hiKKcF.rst @@ -0,0 +1,2 @@ +Reverts removal of the old buffer protocol because they are part of stable +ABI. diff --git a/Misc/stable_abi.txt b/Misc/stable_abi.txt index 62c0cdcc5d4..ba16669bd29 100644 --- a/Misc/stable_abi.txt +++ b/Misc/stable_abi.txt @@ -1642,6 +1642,17 @@ function _Py_VaBuildValue_SizeT added 3.2 abi_only +# Old buffer protocol support (deprecated) + +function PyObject_AsCharBuffer + added 3.2 +function PyObject_AsReadBuffer + added 3.2 +function PyObject_AsWriteBuffer + added 3.2 +function PyObject_CheckReadBuffer + added 3.2 + # Flags are implicitly part of the ABI: const Py_TPFLAGS_DEFAULT diff --git a/Objects/abstract.c b/Objects/abstract.c index 842c36780de..8ad1910fb1b 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -293,6 +293,85 @@ PyObject_CheckBuffer(PyObject *obj) } +/* We release the buffer right after use of this function which could + cause issues later on. Don't use these functions in new code. + */ +int +PyObject_CheckReadBuffer(PyObject *obj) +{ + PyBufferProcs *pb = Py_TYPE(obj)->tp_as_buffer; + Py_buffer view; + + if (pb == NULL || + pb->bf_getbuffer == NULL) + return 0; + if ((*pb->bf_getbuffer)(obj, &view, PyBUF_SIMPLE) == -1) { + PyErr_Clear(); + return 0; + } + PyBuffer_Release(&view); + return 1; +} + +static int +as_read_buffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len) +{ + Py_buffer view; + + if (obj == NULL || buffer == NULL || buffer_len == NULL) { + null_error(); + return -1; + } + if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) + return -1; + + *buffer = view.buf; + *buffer_len = view.len; + PyBuffer_Release(&view); + return 0; +} + +int +PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + Py_ssize_t *buffer_len) +{ + return as_read_buffer(obj, (const void **)buffer, buffer_len); +} + +int PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + Py_ssize_t *buffer_len) +{ + return as_read_buffer(obj, buffer, buffer_len); +} + +int PyObject_AsWriteBuffer(PyObject *obj, + void **buffer, + Py_ssize_t *buffer_len) +{ + PyBufferProcs *pb; + Py_buffer view; + + if (obj == NULL || buffer == NULL || buffer_len == NULL) { + null_error(); + return -1; + } + pb = Py_TYPE(obj)->tp_as_buffer; + if (pb == NULL || + pb->bf_getbuffer == NULL || + ((*pb->bf_getbuffer)(obj, &view, PyBUF_WRITABLE) != 0)) { + PyErr_SetString(PyExc_TypeError, + "expected a writable bytes-like object"); + return -1; + } + + *buffer = view.buf; + *buffer_len = view.len; + PyBuffer_Release(&view); + return 0; +} + /* Buffer C-API for Python 3.0 */ int diff --git a/PC/python3dll.c b/PC/python3dll.c index 1659e9fc64f..e0a78294a65 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -413,8 +413,11 @@ EXPORT_FUNC(PyNumber_Subtract) EXPORT_FUNC(PyNumber_ToBase) EXPORT_FUNC(PyNumber_TrueDivide) EXPORT_FUNC(PyNumber_Xor) +EXPORT_FUNC(PyObject_AsCharBuffer) EXPORT_FUNC(PyObject_ASCII) EXPORT_FUNC(PyObject_AsFileDescriptor) +EXPORT_FUNC(PyObject_AsReadBuffer) +EXPORT_FUNC(PyObject_AsWriteBuffer) EXPORT_FUNC(PyObject_Bytes) EXPORT_FUNC(PyObject_Call) EXPORT_FUNC(PyObject_CallFunction) @@ -424,6 +427,7 @@ EXPORT_FUNC(PyObject_CallMethodObjArgs) EXPORT_FUNC(PyObject_CallNoArgs) EXPORT_FUNC(PyObject_CallObject) EXPORT_FUNC(PyObject_Calloc) +EXPORT_FUNC(PyObject_CheckReadBuffer) EXPORT_FUNC(PyObject_ClearWeakRefs) EXPORT_FUNC(PyObject_DelItem) EXPORT_FUNC(PyObject_DelItemString)