From ada43c4f82b9df4e260f1368038c31cd55c4a61e Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers <7943856+bschoenmaeckers@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:59:04 +0200 Subject: [PATCH] Add `PyABIInfo_Check` to the c-api (#8941) --- crates/capi/src/modsupport.rs | 59 ++++++++++++++++++++++++++++++++++- crates/capi/src/slots.rs | 31 +----------------- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/crates/capi/src/modsupport.rs b/crates/capi/src/modsupport.rs index 469a6e639..509039777 100644 --- a/crates/capi/src/modsupport.rs +++ b/crates/capi/src/modsupport.rs @@ -3,8 +3,54 @@ use crate::object::PyTypeObject; use crate::pystate::with_vm; use crate::util::{CStrExt, FfiPtrExt}; use core::ffi::{c_char, c_int, c_long}; -use rustpython_vm::AsObject; use rustpython_vm::builtins::PyModule; +use rustpython_vm::{AsObject, PyResult, VirtualMachine}; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyABIInfo { + pub abiinfo_major_version: u8, + pub abiinfo_minor_version: u8, + pub flags: u16, + pub build_version: u32, + pub abi_version: u32, +} + +impl PyABIInfo { + pub(crate) fn is_supported( + &self, + vm: &VirtualMachine, + module_name: Option<&str>, + ) -> PyResult<()> { + const PY_ABIINFO_STABLE: u16 = 0x0001; + const PY_ABIINFO_FREETHREADED: u16 = 0x0004; + + let module_name = module_name.unwrap_or(""); + + if self.abiinfo_major_version == 0 { + return Ok(()); + } + + if self.abiinfo_major_version > 1 { + return Err( + vm.new_import_error("PyABIInfo version too high", vm.ctx.new_str(module_name)) + ); + } + + if self.flags & PY_ABIINFO_STABLE == 0 { + return Err(vm.new_import_error("not using stable ABI", vm.ctx.new_str(module_name))); + } + + if self.flags & PY_ABIINFO_FREETHREADED == 0 { + return Err(vm.new_import_error( + "incompatible with free-threaded python", + vm.ctx.new_str(module_name), + )); + } + + Ok(()) + } +} #[unsafe(no_mangle)] pub unsafe extern "C" fn PyModule_AddObjectRef( @@ -84,6 +130,17 @@ pub unsafe extern "C" fn PyModule_AddType( }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyABIInfo_Check( + info: *mut PyABIInfo, + module_name: *const c_char, +) -> c_int { + with_vm(|vm| { + let module_name = unsafe { module_name.try_as_str_opt(vm) }?; + unsafe { &*info }.is_supported(vm, module_name) + }) +} + #[cfg(test)] mod tests { use pyo3::prelude::*; diff --git a/crates/capi/src/slots.rs b/crates/capi/src/slots.rs index 73161c168..79b11c7b4 100644 --- a/crates/capi/src/slots.rs +++ b/crates/capi/src/slots.rs @@ -1,5 +1,6 @@ use crate::PyObject; use crate::methodobject::PyMethodDef; +use crate::modsupport::PyABIInfo; use crate::object::PyType_Slot; use crate::util::CStrExt; use core::ffi::{c_char, c_int, c_void}; @@ -129,36 +130,6 @@ pub union PySlotValue { pub sl_uint64: u64, } -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct PyABIInfo { - pub abiinfo_major_version: u8, - pub abiinfo_minor_version: u8, - pub flags: u16, - pub build_version: u32, - pub abi_version: u32, -} - -impl PyABIInfo { - #[allow(dead_code)] - #[must_use] - pub(crate) fn is_supported(&self) -> bool { - const PY_ABIINFO_STABLE: u16 = 0x0001; - const PY_ABIINFO_FREETHREADED: u16 = 0x0004; - - if self.abiinfo_major_version != 1 || self.abiinfo_minor_version != 0 { - return false; - } - - // Only accept abi3t - if self.flags & PY_ABIINFO_STABLE == 0 || self.flags & PY_ABIINFO_FREETHREADED == 0 { - return false; - } - - true - } -} - #[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub(crate) enum PySlotModule<'a> {