Skip to content

Commit f66c857

Browse files
authored
bpo-45459: Add Py_buffer to limited API (pythonGH-29991)
- [x] ``Py_buffer`` struct - [x] ``PyBuffer_*()`` API functions - [x] ``PyBUF_*`` constants - [x] ``Py_bf_getbuffer`` and ``Py_bf_releasebuffer`` type slots - [x] ``PyMemoryView_FromBuffer()`` API - [x] tests for limited API - [x] ``make regen-limited-abi`` - [x] documentation update - [ ] export ``PyPickleBuffer*()`` API ???
1 parent 08f8301 commit f66c857

17 files changed

+308
-131
lines changed

Doc/c-api/buffer.rst

+7
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ Buffer-related functions
499499
This function fails if *len* != *src->len*.
500500
501501
502+
.. c:function:: int PyObject_CopyData(Py_buffer *dest, Py_buffer *src)
503+
504+
Copy data from *src* to *dest* buffer. Can convert between C-style and
505+
or Fortran-style buffers.
506+
507+
``0`` is returned on success, ``-1`` on error.
508+
502509
.. c:function:: void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, char order)
503510
504511
Fill the *strides* array with byte-strides of a :term:`contiguous` (C-style if

Doc/c-api/type.rst

+5-6
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,6 @@ The following functions and structs are used to create
272272
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
273273
(see :ref:`PyMemberDef <pymemberdef-offsets>`)
274274
275-
The following fields cannot be set using :c:type:`PyType_Spec` and
276-
:c:type:`PyType_Slot` under the limited API:
277-
278-
* :c:member:`~PyBufferProcs.bf_getbuffer`
279-
* :c:member:`~PyBufferProcs.bf_releasebuffer`
280-
281275
Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be
282276
problematic on some platforms.
283277
To avoid issues, use the *bases* argument of
@@ -287,6 +281,11 @@ The following functions and structs are used to create
287281
288282
Slots in :c:type:`PyBufferProcs` in may be set in the unlimited API.
289283
284+
.. versionchanged:: 3.11
285+
:c:member:`~PyBufferProcs.bf_getbuffer` and
286+
:c:member:`~PyBufferProcs.bf_releasebuffer` are now available
287+
under limited API.
288+
290289
.. c:member:: void *PyType_Slot.pfunc
291290
292291
The desired value of the slot. In most cases, this is a pointer

Doc/data/stable_abi.dat

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ function,PyArg_ValidateKeywordArguments,3.2,
1010
var,PyBaseObject_Type,3.2,
1111
function,PyBool_FromLong,3.2,
1212
var,PyBool_Type,3.2,
13+
function,PyBuffer_FillContiguousStrides,3.11,
14+
function,PyBuffer_FillInfo,3.11,
15+
function,PyBuffer_FromContiguous,3.11,
16+
function,PyBuffer_GetPointer,3.11,
17+
function,PyBuffer_IsContiguous,3.11,
18+
function,PyBuffer_Release,3.11,
19+
function,PyBuffer_SizeFromFormat,3.11,
20+
function,PyBuffer_ToContiguous,3.11,
1321
var,PyByteArrayIter_Type,3.2,
1422
function,PyByteArray_AsString,3.2,
1523
function,PyByteArray_Concat,3.2,
@@ -375,6 +383,7 @@ function,PyMem_Malloc,3.2,
375383
function,PyMem_Realloc,3.2,
376384
type,PyMemberDef,3.2,
377385
var,PyMemberDescr_Type,3.2,
386+
function,PyMemoryView_FromBuffer,3.11,
378387
function,PyMemoryView_FromMemory,3.7,
379388
function,PyMemoryView_FromObject,3.2,
380389
function,PyMemoryView_GetContiguous,3.2,
@@ -476,8 +485,10 @@ function,PyObject_CallMethodObjArgs,3.2,
476485
function,PyObject_CallNoArgs,3.10,
477486
function,PyObject_CallObject,3.2,
478487
function,PyObject_Calloc,3.7,
488+
function,PyObject_CheckBuffer,3.11,
479489
function,PyObject_CheckReadBuffer,3.2,
480490
function,PyObject_ClearWeakRefs,3.2,
491+
function,PyObject_CopyData,3.11,
481492
function,PyObject_DelItem,3.2,
482493
function,PyObject_DelItemString,3.2,
483494
function,PyObject_Dir,3.2,
@@ -495,6 +506,7 @@ function,PyObject_GenericSetDict,3.7,
495506
function,PyObject_GetAIter,3.10,
496507
function,PyObject_GetAttr,3.2,
497508
function,PyObject_GetAttrString,3.2,
509+
function,PyObject_GetBuffer,3.11,
498510
function,PyObject_GetItem,3.2,
499511
function,PyObject_GetIter,3.2,
500512
function,PyObject_HasAttr,3.2,
@@ -832,6 +844,7 @@ var,Py_UTF8Mode,3.8,
832844
function,Py_VaBuildValue,3.2,
833845
var,Py_Version,3.11,
834846
function,Py_XNewRef,3.10,
847+
type,Py_buffer,3.11,
835848
type,Py_intptr_t,3.2,
836849
type,Py_ssize_t,3.2,
837850
type,Py_uintptr_t,3.2,

Doc/whatsnew/3.11.rst

+20
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,26 @@ New Features
657657
:c:macro:`PY_VERSION_HEX`.
658658
(Contributed by Gabriele N. Tornetta in :issue:`43931`.)
659659

660+
* :c:type:`Py_buffer` and APIs are now part of the limited API and the stable
661+
ABI:
662+
663+
* :c:func:`PyObject_CheckBuffer`
664+
* :c:func:`PyObject_GetBuffer`
665+
* :c:func:`PyBuffer_GetPointer`
666+
* :c:func:`PyBuffer_SizeFromFormat`
667+
* :c:func:`PyBuffer_ToContiguous`
668+
* :c:func:`PyBuffer_FromContiguous`
669+
* :c:func:`PyBuffer_CopyData`
670+
* :c:func:`PyBuffer_IsContiguous`
671+
* :c:func:`PyBuffer_FillContiguousStrides`
672+
* :c:func:`PyBuffer_FillInfo`
673+
* :c:func:`PyBuffer_Release`
674+
* :c:func:`PyMemoryView_FromBuffer`
675+
* :c:member:`~PyBufferProcs.bf_getbuffer` and
676+
:c:member:`~PyBufferProcs.bf_releasebuffer` type slots
677+
678+
(Contributed by Christian Heimes in :issue:`45459`.)
679+
660680

661681
Porting to Python 3.11
662682
----------------------

Include/Python.h

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "longobject.h"
5151
#include "cpython/longintrepr.h"
5252
#include "boolobject.h"
53+
#include "buffer.h"
5354
#include "floatobject.h"
5455
#include "complexobject.h"
5556
#include "rangeobject.h"

Include/buffer.h

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/* Public Py_buffer API */
2+
3+
#ifndef Py_BUFFER_H
4+
#define Py_BUFFER_H
5+
#ifdef __cplusplus
6+
extern "C" {
7+
#endif
8+
9+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
10+
11+
/* === New Buffer API ============================================
12+
* Limited API and stable ABI since Python 3.11
13+
*
14+
* Py_buffer struct layout and size is now part of the stable abi3. The
15+
* struct layout and size must not be changed in any way, as it would
16+
* break the ABI.
17+
*
18+
*/
19+
20+
typedef struct {
21+
void *buf;
22+
PyObject *obj; /* owned reference */
23+
Py_ssize_t len;
24+
Py_ssize_t itemsize; /* This is Py_ssize_t so it can be
25+
pointed to by strides in simple case.*/
26+
int readonly;
27+
int ndim;
28+
char *format;
29+
Py_ssize_t *shape;
30+
Py_ssize_t *strides;
31+
Py_ssize_t *suboffsets;
32+
void *internal;
33+
} Py_buffer;
34+
35+
/* Return 1 if the getbuffer function is available, otherwise return 0. */
36+
PyAPI_FUNC(int) PyObject_CheckBuffer(PyObject *obj);
37+
38+
/* This is a C-API version of the getbuffer function call. It checks
39+
to make sure object has the required function pointer and issues the
40+
call.
41+
42+
Returns -1 and raises an error on failure and returns 0 on success. */
43+
PyAPI_FUNC(int) PyObject_GetBuffer(PyObject *obj, Py_buffer *view,
44+
int flags);
45+
46+
/* Get the memory area pointed to by the indices for the buffer given.
47+
Note that view->ndim is the assumed size of indices. */
48+
PyAPI_FUNC(void *) PyBuffer_GetPointer(const Py_buffer *view, const Py_ssize_t *indices);
49+
50+
/* Return the implied itemsize of the data-format area from a
51+
struct-style description. */
52+
PyAPI_FUNC(Py_ssize_t) PyBuffer_SizeFromFormat(const char *format);
53+
54+
/* Implementation in memoryobject.c */
55+
PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, const Py_buffer *view,
56+
Py_ssize_t len, char order);
57+
58+
PyAPI_FUNC(int) PyBuffer_FromContiguous(const Py_buffer *view, const void *buf,
59+
Py_ssize_t len, char order);
60+
61+
/* Copy len bytes of data from the contiguous chunk of memory
62+
pointed to by buf into the buffer exported by obj. Return
63+
0 on success and return -1 and raise a PyBuffer_Error on
64+
error (i.e. the object does not have a buffer interface or
65+
it is not working).
66+
67+
If fort is 'F', then if the object is multi-dimensional,
68+
then the data will be copied into the array in
69+
Fortran-style (first dimension varies the fastest). If
70+
fort is 'C', then the data will be copied into the array
71+
in C-style (last dimension varies the fastest). If fort
72+
is 'A', then it does not matter and the copy will be made
73+
in whatever way is more efficient. */
74+
PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src);
75+
76+
/* Copy the data from the src buffer to the buffer of destination. */
77+
PyAPI_FUNC(int) PyBuffer_IsContiguous(const Py_buffer *view, char fort);
78+
79+
/*Fill the strides array with byte-strides of a contiguous
80+
(Fortran-style if fort is 'F' or C-style otherwise)
81+
array of the given shape with the given number of bytes
82+
per element. */
83+
PyAPI_FUNC(void) PyBuffer_FillContiguousStrides(int ndims,
84+
Py_ssize_t *shape,
85+
Py_ssize_t *strides,
86+
int itemsize,
87+
char fort);
88+
89+
/* Fills in a buffer-info structure correctly for an exporter
90+
that can only share a contiguous chunk of memory of
91+
"unsigned bytes" of the given length.
92+
93+
Returns 0 on success and -1 (with raising an error) on error. */
94+
PyAPI_FUNC(int) PyBuffer_FillInfo(Py_buffer *view, PyObject *o, void *buf,
95+
Py_ssize_t len, int readonly,
96+
int flags);
97+
98+
/* Releases a Py_buffer obtained from getbuffer ParseTuple's "s*". */
99+
PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view);
100+
101+
/* Maximum number of dimensions */
102+
#define PyBUF_MAX_NDIM 64
103+
104+
/* Flags for getting buffers */
105+
#define PyBUF_SIMPLE 0
106+
#define PyBUF_WRITABLE 0x0001
107+
108+
#ifndef Py_LIMITED_API
109+
/* we used to include an E, backwards compatible alias */
110+
#define PyBUF_WRITEABLE PyBUF_WRITABLE
111+
#endif
112+
113+
#define PyBUF_FORMAT 0x0004
114+
#define PyBUF_ND 0x0008
115+
#define PyBUF_STRIDES (0x0010 | PyBUF_ND)
116+
#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES)
117+
#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES)
118+
#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES)
119+
#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES)
120+
121+
#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE)
122+
#define PyBUF_CONTIG_RO (PyBUF_ND)
123+
124+
#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE)
125+
#define PyBUF_STRIDED_RO (PyBUF_STRIDES)
126+
127+
#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT)
128+
#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT)
129+
130+
#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT)
131+
#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT)
132+
133+
134+
#define PyBUF_READ 0x100
135+
#define PyBUF_WRITE 0x200
136+
137+
#endif /* !Py_LIMITED_API || Py_LIMITED_API >= 3.11 */
138+
139+
#ifdef __cplusplus
140+
}
141+
#endif
142+
#endif /* Py_BUFFER_H */

