Skip to content

Commit 3beb122

Browse files
authored
Merge pull request #2150 from davidhewitt/python-3.11-buffer
python-3.11: support buffer API on abi3
2 parents fc2b0f8 + f75579a commit 3beb122

File tree

12 files changed

+211
-25
lines changed

12 files changed

+211
-25
lines changed

guide/src/building_and_distribution.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ not work when compiling for `abi3`. These are:
163163

164164
- `#[pyo3(text_signature = "...")]` does not work on classes until Python 3.10 or greater.
165165
- The `dict` and `weakref` options on classes are not supported until Python 3.9 or greater.
166-
- The buffer API is not supported.
166+
- The buffer API is not supported until Python 3.11 or greater.
167167
- Optimizations which rely on knowledge of the exact Python version compiled against.
168168

169169
## Embedding Python in Rust

pyo3-ffi/src/buffer.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use crate::object::PyObject;
2+
use crate::pyport::Py_ssize_t;
3+
use std::os::raw::{c_char, c_int, c_void};
4+
use std::ptr;
5+
6+
#[cfg(PyPy)]
7+
const Py_MAX_NDIMS: usize = 36;
8+
9+
#[repr(C)]
10+
#[derive(Copy, Clone)]
11+
pub struct Py_buffer {
12+
pub buf: *mut c_void,
13+
/// Owned reference
14+
pub obj: *mut crate::PyObject,
15+
pub len: Py_ssize_t,
16+
pub itemsize: Py_ssize_t,
17+
pub readonly: c_int,
18+
pub ndim: c_int,
19+
pub format: *mut c_char,
20+
pub shape: *mut Py_ssize_t,
21+
pub strides: *mut Py_ssize_t,
22+
pub suboffsets: *mut Py_ssize_t,
23+
pub internal: *mut c_void,
24+
#[cfg(PyPy)]
25+
pub flags: c_int,
26+
#[cfg(PyPy)]
27+
pub _strides: [Py_ssize_t; Py_MAX_NDIMS],
28+
#[cfg(PyPy)]
29+
pub _shape: [Py_ssize_t; Py_MAX_NDIMS],
30+
}
31+
32+
impl Py_buffer {
33+
pub const fn new() -> Self {
34+
Py_buffer {
35+
buf: ptr::null_mut(),
36+
obj: ptr::null_mut(),
37+
len: 0,
38+
itemsize: 0,
39+
readonly: 0,
40+
ndim: 0,
41+
format: ptr::null_mut(),
42+
shape: ptr::null_mut(),
43+
strides: ptr::null_mut(),
44+
suboffsets: ptr::null_mut(),
45+
internal: ptr::null_mut(),
46+
#[cfg(PyPy)]
47+
flags: 0,
48+
#[cfg(PyPy)]
49+
_strides: [0; Py_MAX_NDIMS],
50+
#[cfg(PyPy)]
51+
_shape: [0; Py_MAX_NDIMS],
52+
}
53+
}
54+
}
55+
56+
/* Return 1 if the getbuffer function is available, otherwise return 0. */
57+
extern "C" {
58+
#[cfg(not(PyPy))]
59+
pub fn PyObject_CheckBuffer(obj: *mut PyObject) -> c_int;
60+
61+
#[cfg_attr(PyPy, link_name = "PyPyObject_GetBuffer")]
62+
pub fn PyObject_GetBuffer(obj: *mut PyObject, view: *mut Py_buffer, flags: c_int) -> c_int;
63+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_GetPointer")]
64+
pub fn PyBuffer_GetPointer(view: *const Py_buffer, indices: *const Py_ssize_t) -> *mut c_void;
65+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_SizeFromFormat")]
66+
pub fn PyBuffer_SizeFromFormat(format: *const c_char) -> Py_ssize_t;
67+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_ToContiguous")]
68+
pub fn PyBuffer_ToContiguous(
69+
buf: *mut c_void,
70+
view: *const Py_buffer,
71+
len: Py_ssize_t,
72+
order: c_char,
73+
) -> c_int;
74+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FromContiguous")]
75+
pub fn PyBuffer_FromContiguous(
76+
view: *const Py_buffer,
77+
buf: *const c_void,
78+
len: Py_ssize_t,
79+
order: c_char,
80+
) -> c_int;
81+
pub fn PyObject_CopyData(dest: *mut PyObject, src: *mut PyObject) -> c_int;
82+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_IsContiguous")]
83+
pub fn PyBuffer_IsContiguous(view: *const Py_buffer, fort: c_char) -> c_int;
84+
pub fn PyBuffer_FillContiguousStrides(
85+
ndims: c_int,
86+
shape: *mut Py_ssize_t,
87+
strides: *mut Py_ssize_t,
88+
itemsize: c_int,
89+
fort: c_char,
90+
);
91+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FillInfo")]
92+
pub fn PyBuffer_FillInfo(
93+
view: *mut Py_buffer,
94+
o: *mut PyObject,
95+
buf: *mut c_void,
96+
len: Py_ssize_t,
97+
readonly: c_int,
98+
flags: c_int,
99+
) -> c_int;
100+
#[cfg_attr(PyPy, link_name = "PyPyBuffer_Release")]
101+
pub fn PyBuffer_Release(view: *mut Py_buffer);
102+
}
103+
104+
/// Maximum number of dimensions
105+
pub const PyBUF_MAX_NDIM: c_int = 64;
106+
107+
/* Flags for getting buffers */
108+
pub const PyBUF_SIMPLE: c_int = 0;
109+
pub const PyBUF_WRITABLE: c_int = 0x0001;
110+
/* we used to include an E, backwards compatible alias */
111+
pub const PyBUF_WRITEABLE: c_int = PyBUF_WRITABLE;
112+
pub const PyBUF_FORMAT: c_int = 0x0004;
113+
pub const PyBUF_ND: c_int = 0x0008;
114+
pub const PyBUF_STRIDES: c_int = 0x0010 | PyBUF_ND;
115+
pub const PyBUF_C_CONTIGUOUS: c_int = 0x0020 | PyBUF_STRIDES;
116+
pub const PyBUF_F_CONTIGUOUS: c_int = 0x0040 | PyBUF_STRIDES;
117+
pub const PyBUF_ANY_CONTIGUOUS: c_int = 0x0080 | PyBUF_STRIDES;
118+
pub const PyBUF_INDIRECT: c_int = 0x0100 | PyBUF_STRIDES;
119+
120+
pub const PyBUF_CONTIG: c_int = PyBUF_ND | PyBUF_WRITABLE;
121+
pub const PyBUF_CONTIG_RO: c_int = PyBUF_ND;
122+
123+
pub const PyBUF_STRIDED: c_int = PyBUF_STRIDES | PyBUF_WRITABLE;
124+
pub const PyBUF_STRIDED_RO: c_int = PyBUF_STRIDES;
125+
126+
pub const PyBUF_RECORDS: c_int = PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT;
127+
pub const PyBUF_RECORDS_RO: c_int = PyBUF_STRIDES | PyBUF_FORMAT;
128+
129+
pub const PyBUF_FULL: c_int = PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT;
130+
pub const PyBUF_FULL_RO: c_int = PyBUF_INDIRECT | PyBUF_FORMAT;
131+
132+
pub const PyBUF_READ: c_int = 0x100;
133+
pub const PyBUF_WRITE: c_int = 0x200;

