Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/5746.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added FFI wrappers for the PyABIInfo and PyModExport APIs available in Python 3.15.
56 changes: 45 additions & 11 deletions pyo3-ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ fn main() {

**`src/lib.rs`**
```rust,no_run
#[cfg(Py_3_15)]
use std::ffi::c_void;
use std::ffi::{c_char, c_long};
use std::ptr;

use pyo3_ffi::*;

#[cfg(not(Py_3_15))]
static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: c"string_sum".as_ptr(),
Expand All @@ -95,16 +98,39 @@ static mut METHODS: [PyMethodDef; 2] = [
PyMethodDef::zeroed(),
];

const SLOTS_LEN: usize = 1 + if cfg!(Py_3_12) { 1 } else { 0 } + if cfg!(Py_GIL_DISABLED) { 1 } else { 0 };
#[cfg(Py_3_15)]
PyABIInfo_VAR!(ABI_INFO);

const SLOTS_LEN: usize =
1 + cfg!(Py_3_12) as usize + cfg!(Py_GIL_DISABLED) as usize + 4 * (cfg!(Py_3_15) as usize);
static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [
// NB: only include this slot if the module does not store any global state in `static` variables
// or other data which could cross between subinterpreters
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_abi,
value: std::ptr::addr_of_mut!(ABI_INFO).cast(),
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_name,
// safety: Python does not write to this field
value: c"string_sum".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_doc,
// safety: Python does not write to this field
value: c"A Python module written in Rust.".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_methods,
value: std::ptr::addr_of_mut!(METHODS).cast(),
},
#[cfg(Py_3_12)]
PyModuleDef_Slot {
slot: Py_mod_multiple_interpreters,
value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED,
},
// NB: only include this slot if the module does not depend on the GIL for thread safety
#[cfg(Py_GIL_DISABLED)]
PyModuleDef_Slot {
slot: Py_mod_gil,
Expand All @@ -116,13 +142,21 @@ static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [
},
];

// The module initialization function, which must be named `PyInit_<your_module>`.
#[allow(non_snake_case)]
// The module initialization function
#[cfg(not(Py_3_15))]
#[allow(non_snake_case, reason = "must be named `PyInit_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF))
}

#[cfg(Py_3_15)]
#[allow(non_snake_case, reason = "must be named `PyModExport_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyModExport_string_sum() -> *mut PyModuleDef_Slot {
std::ptr::addr_of_mut!(SLOTS).cast()
}

/// A helper to parse function arguments
/// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
Expand All @@ -140,7 +174,10 @@ unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
let mut overflow = 0;
let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);

#[allow(irrefutable_let_patterns)] // some platforms have c_long equal to i32
#[allow(
irrefutable_let_patterns,
reason = "some platforms have c_long equal to i32"
)]
if overflow != 0 {
raise_overflowerror(obj);
None
Expand Down Expand Up @@ -200,10 +237,7 @@ pub unsafe extern "C" fn sum_as_string(
PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
}
None => {
PyErr_SetString(
PyExc_OverflowError,
c"arguments too large to add".as_ptr(),
);
PyErr_SetString(PyExc_OverflowError, c"arguments too large to add".as_ptr());
std::ptr::null_mut()
}
}
Expand Down
11 changes: 11 additions & 0 deletions pyo3-ffi/examples/sequential/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ use pyo3_ffi::*;

mod id;
mod module;
#[cfg(not(Py_3_15))]
use crate::module::MODULE_DEF;
#[cfg(Py_3_15)]
use crate::module::SEQUENTIAL_SLOTS;

#[cfg(not(Py_3_15))]
#[allow(non_snake_case, reason = "must be named `PyInit_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyInit_sequential() -> *mut PyObject {
PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF))
}

#[cfg(Py_3_15)]
#[allow(non_snake_case, reason = "must be named `PyModExport_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyModExport_sequential() -> *mut PyModuleDef_Slot {
ptr::addr_of_mut!(SEQUENTIAL_SLOTS).cast()
}
45 changes: 43 additions & 2 deletions pyo3-ffi/examples/sequential/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::{mem, ptr};
use pyo3_ffi::*;
use std::ffi::{c_int, c_void};

