Skip to content

Commit ce5e1a6

Browse files
authored
bpo-41103: Resurrect the old buffer protocol. (GH-27437)
Revert "bpo-41103: Remove old buffer protocol support (#21117)" This reverts commit 6f8a6ee.
1 parent d542742 commit ce5e1a6

File tree

10 files changed

+221
-5
lines changed

10 files changed

+221
-5
lines changed

Doc/c-api/abstract.rst

+1
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ but whose items have not been set to some non-\ ``NULL`` value yet.
2424
mapping.rst
2525
iter.rst
2626
buffer.rst
27+
objbuffer.rst

Doc/c-api/objbuffer.rst

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.. highlight:: c
2+
3+
Old Buffer Protocol
4+
-------------------
5+
6+
.. deprecated:: 3.0
7+
8+
These functions were part of the "old buffer protocol" API in Python 2.
9+
In Python 3, this protocol doesn't exist anymore but the functions are still
10+
exposed to ease porting 2.x code. They act as a compatibility wrapper
11+
around the :ref:`new buffer protocol <bufferobjects>`, but they don't give
12+
you control over the lifetime of the resources acquired when a buffer is
13+
exported.
14+
15+
Therefore, it is recommended that you call :c:func:`PyObject_GetBuffer`
16+
(or the ``y*`` or ``w*`` :ref:`format codes <arg-parsing>` with the
17+
:c:func:`PyArg_ParseTuple` family of functions) to get a buffer view over
18+
an object, and :c:func:`PyBuffer_Release` when the buffer view can be released.
19+
20+
21+
.. c:function:: int PyObject_AsCharBuffer(PyObject *obj, const char **buffer, Py_ssize_t *buffer_len)
22+
23+
Returns a pointer to a read-only memory location usable as character-based
24+
input. The *obj* argument must support the single-segment character buffer
25+
interface. On success, returns ``0``, sets *buffer* to the memory location
26+
and *buffer_len* to the buffer length. Returns ``-1`` and sets a
27+
:exc:`TypeError` on error.
28+
29+
30+
.. c:function:: int PyObject_AsReadBuffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len)
31+
32+
Returns a pointer to a read-only memory location containing arbitrary data.
33+
The *obj* argument must support the single-segment readable buffer
34+
interface. On success, returns ``0``, sets *buffer* to the memory location
35+
and *buffer_len* to the buffer length. Returns ``-1`` and sets a
36+
:exc:`TypeError` on error.
37+
38+
39+
.. c:function:: int PyObject_CheckReadBuffer(PyObject *o)
40+
41+
Returns ``1`` if *o* supports the single-segment readable buffer interface.
42+
Otherwise returns ``0``. This function always succeeds.
43+
44+
Note that this function tries to get and release a buffer, and exceptions
45+
which occur while calling corresponding functions will get suppressed.
46+
To get error reporting use :c:func:`PyObject_GetBuffer()` instead.
47+
48+
49+
.. c:function:: int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len)
50+
51+
Returns a pointer to a writable memory location. The *obj* argument must
52+
support the single-segment, character buffer interface. On success,
53+
returns ``0``, sets *buffer* to the memory location and *buffer_len* to the
54+
buffer length. Returns ``-1`` and sets a :exc:`TypeError` on error.
55+

Doc/data/refcounts.dat

+18
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,21 @@ PyOS_FSPath:PyObject*:path:0:
15711571
PyObject_ASCII:PyObject*::+1:
15721572
PyObject_ASCII:PyObject*:o:0:
15731573

1574+
PyObject_AsCharBuffer:int:::
1575+
PyObject_AsCharBuffer:PyObject*:obj:0:
1576+
PyObject_AsCharBuffer:const char**:buffer::
1577+
PyObject_AsCharBuffer:Py_ssize_t*:buffer_len::
1578+
1579+
PyObject_AsReadBuffer:int:::
1580+
PyObject_AsReadBuffer:PyObject*:obj:0:
1581+
PyObject_AsReadBuffer:const void**:buffer::
1582+
PyObject_AsReadBuffer:Py_ssize_t*:buffer_len::
1583+
1584+
PyObject_AsWriteBuffer:int:::
1585+
PyObject_AsWriteBuffer:PyObject*:obj:0:
1586+
PyObject_AsWriteBuffer:void**:buffer::
1587+
PyObject_AsWriteBuffer:Py_ssize_t*:buffer_len::
1588+
15741589
PyObject_Bytes:PyObject*::+1:
15751590
PyObject_Bytes:PyObject*:o:0:
15761591

@@ -1606,6 +1621,9 @@ PyObject_CallObject:PyObject*:args:0:
16061621
PyObject_CheckBuffer:int:::
16071622
PyObject_CheckBuffer:PyObject*:obj:0:
16081623