pyo3-ffi/src/cpython/abstract_.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use crate::{PyObject, Py_buffer, Py_ssize_t};
2-
use std::os::raw::{c_char, c_int, c_void};
1+
use crate::{PyObject, Py_ssize_t};
2+
use std::os::raw::{c_char, c_int};
3+
4+
#[cfg(not(Py_3_11))]
5+
use crate::Py_buffer;
36

47
#[cfg(all(Py_3_8, not(PyPy)))]
58
use crate::{
@@ -211,6 +214,7 @@ extern "C" {
211214
#[cfg_attr(PyPy, link_name = "PyPyObject_LengthHint")]
212215
pub fn PyObject_LengthHint(o: *mut PyObject, arg1: Py_ssize_t) -> Py_ssize_t;
213216

217+
#[cfg(not(Py_3_11))] // moved to src/buffer.rs from 3.11
214218
#[cfg(all(Py_3_9, not(PyPy)))]
215219
pub fn PyObject_CheckBuffer(obj: *mut PyObject) -> c_int;
216220
}
@@ -222,24 +226,28 @@ pub unsafe fn PyObject_CheckBuffer(o: *mut PyObject) -> c_int {
222226
(!tp_as_buffer.is_null() && (*tp_as_buffer).bf_getbuffer.is_some()) as c_int
223227
}
224228

229+
#[cfg(not(Py_3_11))] // moved to src/buffer.rs from 3.11
225230
extern "C" {
226231
#[cfg_attr(PyPy, link_name = "PyPyObject_GetBuffer")]
227232
pub fn PyObject_GetBuffer(obj: *mut PyObject, view: *mut Py_buffer, flags: c_int) -> c_int;
228233
#[cfg_attr(PyPy, link_name = "PyPyBuffer_GetPointer")]
229-
pub fn PyBuffer_GetPointer(view: *mut Py_buffer, indices: *mut Py_ssize_t) -> *mut c_void;
234+
pub fn PyBuffer_GetPointer(
235+
view: *mut Py_buffer,
236+
indices: *mut Py_ssize_t,
237+
) -> *mut std::os::raw::c_void;
230238
#[cfg_attr(PyPy, link_name = "PyPyBuffer_SizeFromFormat")]
231239
pub fn PyBuffer_SizeFromFormat(format: *const c_char) -> Py_ssize_t;
232240
#[cfg_attr(PyPy, link_name = "PyPyBuffer_ToContiguous")]
233241
pub fn PyBuffer_ToContiguous(
234-
buf: *mut c_void,
242+
buf: *mut std::os::raw::c_void,
235243
view: *mut Py_buffer,
236244
len: Py_ssize_t,
237245
order: c_char,
238246
) -> c_int;
239247
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FromContiguous")]
240248
pub fn PyBuffer_FromContiguous(
241249
view: *mut Py_buffer,
242-
buf: *mut c_void,
250+
buf: *mut std::os::raw::c_void,
243251
len: Py_ssize_t,
244252
order: c_char,
245253
) -> c_int;
@@ -257,7 +265,7 @@ extern "C" {
257265
pub fn PyBuffer_FillInfo(
258266
view: *mut Py_buffer,
259267
o: *mut PyObject,
260-
buf: *mut c_void,
268+
buf: *mut std::os::raw::c_void,
261269
len: Py_ssize_t,
262270
readonly: c_int,
263271
flags: c_int,

pyo3-ffi/src/cpython/object.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::os::raw::c_int;
1111
// skipped _Py_static_string
1212
// skipped _Py_IDENTIFIER
1313

14+
#[cfg(not(Py_3_11))] // moved to src/buffer.rs from Python
1415
mod bufferinfo {
1516
use crate::Py_ssize_t;
1617
use std::os::raw::{c_char, c_int, c_void};
@@ -106,6 +107,7 @@ mod bufferinfo {
106107
pub const PyBUF_WRITE: c_int = 0x200;
107108
}
108109

110+
#[cfg(not(Py_3_11))]
109111
pub use self::bufferinfo::*;
110112

111113
#[cfg(Py_3_8)]
@@ -199,8 +201,8 @@ mod typeobject {
199201
#[repr(C)]
200202
#[derive(Clone, Default)]
201203
pub struct PyBufferProcs {
202-
pub bf_getbuffer: Option<super::getbufferproc>,
203-
pub bf_releasebuffer: Option<super::releasebufferproc>,
204+
pub bf_getbuffer: Option<crate::getbufferproc>,
205+
pub bf_releasebuffer: Option<crate::releasebufferproc>,
204206
}
205207

206208
pub type printfunc =

pyo3-ffi/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
//! ml_flags: METH_FASTCALL,
122122
//! ml_doc: "returns the sum of two integers as a string\0".as_ptr() as *const c_char,
123123
//! };
124-
//!
124+
//!
125125
//! // PyModule_AddObject can technically fail.
126126
//! // For more involved applications error checking may be necessary
127127
//! PyModule_AddObject(
@@ -268,6 +268,8 @@ macro_rules! opaque_struct {
268268
pub use self::abstract_::*;
269269
pub use self::bltinmodule::*;
270270
pub use self::boolobject::*;
271+
#[cfg(Py_3_11)]
272+
pub use self::buffer::*;
271273
pub use self::bytearrayobject::*;
272274
pub use self::bytesobject::*;
273275
pub use self::ceval::*;
@@ -332,6 +334,8 @@ mod abstract_;
332334
// skipped ast.h
333335
mod bltinmodule;
334336
mod boolobject;
337+
#[cfg(Py_3_11)]
338+
mod buffer;
335339
mod bytearrayobject;
336340
mod bytesobject;
337341
// skipped cellobject.h

pyo3-ffi/src/object.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ pub type newfunc = unsafe extern "C" fn(
162162
) -> *mut PyObject;
163163
pub type allocfunc =
164164
unsafe extern "C" fn(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyObject;
165+
#[cfg(Py_3_11)]
166+
pub type getbufferproc =
167+
unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut crate::Py_buffer, arg3: c_int) -> c_int;
168+
#[cfg(Py_3_11)]
169+
pub type releasebufferproc = unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut crate::Py_buffer);
165170

166171
#[repr(C)]
167172
#[derive(Copy, Clone)]

pyo3-ffi/src/typeslots.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::os::raw::c_int;
22

3-
#[cfg(not(Py_LIMITED_API))]
43
pub const Py_bf_getbuffer: c_int = 1;
5-
#[cfg(not(Py_LIMITED_API))]
64
pub const Py_bf_releasebuffer: c_int = 2;
75
pub const Py_mp_ass_subscript: c_int = 3;
86
pub const Py_mp_length: c_int = 4;

src/buffer.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![cfg(not(Py_LIMITED_API))]
1+
#![cfg(any(not(Py_LIMITED_API), Py_3_11))]
22
// Copyright (c) 2017 Daniel Grunwald
33
//
44
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
@@ -235,8 +235,20 @@ impl<T: Element> PyBuffer<T> {
235235
}
236236
unsafe {
237237
ffi::PyBuffer_GetPointer(
238-
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer,
239-
indices.as_ptr() as *mut usize as *mut ffi::Py_ssize_t,
238+
#[cfg(Py_3_11)]
239+
&*self.0,
240+
#[cfg(not(Py_3_11))]
241+
{
242+
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
243+
},
244+
#[cfg(Py_3_11)]
245+
{
246+
indices.as_ptr() as *const ffi::Py_ssize_t
247+
},
248+
#[cfg(not(Py_3_11))]
249+
{
250+
indices.as_ptr() as *mut ffi::Py_ssize_t
251+
},
240252
)
241253
}
242254
}
@@ -477,7 +489,12 @@ impl<T: Element> PyBuffer<T> {
477489
py,
478490
ffi::PyBuffer_ToContiguous(
479491
target.as_ptr() as *mut raw::c_void,
480-
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer,
492+
#[cfg(Py_3_11)]
493+
&*self.0,
494+
#[cfg(not(Py_3_11))]
495+
{
496+
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
497+
},
481498
self.0.len,
482499
fort as std::os::raw::c_char,
483500
),
@@ -510,8 +527,13 @@ impl<T: Element> PyBuffer<T> {
510527
err::error_on_minusone(
511528
py,
512529
ffi::PyBuffer_ToContiguous(
513-
vec.as_mut_ptr() as *mut raw::c_void,
514-
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer,
530+
vec.as_ptr() as *mut raw::c_void,
531+
#[cfg(Py_3_11)]
532+
&*self.0,
533+
#[cfg(not(Py_3_11))]
534+
{
535+
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
536+
},
515537
self.0.len,
516538
fort as std::os::raw::c_char,
517539
),
@@ -564,8 +586,20 @@ impl<T: Element> PyBuffer<T> {
564586
err::error_on_minusone(
565587
py,
566588
ffi::PyBuffer_FromContiguous(
567-
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer,
568-
source.as_ptr() as *mut raw::c_void,
589+
#[cfg(Py_3_11)]
590+
&*self.0,
591+
#[cfg(not(Py_3_11))]
592+
{
593+
&*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
594+
},
595+
#[cfg(Py_3_11)]
596+
{
597+
source.as_ptr() as *const raw::c_void
598+
},
599+
#[cfg(not(Py_3_11))]
600+
{
601+
source.as_ptr() as *mut raw::c_void
602+
},
569603
self.0.len,
570604
fort as std::os::raw::c_char,
571605
),
@@ -678,6 +712,8 @@ mod tests {
678712
assert_eq!(slice[0].get(), b'a');
679713
assert_eq!(slice[2].get(), b'c');
680714

715+
assert_eq!(unsafe { *(buffer.get_ptr(&[1]) as *mut u8) }, b'b');
716+
681717
assert!(buffer.copy_to_slice(py, &mut [0u8]).is_err());
682718
let mut arr = [0; 5];
683719
buffer.copy_to_slice(py, &mut arr).unwrap();

src/class/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
mod macros;
77

88
pub mod basic;
9-
#[cfg(not(Py_LIMITED_API))]
9+
#[cfg(any(not(Py_LIMITED_API), Py_3_11))]
1010
pub mod buffer;
1111
pub mod descr;
1212
pub mod gc;
@@ -19,7 +19,7 @@ pub mod pyasync;
1919
pub mod sequence;
2020

2121
pub use self::basic::PyObjectProtocol;
22-
#[cfg(not(Py_LIMITED_API))]
22+
#[cfg(any(not(Py_LIMITED_API), Py_3_11))]
2323
pub use self::buffer::PyBufferProtocol;
2424
pub use self::descr::PyDescrProtocol;
2525
pub use self::gc::{PyGCProtocol, PyTraverseError, PyVisit};

tests/test_buffer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![cfg(feature = "macros")]
2-
#![cfg(not(Py_LIMITED_API))]
2+
#![cfg(any(not(Py_LIMITED_API), Py_3_11))]
33

44
use pyo3::{buffer::PyBuffer, exceptions::PyBufferError, ffi, prelude::*, AsPyPointer};
55
use std::{

0 commit comments

Comments
 (0)