Include/cpython/abstract.h

-68
Original file line numberDiff line numberDiff line change
@@ -168,74 +168,6 @@ PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o);
168168
value. If one of the calls fails, this function returns -1. */
169169
PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t);
170170

171-
/* === New Buffer API ============================================ */
172-
173-
/* Return 1 if the getbuffer function is available, otherwise return 0. */
174-
PyAPI_FUNC(int) PyObject_CheckBuffer(PyObject *obj);
175-
176-
/* This is a C-API version of the getbuffer function call. It checks
177-
to make sure object has the required function pointer and issues the
178-
call.
179-
180-
Returns -1 and raises an error on failure and returns 0 on success. */
181-
PyAPI_FUNC(int) PyObject_GetBuffer(PyObject *obj, Py_buffer *view,
182-
int flags);
183-
184-
/* Get the memory area pointed to by the indices for the buffer given.
185-
Note that view->ndim is the assumed size of indices. */
186-
PyAPI_FUNC(void *) PyBuffer_GetPointer(const Py_buffer *view, const Py_ssize_t *indices);
187-
188-
/* Return the implied itemsize of the data-format area from a
189-
struct-style description. */
190-
PyAPI_FUNC(Py_ssize_t) PyBuffer_SizeFromFormat(const char *format);
191-
192-
/* Implementation in memoryobject.c */
193-
PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, const Py_buffer *view,
194-
Py_ssize_t len, char order);
195-
196-
PyAPI_FUNC(int) PyBuffer_FromContiguous(const Py_buffer *view, const void *buf,
197-
Py_ssize_t len, char order);
198-
199-
/* Copy len bytes of data from the contiguous chunk of memory
200-
pointed to by buf into the buffer exported by obj. Return
201-
0 on success and return -1 and raise a PyBuffer_Error on
202-
error (i.e. the object does not have a buffer interface or
203-
it is not working).
204-
205-
If fort is 'F', then if the object is multi-dimensional,
206-
then the data will be copied into the array in
207-
Fortran-style (first dimension varies the fastest). If
208-
fort is 'C', then the data will be copied into the array
209-
in C-style (last dimension varies the fastest). If fort
210-
is 'A', then it does not matter and the copy will be made
211-
in whatever way is more efficient. */
212-
PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src);
213-
214-
/* Copy the data from the src buffer to the buffer of destination. */
215-
PyAPI_FUNC(int) PyBuffer_IsContiguous(const Py_buffer *view, char fort);
216-
217-
/*Fill the strides array with byte-strides of a contiguous
218-
(Fortran-style if fort is 'F' or C-style otherwise)
219-
array of the given shape with the given number of bytes
220-
per element. */
221-
PyAPI_FUNC(void) PyBuffer_FillContiguousStrides(int ndims,
222-
Py_ssize_t *shape,
223-
Py_ssize_t *strides,
224-
int itemsize,
225-
char fort);
226-
227-
/* Fills in a buffer-info structure correctly for an exporter
228-
that can only share a contiguous chunk of memory of
229-
"unsigned bytes" of the given length.
230-
231-
Returns 0 on success and -1 (with raising an error) on error. */
232-
PyAPI_FUNC(int) PyBuffer_FillInfo(Py_buffer *view, PyObject *o, void *buf,
233-
Py_ssize_t len, int readonly,
234-
int flags);
235-
236-
/* Releases a Py_buffer obtained from getbuffer ParseTuple's "s*". */
237-
PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view);
238-
239171
/* === Sequence protocol ================================================ */
240172

241173
/* Assume tp_as_sequence and sq_item exist and that 'i' does not

0 commit comments

Comments
 (0)