1624+
PyObject_CheckReadBuffer:int:::
1625+
PyObject_CheckReadBuffer:PyObject*:o:0:
1626+
16091627
PyObject_DelAttr:int:::
16101628
PyObject_DelAttr:PyObject*:o:0:
16111629
PyObject_DelAttr:PyObject*:attr_name:0:

Doc/data/stable_abi.dat

+4
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,10 @@ function,PyOS_strtoul,3.2,
462462
function,PyOS_vsnprintf,3.2,
463463
type,PyObject,3.2,
464464
function,PyObject_ASCII,3.2,
465+
function,PyObject_AsCharBuffer,3.2,
465466
function,PyObject_AsFileDescriptor,3.2,
467+
function,PyObject_AsReadBuffer,3.2,
468+
function,PyObject_AsWriteBuffer,3.2,
466469
function,PyObject_Bytes,3.2,
467470
function,PyObject_Call,3.2,
468471
function,PyObject_CallFunction,3.2,
@@ -472,6 +475,7 @@ function,PyObject_CallMethodObjArgs,3.2,
472475
function,PyObject_CallNoArgs,3.10,
473476
function,PyObject_CallObject,3.2,
474477
function,PyObject_Calloc,3.7,
478+
function,PyObject_CheckReadBuffer,3.2,
475479
function,PyObject_ClearWeakRefs,3.2,
476480
function,PyObject_DelItem,3.2,
477481
function,PyObject_DelItemString,3.2,

Doc/whatsnew/3.10.rst

-5
Original file line numberDiff line numberDiff line change
@@ -2113,11 +2113,6 @@ Deprecated
21132113
Removed
21142114
-------
21152115
2116-
* ``PyObject_AsCharBuffer()``, ``PyObject_AsReadBuffer()``, ``PyObject_CheckReadBuffer()``,
2117-
and ``PyObject_AsWriteBuffer()`` are removed. Please migrate to new buffer protocol;
2118-
:c:func:`PyObject_GetBuffer` and :c:func:`PyBuffer_Release`.
2119-
(Contributed by Inada Naoki in :issue:`41103`.)
2120-
21212116
* Removed ``Py_UNICODE_str*`` functions manipulating ``Py_UNICODE*`` strings.
21222117
(Contributed by Inada Naoki in :issue:`41123`.)
21232118

Include/abstract.h

+47
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,53 @@ PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, const char *key);
309309
PyAPI_FUNC(int) PyObject_DelItem(PyObject *o, PyObject *key);
310310

311311

312+
/* === Old Buffer API ============================================ */
313+
314+
/* FIXME: usage of these should all be replaced in Python itself
315+
but for backwards compatibility we will implement them.
316+
Their usage without a corresponding "unlock" mechanism
317+
may create issues (but they would already be there). */
318+
319+
/* Takes an arbitrary object which must support the (character, single segment)
320+
buffer interface and returns a pointer to a read-only memory location
321+
useable as character based input for subsequent processing.
322+
323+
Return 0 on success. buffer and buffer_len are only set in case no error
324+
occurs. Otherwise, -1 is returned and an exception set. */
325+
Py_DEPRECATED(3.0)
326+
PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj,
327+
const char **buffer,
328+
Py_ssize_t *buffer_len);
329+
330+
/* Checks whether an arbitrary object supports the (character, single segment)
331+
buffer interface.
332+
333+
Returns 1 on success, 0 on failure. */
334+
Py_DEPRECATED(3.0) PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *obj);
335+
336+
/* Same as PyObject_AsCharBuffer() except that this API expects (readable,
337+
single segment) buffer interface and returns a pointer to a read-only memory
338+
location which can contain arbitrary data.
339+
340+
0 is returned on success. buffer and buffer_len are only set in case no
341+
error occurs. Otherwise, -1 is returned and an exception set. */
342+
Py_DEPRECATED(3.0)
343+
PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *obj,
344+
const void **buffer,
345+
Py_ssize_t *buffer_len);
346+
347+
/* Takes an arbitrary object which must support the (writable, single segment)
348+
buffer interface and returns a pointer to a writable memory location in
349+
buffer of size 'buffer_len'.
350+
351+
Return 0 on success. buffer and buffer_len are only set in case no error
352+
occurs. Otherwise, -1 is returned and an exception set. */
353+
Py_DEPRECATED(3.0)
354+
PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *obj,
355+
void **buffer,
356+
Py_ssize_t *buffer_len);
357+
358+
312359
/* === New Buffer API ============================================ */
313360