#[cfg(not(Py_3_15))]
pub static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: c"sequential".as_ptr(),
Expand All @@ -14,9 +15,49 @@ pub static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_free: Some(sequential_free),
};

#[cfg(Py_3_15)]
PyABIInfo_VAR!(ABI_INFO);

const SEQUENTIAL_SLOTS_LEN: usize =
2 + if cfg!(Py_3_12) { 1 } else { 0 } + if cfg!(Py_GIL_DISABLED) { 1 } else { 0 };
static mut SEQUENTIAL_SLOTS: [PyModuleDef_Slot; SEQUENTIAL_SLOTS_LEN] = [
2 + cfg!(Py_3_12) as usize + cfg!(Py_GIL_DISABLED) as usize + 7 * (cfg!(Py_3_15) as usize);
pub static mut SEQUENTIAL_SLOTS: [PyModuleDef_Slot; SEQUENTIAL_SLOTS_LEN] = [
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_abi,
value: std::ptr::addr_of_mut!(ABI_INFO).cast(),
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_name,
// safety: Python does not write to this field
value: c"sequential".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_doc,
// safety: Python does not write to this field
value: c"A library for generating sequential ids, written in Rust.".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_state_size,
value: mem::size_of::<sequential_state>() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_state_traverse,
value: sequential_traverse as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_state_clear,
value: sequential_clear as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_state_free,
value: sequential_free as *mut c_void,
},
PyModuleDef_Slot {
slot: Py_mod_exec,
value: sequential_exec as *mut c_void,
Expand Down
38 changes: 37 additions & 1 deletion pyo3-ffi/examples/string-sum/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#[cfg(Py_3_15)]
use std::ffi::c_void;
use std::ffi::{c_char, c_long};
use std::ptr;

use pyo3_ffi::*;

#[cfg(not(Py_3_15))]
static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: c"string_sum".as_ptr(),
Expand All @@ -28,9 +31,34 @@ static mut METHODS: [PyMethodDef; 2] = [
PyMethodDef::zeroed(),
];

#[cfg(Py_3_15)]
PyABIInfo_VAR!(ABI_INFO);

const SLOTS_LEN: usize =
1 + if cfg!(Py_3_12) { 1 } else { 0 } + if cfg!(Py_GIL_DISABLED) { 1 } else { 0 };
1 + cfg!(Py_3_12) as usize + cfg!(Py_GIL_DISABLED) as usize + 4 * (cfg!(Py_3_15) as usize);
static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_abi,
value: std::ptr::addr_of_mut!(ABI_INFO).cast(),
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_name,
// safety: Python does not write to this field
value: c"string_sum".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_doc,
// safety: Python does not write to this field
value: c"A Python module written in Rust.".as_ptr() as *mut c_void,
},
#[cfg(Py_3_15)]
PyModuleDef_Slot {
slot: Py_mod_methods,
value: std::ptr::addr_of_mut!(METHODS).cast(),
},
#[cfg(Py_3_12)]
PyModuleDef_Slot {
slot: Py_mod_multiple_interpreters,
Expand All @@ -48,12 +76,20 @@ static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [
];

// The module initialization function
#[cfg(not(Py_3_15))]
#[allow(non_snake_case, reason = "must be named `PyInit_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF))
}

#[cfg(Py_3_15)]
#[allow(non_snake_case, reason = "must be named `PyModExport_<your_module>`")]
#[no_mangle]
pub unsafe extern "C" fn PyModExport_string_sum() -> *mut PyModuleDef_Slot {
std::ptr::addr_of_mut!(SLOTS).cast()
}

/// A helper to parse function arguments
/// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
Expand Down
86 changes: 65 additions & 21 deletions pyo3-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,24 +126,27 @@
//!
//! **`src/lib.rs`**
//! ```rust,no_run
//! #[cfg(Py_3_15)]
//! use std::ffi::c_void;
//! use std::ffi::{c_char, c_long};
//! use std::ptr;
//!
//! use pyo3_ffi::*;
//!
//! #[cfg(not(Py_3_15))]
//! static mut MODULE_DEF: PyModuleDef = PyModuleDef {
//! m_base: PyModuleDef_HEAD_INIT,
//! m_name: c"string_sum".as_ptr(),
//! m_doc: c"A Python module written in Rust.".as_ptr(),
//! m_size: 0,
//! m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
//! m_slots: std::ptr::null_mut(),
//! m_methods: std::ptr::addr_of_mut!(METHODS).cast(),
//! m_slots: std::ptr::addr_of_mut!(SLOTS).cast(),
//! m_traverse: None,
//! m_clear: None,
//! m_free: None,
//! };
//!
//! static mut METHODS: &[PyMethodDef] = &[
//! static mut METHODS: [PyMethodDef; 2] = [
//! PyMethodDef {
//! ml_name: c"sum_as_string".as_ptr(),
//! ml_meth: PyMethodDefPointer {
Expand All @@ -156,22 +159,63 @@
//! PyMethodDef::zeroed(),
//! ];
//!
//! // The module initialization function.
//! #[cfg(Py_3_15)]
//! PyABIInfo_VAR!(ABI_INFO);
//!
//! const SLOTS_LEN: usize =
//! 1 + cfg!(Py_3_12) as usize + cfg!(Py_GIL_DISABLED) as usize + 4 * (cfg!(Py_3_15) as usize);
//! static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [
//! #[cfg(Py_3_15)]
//! PyModuleDef_Slot {
//! slot: Py_mod_abi,
//! value: std::ptr::addr_of_mut!(ABI_INFO).cast(),
//! },
//! #[cfg(Py_3_15)]
//! PyModuleDef_Slot {
//! slot: Py_mod_name,
//! // safety: Python does not write to this field
//! value: c"string_sum".as_ptr() as *mut c_void,
//! },
//! #[cfg(Py_3_15)]
//! PyModuleDef_Slot {
//! slot: Py_mod_doc,
//! // safety: Python does not write to this field
//! value: c"A Python module written in Rust.".as_ptr() as *mut c_void,
//! },
//! #[cfg(Py_3_15)]
//! PyModuleDef_Slot {
//! slot: Py_mod_methods,
//! value: std::ptr::addr_of_mut!(METHODS).cast(),
//! },
//! #[cfg(Py_3_12)]
//! PyModuleDef_Slot {
//! slot: Py_mod_multiple_interpreters,
//! value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED,
//! },
//! #[cfg(Py_GIL_DISABLED)]
//! PyModuleDef_Slot {
//! slot: Py_mod_gil,
//! value: Py_MOD_GIL_NOT_USED,
//! },
//! PyModuleDef_Slot {
//! slot: 0,
//! value: ptr::null_mut(),
//! },
//! ];
//!
//! // The module initialization function
//! #[cfg(not(Py_3_15))]
//! #[allow(non_snake_case, reason = "must be named `PyInit_<your_module>`")]
//! #[no_mangle]
//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
//! let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF));
//! if module.is_null() {
//! return module;
//! }
//! #[cfg(Py_GIL_DISABLED)]
//! {
//! if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 {
//! Py_DECREF(module);
//! return std::ptr::null_mut();
//! }
//! }
//! module
//! PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF))
//! }
//!
//! #[cfg(Py_3_15)]
//! #[allow(non_snake_case, reason = "must be named `PyModExport_<your_module>`")]
//! #[no_mangle]
//! pub unsafe extern "C" fn PyModExport_string_sum() -> *mut PyModuleDef_Slot {
//! std::ptr::addr_of_mut!(SLOTS).cast()
//! }
//!
//! /// A helper to parse function arguments
Expand All @@ -191,7 +235,10 @@
//! let mut overflow = 0;
//! let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);
//!
//! #[allow(irrefutable_let_patterns, reason = "some platforms have c_long equal to i32")]
//! #[allow(
//! irrefutable_let_patterns,
//! reason = "some platforms have c_long equal to i32"
//! )]
//! if overflow != 0 {
//! raise_overflowerror(obj);
//! None
Expand Down Expand Up @@ -251,10 +298,7 @@
//! PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
//! }
//! None => {
//! PyErr_SetString(
//! PyExc_OverflowError,
//! c"arguments too large to add".as_ptr(),
//! );
//! PyErr_SetString(PyExc_OverflowError, c"arguments too large to add".as_ptr());
//! std::ptr::null_mut()
//! }
//! }
Expand Down
Loading
Loading