314361
/* Takes an arbitrary object and returns the result of calling
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Reverts removal of the old buffer protocol because they are part of stable
2+
ABI.

Misc/stable_abi.txt

+11
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,17 @@ function _Py_VaBuildValue_SizeT
16421642
added 3.2
16431643
abi_only
16441644

1645+
# Old buffer protocol support (deprecated)
1646+
1647+
function PyObject_AsCharBuffer
1648+
added 3.2
1649+
function PyObject_AsReadBuffer
1650+
added 3.2
1651+
function PyObject_AsWriteBuffer
1652+
added 3.2
1653+
function PyObject_CheckReadBuffer
1654+
added 3.2
1655+
16451656
# Flags are implicitly part of the ABI:
16461657

16471658
const Py_TPFLAGS_DEFAULT

Objects/abstract.c

+79
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,85 @@ PyObject_CheckBuffer(PyObject *obj)
293293
}
294294

295295

296+
/* We release the buffer right after use of this function which could
297+
cause issues later on. Don't use these functions in new code.
298+
*/
299+
int
300+
PyObject_CheckReadBuffer(PyObject *obj)
301+
{
302+
PyBufferProcs *pb = Py_TYPE(obj)->tp_as_buffer;
303+
Py_buffer view;
304+
305+
if (pb == NULL ||
306+
pb->bf_getbuffer == NULL)
307+
return 0;
308+
if ((*pb->bf_getbuffer)(obj, &view, PyBUF_SIMPLE) == -1) {
309+
PyErr_Clear();
310+
return 0;
311+
}
312+
PyBuffer_Release(&view);
313+
return 1;
314+
}
315+
316+
static int
317+
as_read_buffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len)
318+
{
319+
Py_buffer view;
320+
321+
if (obj == NULL || buffer == NULL || buffer_len == NULL) {
322+
null_error();
323+
return -1;
324+
}
325+
if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0)
326+
return -1;
327+
328+
*buffer = view.buf;
329+
*buffer_len = view.len;
330+
PyBuffer_Release(&view);
331+
return 0;
332+
}
333+
334+
int
335+
PyObject_AsCharBuffer(PyObject *obj,
336+
const char **buffer,
337+
Py_ssize_t *buffer_len)
338+
{
339+
return as_read_buffer(obj, (const void **)buffer, buffer_len);
340+
}
341+
342+
int PyObject_AsReadBuffer(PyObject *obj,
343+
const void **buffer,
344+
Py_ssize_t *buffer_len)
345+
{
346+
return as_read_buffer(obj, buffer, buffer_len);
347+
}
348+
349+
int PyObject_AsWriteBuffer(PyObject *obj,
350+
void **buffer,
351+
Py_ssize_t *buffer_len)
352+
{
353+
PyBufferProcs *pb;
354+
Py_buffer view;
355+
356+
if (obj == NULL || buffer == NULL || buffer_len == NULL) {
357+
null_error();
358+
return -1;
359+
}
360+
pb = Py_TYPE(obj)->tp_as_buffer;
361+
if (pb == NULL ||
362+
pb->bf_getbuffer == NULL ||
363+
((*pb->bf_getbuffer)(obj, &view, PyBUF_WRITABLE) != 0)) {
364+
PyErr_SetString(PyExc_TypeError,
365+
"expected a writable bytes-like object");
366+
return -1;
367+
}
368+
369+
*buffer = view.buf;
370+
*buffer_len = view.len;
371+
PyBuffer_Release(&view);
372+
return 0;
373+
}
374+
296375
/* Buffer C-API for Python 3.0 */
297376

298377
int

PC/python3dll.c

+4
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,11 @@ EXPORT_FUNC(PyNumber_Subtract)
413413
EXPORT_FUNC(PyNumber_ToBase)
414414
EXPORT_FUNC(PyNumber_TrueDivide)
415415
EXPORT_FUNC(PyNumber_Xor)
416+
EXPORT_FUNC(PyObject_AsCharBuffer)
416417
EXPORT_FUNC(PyObject_ASCII)
417418
EXPORT_FUNC(PyObject_AsFileDescriptor)
419+
EXPORT_FUNC(PyObject_AsReadBuffer)
420+
EXPORT_FUNC(PyObject_AsWriteBuffer)
418421
EXPORT_FUNC(PyObject_Bytes)
419422
EXPORT_FUNC(PyObject_Call)
420423
EXPORT_FUNC(PyObject_CallFunction)
@@ -424,6 +427,7 @@ EXPORT_FUNC(PyObject_CallMethodObjArgs)
424427
EXPORT_FUNC(PyObject_CallNoArgs)
425428
EXPORT_FUNC(PyObject_CallObject)
426429
EXPORT_FUNC(PyObject_Calloc)
430+
EXPORT_FUNC(PyObject_CheckReadBuffer)
427431
EXPORT_FUNC(PyObject_ClearWeakRefs)
428432
EXPORT_FUNC(PyObject_DelItem)
429433
EXPORT_FUNC(PyObject_DelItemString)

0 commit comments

Comments
 (0)