From d571a5f377da7844fb6fc0de925d897162b94a9c Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Fri, 3 Apr 2026 12:27:55 +0100 Subject: [PATCH 1/8] sync `abstract.h` bindings to Python 3.14 --- pyo3-ffi/src/abstract_.rs | 111 ++++++++++----------- pyo3-ffi/src/cpython/abstract_.rs | 160 ++++++++++++++---------------- 2 files changed, 124 insertions(+), 147 deletions(-) diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index 714e6d0d3f0..6fa00917d01 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -54,21 +54,13 @@ extern_libpython! { ... ) -> *mut PyObject; - #[cfg(not(Py_3_13))] + #[cfg(all(PyPy, not(Py_3_13)))] // called internally in PyUnicodeDecodeError_Create on PyPy #[cfg_attr(PyPy, link_name = "_PyPyObject_CallFunction_SizeT")] - pub fn _PyObject_CallFunction_SizeT( + pub(crate) fn _PyObject_CallFunction_SizeT( callable_object: *mut PyObject, format: *const c_char, ... ) -> *mut PyObject; - #[cfg(not(Py_3_13))] - #[cfg_attr(PyPy, link_name = "_PyPyObject_CallMethod_SizeT")] - pub fn _PyObject_CallMethod_SizeT( - o: *mut PyObject, - method: *const c_char, - format: *const c_char, - ... - ) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyObject_CallFunctionObjArgs")] pub fn PyObject_CallFunctionObjArgs(callable: *mut PyObject, ...) -> *mut PyObject; @@ -78,10 +70,23 @@ extern_libpython! { method: *mut PyObject, ... ) -> *mut PyObject; + + #[cfg(all(Py_3_12, Py_LIMITED_API))] // is an inline function in cpython/abstract.rs on version-specific ABI + #[cfg_attr(PyPy, link_name = "PyPyVectorcall_NARGS")] + pub fn PyVectorcall_NARGS(nargsf: size_t) -> size_t; + + #[cfg_attr(not(any(Py_3_12, PyPy)), link_name = "_PyVectorcall_Call")] // symbol made public in 3.12 + #[cfg_attr(PyPy, link_name = "PyPyVectorcall_Call")] + pub fn PyVectorcall_Call( + callable: *mut PyObject, + tuple: *mut PyObject, + dict: *mut PyObject + ) -> *mut PyObject; } + #[cfg(any(Py_3_12, not(Py_LIMITED_API)))] pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = - 1 << (8 * std::mem::size_of::() as size_t - 1); + (1 as size_t) << (8 as size_t * std::mem::size_of::() as size_t - 1); extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")] @@ -104,14 +109,12 @@ extern_libpython! { pub fn PyObject_Type(o: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyObject_Size")] pub fn PyObject_Size(o: *mut PyObject) -> Py_ssize_t; -} -#[inline] -pub unsafe fn PyObject_Length(o: *mut PyObject) -> Py_ssize_t { - PyObject_Size(o) -} + // PyObject_Length is a direct alias for PyObject_Size + #[cfg_attr(not(PyPy), link_name = "PyObject_Size")] + #[cfg_attr(PyPy, link_name = "PyPyObject_Size")] + pub fn PyObject_Length(o: *mut PyObject) -> Py_ssize_t; -extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyObject_GetItem")] pub fn PyObject_GetItem(o: *mut PyObject, key: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyObject_SetItem")] @@ -120,18 +123,20 @@ extern_libpython! { pub fn PyObject_DelItemString(o: *mut PyObject, key: *const c_char) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyObject_DelItem")] pub fn PyObject_DelItem(o: *mut PyObject, key: *mut PyObject) -> c_int; -} -extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyObject_Format")] pub fn PyObject_Format(obj: *mut PyObject, format_spec: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyObject_GetIter")] pub fn PyObject_GetIter(arg1: *mut PyObject) -> *mut PyObject; -} + #[cfg(Py_3_10)] + #[cfg_attr(PyPy, link_name = "PyPyObject_GetAIter")] + pub fn PyObject_GetAIter(arg1: *mut PyObject) -> *mut PyObject; -extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyIter_Check")] pub fn PyIter_Check(obj: *mut PyObject) -> c_int; + #[cfg(Py_3_10)] + #[cfg_attr(PyPy, link_name = "PyPyAIter_Check")] + pub fn PyAIter_Check(obj: *mut PyObject) -> c_int; #[cfg(Py_3_14)] #[cfg_attr(PyPy, link_name = "PyPyIter_NextItem")] @@ -185,20 +190,8 @@ extern_libpython! { pub fn PyNumber_Xor(o1: *mut PyObject, o2: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyNumber_Or")] pub fn PyNumber_Or(o1: *mut PyObject, o2: *mut PyObject) -> *mut PyObject; -} -// Defined as this macro in Python limited API, but relies on -// non-limited PyTypeObject. Don't expose this since it cannot be used. -#[cfg(not(any(Py_LIMITED_API, PyPy)))] -#[inline] -pub unsafe fn PyIndex_Check(o: *mut PyObject) -> c_int { - let tp_as_number = (*Py_TYPE(o)).tp_as_number; - (!tp_as_number.is_null() && (*tp_as_number).nb_index.is_some()) as c_int -} - -extern_libpython! { - #[cfg(any(Py_LIMITED_API, PyPy))] - #[link_name = "PyPyIndex_Check"] + #[cfg_attr(PyPy, link_name = "PyPyIndex_Check")] pub fn PyIndex_Check(o: *mut PyObject) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyNumber_Index")] @@ -246,18 +239,11 @@ extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPySequence_Size")] pub fn PySequence_Size(o: *mut PyObject) -> Py_ssize_t; - #[cfg(PyPy)] - #[link_name = "PyPySequence_Length"] + // PySequence_Length is a direct alias for PySequence_Size + #[cfg_attr(not(PyPy), link_name = "PySequence_Size")] + #[cfg_attr(PyPy, link_name = "PyPySequence_Size")] pub fn PySequence_Length(o: *mut PyObject) -> Py_ssize_t; -} -#[inline] -#[cfg(not(PyPy))] -pub unsafe fn PySequence_Length(o: *mut PyObject) -> Py_ssize_t { - PySequence_Size(o) -} - -extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPySequence_Concat")] pub fn PySequence_Concat(o1: *mut PyObject, o2: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPySequence_Repeat")] @@ -285,20 +271,15 @@ extern_libpython! { pub fn PySequence_List(o: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPySequence_Fast")] pub fn PySequence_Fast(o: *mut PyObject, m: *const c_char) -> *mut PyObject; - // skipped PySequence_Fast_GET_SIZE - // skipped PySequence_Fast_GET_ITEM - // skipped PySequence_Fast_GET_ITEMS pub fn PySequence_Count(o: *mut PyObject, value: *mut PyObject) -> Py_ssize_t; #[cfg_attr(PyPy, link_name = "PyPySequence_Contains")] pub fn PySequence_Contains(seq: *mut PyObject, ob: *mut PyObject) -> c_int; -} -#[inline] -pub unsafe fn PySequence_In(o: *mut PyObject, value: *mut PyObject) -> c_int { - PySequence_Contains(o, value) -} + // PySequence_In is a direct alias for PySequence_Contains + #[cfg_attr(not(PyPy), link_name = "PySequence_Contains")] + #[cfg_attr(PyPy, link_name = "PyPySequence_Contains")] + pub fn PySequence_In(o: *mut PyObject, value: *mut PyObject) -> c_int; -extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPySequence_Index")] pub fn PySequence_Index(o: *mut PyObject, value: *mut PyObject) -> Py_ssize_t; #[cfg_attr(PyPy, link_name = "PyPySequence_InPlaceConcat")] @@ -310,15 +291,11 @@ extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyMapping_Size")] pub fn PyMapping_Size(o: *mut PyObject) -> Py_ssize_t; - #[cfg(PyPy)] - #[link_name = "PyPyMapping_Length"] - pub fn PyMapping_Length(o: *mut PyObject) -> Py_ssize_t; -} -#[inline] -#[cfg(not(PyPy))] -pub unsafe fn PyMapping_Length(o: *mut PyObject) -> Py_ssize_t { - PyMapping_Size(o) + // PyMapping_Length is a direct alias for PyMapping_Size + #[cfg_attr(not(PyPy), link_name = "PyMapping_Size")] + #[cfg_attr(PyPy, link_name = "PyPyMapping_Size")] + pub fn PyMapping_Length(o: *mut PyObject) -> Py_ssize_t; } #[inline] @@ -336,6 +313,12 @@ extern_libpython! { pub fn PyMapping_HasKeyString(o: *mut PyObject, key: *const c_char) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyMapping_HasKey")] pub fn PyMapping_HasKey(o: *mut PyObject, key: *mut PyObject) -> c_int; + #[cfg(Py_3_13)] + #[cfg_attr(PyPy, link_name = "PyPyMapping_HasKeyWithError")] + pub fn PyMapping_HasKeyWithError(o: *mut PyObject, key: *mut PyObject) -> c_int; + #[cfg(Py_3_13)] + #[cfg_attr(PyPy, link_name = "PyPyMapping_HasKeyStringWithError")] + pub fn PyMapping_HasKeyStringWithError(o: *mut PyObject, key: *const c_char) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyMapping_Keys")] pub fn PyMapping_Keys(o: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyMapping_Values")] @@ -344,6 +327,12 @@ extern_libpython! { pub fn PyMapping_Items(o: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyMapping_GetItemString")] pub fn PyMapping_GetItemString(o: *mut PyObject, key: *const c_char) -> *mut PyObject; + #[cfg(Py_3_13)] + #[cfg_attr(PyPy, link_name = "PyPyMapping_GetOptionalItem")] + pub fn PyMapping_GetOptionalItem(o: *mut PyObject, key: *mut PyObject, result: *mut *mut PyObject) -> c_int; + #[cfg(Py_3_13)] + #[cfg_attr(PyPy, link_name = "PyPyMapping_GetOptionalItemString")] + pub fn PyMapping_GetOptionalItemString(o: *mut PyObject, key: *const c_char, result: *mut *mut PyObject) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyMapping_SetItemString")] pub fn PyMapping_SetItemString( o: *mut PyObject, diff --git a/pyo3-ffi/src/cpython/abstract_.rs b/pyo3-ffi/src/cpython/abstract_.rs index b6358f854ff..f3792410c93 100644 --- a/pyo3-ffi/src/cpython/abstract_.rs +++ b/pyo3-ffi/src/cpython/abstract_.rs @@ -1,26 +1,25 @@ -use crate::{PyObject, Py_ssize_t}; -#[cfg(any(not(PyPy), not(Py_3_11)))] +use crate::{ + vectorcallfunc, PyListObject, PyList_Check, PyList_GET_ITEM, PyList_GET_SIZE, PyObject, + PyTupleObject, PyTuple_GET_ITEM, PyTuple_GET_SIZE, Py_TYPE, Py_ssize_t, +}; +#[cfg(all(any(not(PyPy), not(Py_3_11)), not(Py_3_12)))] use std::ffi::c_char; +#[cfg(not(Py_3_12))] use std::ffi::c_int; #[cfg(not(Py_3_11))] use crate::Py_buffer; -#[cfg(not(PyPy))] -use crate::{ - vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET, PyTuple_Check, - PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL, -}; +#[cfg(not(any(PyPy, Py_3_11)))] +use crate::{PyCallable_Check, PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL}; +#[cfg(not(Py_3_12))] +use crate::{PyThreadState, PyThreadState_GET, PyTuple_Check}; use libc::size_t; -extern_libpython! { - #[cfg(not(any(PyPy, GraalPy)))] - pub fn _PyStack_AsDict(values: *const *mut PyObject, kwnames: *mut PyObject) -> *mut PyObject; -} - -#[cfg(not(any(PyPy, GraalPy)))] -const _PY_FASTCALL_SMALL_STACK: size_t = 5; +// skipped private _PyObject_CallMethodId +// skipped private _PyStack_AsDict +#[cfg(not(Py_3_12))] extern_libpython! { #[cfg(not(PyPy))] pub fn _Py_CheckFunctionResult( @@ -40,16 +39,26 @@ extern_libpython! { ) -> *mut PyObject; } +#[cfg(not(Py_3_12))] const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = 1 << (8 * std::mem::size_of::() as size_t - 1); +#[cfg(Py_3_12)] // public API from 3.12 +use crate::PY_VECTORCALL_ARGUMENTS_OFFSET; + #[inline(always)] pub unsafe fn PyVectorcall_NARGS(n: size_t) -> Py_ssize_t { let n = n & !PY_VECTORCALL_ARGUMENTS_OFFSET; n.try_into().expect("cannot fail due to mask") } -#[cfg(not(PyPy))] +#[cfg(any(PyPy, Py_3_11))] +extern_libpython! { + #[cfg_attr(PyPy, link_name = "PyPyVectorcall_Function")] + pub fn PyVectorcall_Function(callable: *mut PyObject) -> Option; +} + +#[cfg(not(any(PyPy, Py_3_11)))] #[inline(always)] pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option { assert!(!callable.is_null()); @@ -64,6 +73,7 @@ pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option *mut PyObject; - - #[cfg_attr(not(any(Py_3_9, PyPy)), link_name = "_PyVectorcall_Call")] - #[cfg_attr(PyPy, link_name = "PyPyVectorcall_Call")] - pub fn PyVectorcall_Call( - callable: *mut PyObject, - tuple: *mut PyObject, - dict: *mut PyObject, - ) -> *mut PyObject; -} - +#[cfg(not(Py_3_12))] #[cfg(not(any(PyPy, GraalPy)))] #[inline(always)] pub unsafe fn _PyObject_FastCallTstate( @@ -133,6 +121,7 @@ pub unsafe fn _PyObject_FastCallTstate( _PyObject_VectorcallTstate(tstate, func, args, nargs as size_t, std::ptr::null_mut()) } +#[cfg(not(Py_3_12))] #[cfg(not(any(PyPy, GraalPy)))] #[inline(always)] pub unsafe fn _PyObject_FastCall( @@ -143,24 +132,7 @@ pub unsafe fn _PyObject_FastCall( _PyObject_FastCallTstate(PyThreadState_GET(), func, args, nargs) } -#[cfg(not(PyPy))] -#[inline(always)] -pub unsafe fn _PyObject_CallNoArg(func: *mut PyObject) -> *mut PyObject { - _PyObject_VectorcallTstate( - PyThreadState_GET(), - func, - std::ptr::null_mut(), - 0, - std::ptr::null_mut(), - ) -} - -extern_libpython! { - #[cfg(PyPy)] - #[link_name = "_PyPyObject_CallNoArg"] - pub fn _PyObject_CallNoArg(func: *mut PyObject) -> *mut PyObject; -} - +#[cfg(not(Py_3_12))] #[cfg(not(PyPy))] #[inline(always)] pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject { @@ -172,6 +144,25 @@ pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *m _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) } +#[cfg(Py_3_12)] +extern_libpython! { + #[cfg_attr( + all(not(any(PyPy, GraalPy)), not(Py_3_9)), + link_name = "_PyObject_VectorcallDict" + )] + #[cfg_attr(all(PyPy, not(Py_3_9)), link_name = "_PyPyObject_VectorcallDict")] + #[cfg_attr(all(PyPy, Py_3_9), link_name = "PyPyObject_VectorcallDict")] + pub fn PyObject_VectorcallDict( + callable: *mut PyObject, + args: *const *mut PyObject, + nargsf: size_t, + kwdict: *mut PyObject, + ) -> *mut PyObject; + + #[cfg_attr(PyPy, link_name = "PyPyObject_CallOneArg")] + pub fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject; +} + #[cfg(all(Py_3_9, not(PyPy)))] #[inline(always)] pub unsafe fn PyObject_CallMethodNoArgs( @@ -203,12 +194,6 @@ pub unsafe fn PyObject_CallMethodOneArg( ) } -// skipped _PyObject_VectorcallMethodId -// skipped _PyObject_CallMethodIdNoArgs -// skipped _PyObject_CallMethodIdOneArg - -// skipped _PyObject_HasLen - extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyObject_LengthHint")] pub fn PyObject_LengthHint(o: *mut PyObject, arg1: Py_ssize_t) -> Py_ssize_t; @@ -273,31 +258,34 @@ extern_libpython! { pub fn PyBuffer_Release(view: *mut Py_buffer); } -// skipped PySequence_ITEM - -pub const PY_ITERSEARCH_COUNT: c_int = 1; -pub const PY_ITERSEARCH_INDEX: c_int = 2; -pub const PY_ITERSEARCH_CONTAINS: c_int = 3; - -extern_libpython! { - #[cfg(not(any(PyPy, GraalPy)))] - pub fn _PySequence_IterSearch( - seq: *mut PyObject, - obj: *mut PyObject, - operation: c_int, - ) -> Py_ssize_t; +#[inline(always)] +pub unsafe fn PySequence_ITEM(seq: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { + (*(*Py_TYPE(seq)).tp_as_sequence).sq_item.unwrap_unchecked()(seq, i) } -// skipped _PyObject_RealIsInstance -// skipped _PyObject_RealIsSubclass - -// skipped _PySequence_BytesToCharpArray - -// skipped _Py_FreeCharPArray - -// skipped _Py_add_one_to_index_F -// skipped _Py_add_one_to_index_C +#[inline(always)] +pub unsafe fn PySequence_Fast_GET_SIZE(seq: *mut PyObject) -> Py_ssize_t { + if PyList_Check(seq) == 1 { + PyList_GET_SIZE(seq) + } else { + PyTuple_GET_SIZE(seq) + } +} -// skipped _Py_convert_optional_to_ssize_t +#[inline(always)] +pub unsafe fn PySequence_Fast_GET_ITEM(seq: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { + if PyList_Check(seq) == 1 { + PyList_GET_ITEM(seq, i) + } else { + PyTuple_GET_ITEM(seq, i) + } +} -// skipped _PyNumber_Index(*mut PyObject o) +#[inline(always)] +pub unsafe fn PySequence_Fast_ITEMS(seq: *mut PyObject) -> *mut *mut PyObject { + if PyList_Check(seq) == 1 { + (*seq.cast::()).ob_item + } else { + (*seq.cast::()).ob_item.as_mut_ptr() + } +} From 4d202298fd835d4f4f85c3d51f1ab0b09d04fa9a Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 11 Apr 2026 19:08:02 +0100 Subject: [PATCH 2/8] fmt --- pyo3-ffi/src/abstract_.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index 6fa00917d01..bde76dcd1fd 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -54,7 +54,7 @@ extern_libpython! { ... ) -> *mut PyObject; - #[cfg(all(PyPy, not(Py_3_13)))] // called internally in PyUnicodeDecodeError_Create on PyPy + #[cfg(all(PyPy, not(Py_3_13)))] // called internally in PyUnicodeDecodeError_Create on PyPy #[cfg_attr(PyPy, link_name = "_PyPyObject_CallFunction_SizeT")] pub(crate) fn _PyObject_CallFunction_SizeT( callable_object: *mut PyObject, @@ -71,16 +71,16 @@ extern_libpython! { ... ) -> *mut PyObject; - #[cfg(all(Py_3_12, Py_LIMITED_API))] // is an inline function in cpython/abstract.rs on version-specific ABI + #[cfg(all(Py_3_12, Py_LIMITED_API))] // is an inline function in cpython/abstract.rs on version-specific ABI #[cfg_attr(PyPy, link_name = "PyPyVectorcall_NARGS")] pub fn PyVectorcall_NARGS(nargsf: size_t) -> size_t; - #[cfg_attr(not(any(Py_3_12, PyPy)), link_name = "_PyVectorcall_Call")] // symbol made public in 3.12 + #[cfg_attr(not(any(Py_3_12, PyPy)), link_name = "_PyVectorcall_Call")] // symbol made public in 3.12 #[cfg_attr(PyPy, link_name = "PyPyVectorcall_Call")] pub fn PyVectorcall_Call( callable: *mut PyObject, tuple: *mut PyObject, - dict: *mut PyObject + dict: *mut PyObject, ) -> *mut PyObject; } @@ -291,7 +291,6 @@ extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyMapping_Size")] pub fn PyMapping_Size(o: *mut PyObject) -> Py_ssize_t; - // PyMapping_Length is a direct alias for PyMapping_Size #[cfg_attr(not(PyPy), link_name = "PyMapping_Size")] #[cfg_attr(PyPy, link_name = "PyPyMapping_Size")] @@ -329,10 +328,18 @@ extern_libpython! { pub fn PyMapping_GetItemString(o: *mut PyObject, key: *const c_char) -> *mut PyObject; #[cfg(Py_3_13)] #[cfg_attr(PyPy, link_name = "PyPyMapping_GetOptionalItem")] - pub fn PyMapping_GetOptionalItem(o: *mut PyObject, key: *mut PyObject, result: *mut *mut PyObject) -> c_int; + pub fn PyMapping_GetOptionalItem( + o: *mut PyObject, + key: *mut PyObject, + result: *mut *mut PyObject, + ) -> c_int; #[cfg(Py_3_13)] #[cfg_attr(PyPy, link_name = "PyPyMapping_GetOptionalItemString")] - pub fn PyMapping_GetOptionalItemString(o: *mut PyObject, key: *const c_char, result: *mut *mut PyObject) -> c_int; + pub fn PyMapping_GetOptionalItemString( + o: *mut PyObject, + key: *const c_char, + result: *mut *mut PyObject, + ) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyMapping_SetItemString")] pub fn PyMapping_SetItemString( o: *mut PyObject, From ad6397287bc2527cfcd307dcb33ba71c440ba74d Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 11 Apr 2026 19:14:26 +0100 Subject: [PATCH 3/8] newsfragment, remove some private symbols more aggressively --- newsfragments/5942.added.md | 1 + newsfragments/5942.removed.md | 1 + pyo3-ffi/src/cpython/abstract_.rs | 29 +++-------------------------- 3 files changed, 5 insertions(+), 26 deletions(-) create mode 100644 newsfragments/5942.added.md create mode 100644 newsfragments/5942.removed.md diff --git a/newsfragments/5942.added.md b/newsfragments/5942.added.md new file mode 100644 index 00000000000..66ae255c047 --- /dev/null +++ b/newsfragments/5942.added.md @@ -0,0 +1 @@ +Add FFI definitions `PyObject_GetAIter`, `PyAIter_Check`, `PyMapping_HasKeyWithError`, `PyMapping_HasKeyStringWithError`, `PyMapping_GetOptionalItem`, `PyMapping_GetOptionalItemString` diff --git a/newsfragments/5942.removed.md b/newsfragments/5942.removed.md new file mode 100644 index 00000000000..611c73f9f17 --- /dev/null +++ b/newsfragments/5942.removed.md @@ -0,0 +1 @@ +Remove private FFI definitions `_PyStack_AsDict`, `_PyObject_CallNoArg`, `_PyObject_FastCall`, `_PyObject_FastCallTstate`. `_PyObject_VectorcallTstate`, `_PyObject_MakeTpCall`, `_Py_CheckFunctionResult`, `_PyObject_CallFunction_SizeT`, `_PyObject_CallMethod_SizeT`. diff --git a/pyo3-ffi/src/cpython/abstract_.rs b/pyo3-ffi/src/cpython/abstract_.rs index f3792410c93..5e2a7c39ece 100644 --- a/pyo3-ffi/src/cpython/abstract_.rs +++ b/pyo3-ffi/src/cpython/abstract_.rs @@ -22,7 +22,7 @@ use libc::size_t; #[cfg(not(Py_3_12))] extern_libpython! { #[cfg(not(PyPy))] - pub fn _Py_CheckFunctionResult( + fn _Py_CheckFunctionResult( tstate: *mut PyThreadState, callable: *mut PyObject, result: *mut PyObject, @@ -30,7 +30,7 @@ extern_libpython! { ) -> *mut PyObject; #[cfg(not(PyPy))] - pub fn _PyObject_MakeTpCall( + fn _PyObject_MakeTpCall( tstate: *mut PyThreadState, callable: *mut PyObject, args: *const *mut PyObject, @@ -76,7 +76,7 @@ pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option *mut PyObject { - _PyObject_VectorcallTstate(tstate, func, args, nargs as size_t, std::ptr::null_mut()) -} - -#[cfg(not(Py_3_12))] -#[cfg(not(any(PyPy, GraalPy)))] -#[inline(always)] -pub unsafe fn _PyObject_FastCall( - func: *mut PyObject, - args: *const *mut PyObject, - nargs: Py_ssize_t, -) -> *mut PyObject { - _PyObject_FastCallTstate(PyThreadState_GET(), func, args, nargs) -} - #[cfg(not(Py_3_12))] #[cfg(not(PyPy))] #[inline(always)] From 2d8e81001d510884cfc8c8b9a50a704b51b7fa2f Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 11 Apr 2026 19:16:56 +0100 Subject: [PATCH 4/8] more newsfragment --- newsfragments/5942.added.md | 2 +- newsfragments/5942.removed.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/newsfragments/5942.added.md b/newsfragments/5942.added.md index 66ae255c047..ca170d67e23 100644 --- a/newsfragments/5942.added.md +++ b/newsfragments/5942.added.md @@ -1 +1 @@ -Add FFI definitions `PyObject_GetAIter`, `PyAIter_Check`, `PyMapping_HasKeyWithError`, `PyMapping_HasKeyStringWithError`, `PyMapping_GetOptionalItem`, `PyMapping_GetOptionalItemString` +Add FFI definitions `PyObject_GetAIter`, `PyAIter_Check`, `PyMapping_HasKeyWithError`, `PyMapping_HasKeyStringWithError`, `PyMapping_GetOptionalItem`, `PyMapping_GetOptionalItemString`, `PySequence_ITEM`, `PySequence_Fast_GET_SIZE`, `PySequence_Fast_GET_ITEM`, and `PySequence_Fast_ITEMS`. diff --git a/newsfragments/5942.removed.md b/newsfragments/5942.removed.md index 611c73f9f17..a041c333801 100644 --- a/newsfragments/5942.removed.md +++ b/newsfragments/5942.removed.md @@ -1 +1,3 @@ -Remove private FFI definitions `_PyStack_AsDict`, `_PyObject_CallNoArg`, `_PyObject_FastCall`, `_PyObject_FastCallTstate`. `_PyObject_VectorcallTstate`, `_PyObject_MakeTpCall`, `_Py_CheckFunctionResult`, `_PyObject_CallFunction_SizeT`, `_PyObject_CallMethod_SizeT`. +Remove private FFI definitions `_PyStack_AsDict`, `_PyObject_CallNoArg`, `_PyObject_FastCall`, `_PyObject_FastCallTstate`. `_PyObject_VectorcallTstate`, `_PyObject_MakeTpCall`, `_Py_CheckFunctionResult`, `_PyObject_CallFunction_SizeT`, `_PyObject_CallMethod_SizeT`, and `_PySequence_IterSearch`. + +Remove FFI definitions `PY_ITERSEARCH_COUNT`, `PY_ITERSEARCH_INDEX`, and `PY_ITERSEARCH_CONTAINS`. From 9665b0fb8c9cf4e2be84a453b703ac8ee82238ff Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 11 Apr 2026 19:18:13 +0100 Subject: [PATCH 5/8] remove x86 workarounds for removed private functions --- pyo3-ffi/src/impl_/macros.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pyo3-ffi/src/impl_/macros.rs b/pyo3-ffi/src/impl_/macros.rs index c493157186c..be3b0418866 100644 --- a/pyo3-ffi/src/impl_/macros.rs +++ b/pyo3-ffi/src/impl_/macros.rs @@ -38,30 +38,12 @@ macro_rules! extern_libpython_maybe_private_fn { ) => { extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } }; - ( - [_PyObject_CallMethod_SizeT] - $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? - ) => { - extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } - }; ( [_PyObject_MakeTpCall] $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? ) => { extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } }; - ( - [_PySequence_IterSearch] - $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? - ) => { - extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } - }; - ( - [_PyStack_AsDict] - $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? - ) => { - extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } - }; ( [_Py_CheckFunctionResult] $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? From 979d4c36fb54ef20aa3a5a4e14be0a13f1a49604 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 11 Apr 2026 20:10:31 +0100 Subject: [PATCH 6/8] fixup for PyPy, GraalPy --- pyo3-ffi/src/cpython/abstract_.rs | 45 ++++++++++++++++++------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/pyo3-ffi/src/cpython/abstract_.rs b/pyo3-ffi/src/cpython/abstract_.rs index 5e2a7c39ece..9b838146b8a 100644 --- a/pyo3-ffi/src/cpython/abstract_.rs +++ b/pyo3-ffi/src/cpython/abstract_.rs @@ -1,18 +1,21 @@ -use crate::{ - vectorcallfunc, PyListObject, PyList_Check, PyList_GET_ITEM, PyList_GET_SIZE, PyObject, - PyTupleObject, PyTuple_GET_ITEM, PyTuple_GET_SIZE, Py_TYPE, Py_ssize_t, -}; +use crate::{vectorcallfunc, PyObject, Py_TYPE, Py_ssize_t}; #[cfg(all(any(not(PyPy), not(Py_3_11)), not(Py_3_12)))] use std::ffi::c_char; -#[cfg(not(Py_3_12))] +#[cfg(not(Py_3_11))] use std::ffi::c_int; +#[cfg(not(any(PyPy, GraalPy)))] +use crate::{ + PyListObject, PyList_Check, PyList_GET_ITEM, PyList_GET_SIZE, PyTupleObject, PyTuple_GET_ITEM, + PyTuple_GET_SIZE, +}; + #[cfg(not(Py_3_11))] use crate::Py_buffer; #[cfg(not(any(PyPy, Py_3_11)))] use crate::{PyCallable_Check, PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL}; -#[cfg(not(Py_3_12))] +#[cfg(not(any(Py_3_12, PyPy)))] use crate::{PyThreadState, PyThreadState_GET, PyTuple_Check}; use libc::size_t; @@ -109,19 +112,6 @@ pub unsafe fn PyObject_Vectorcall( _PyObject_VectorcallTstate(PyThreadState_GET(), callable, args, nargsf, kwnames) } -#[cfg(not(Py_3_12))] -#[cfg(not(PyPy))] -#[inline(always)] -pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject { - assert!(!arg.is_null()); - let args_array = [std::ptr::null_mut(), arg]; - let args = args_array.as_ptr().offset(1); // For PY_VECTORCALL_ARGUMENTS_OFFSET - let tstate = PyThreadState_GET(); - let nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET; - _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) -} - -#[cfg(Py_3_12)] extern_libpython! { #[cfg_attr( all(not(any(PyPy, GraalPy)), not(Py_3_9)), @@ -135,7 +125,21 @@ extern_libpython! { nargsf: size_t, kwdict: *mut PyObject, ) -> *mut PyObject; +} + +#[cfg(not(any(Py_3_12, PyPy)))] +#[inline(always)] +pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject { + assert!(!arg.is_null()); + let args_array = [std::ptr::null_mut(), arg]; + let args = args_array.as_ptr().offset(1); // For PY_VECTORCALL_ARGUMENTS_OFFSET + let tstate = PyThreadState_GET(); + let nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET; + _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) +} +extern_libpython! { + #[cfg(any(Py_3_12, PyPy))] #[cfg_attr(PyPy, link_name = "PyPyObject_CallOneArg")] pub fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject; } @@ -241,6 +245,7 @@ pub unsafe fn PySequence_ITEM(seq: *mut PyObject, i: Py_ssize_t) -> *mut PyObjec } #[inline(always)] +#[cfg(not(any(PyPy, GraalPy)))] pub unsafe fn PySequence_Fast_GET_SIZE(seq: *mut PyObject) -> Py_ssize_t { if PyList_Check(seq) == 1 { PyList_GET_SIZE(seq) @@ -250,6 +255,7 @@ pub unsafe fn PySequence_Fast_GET_SIZE(seq: *mut PyObject) -> Py_ssize_t { } #[inline(always)] +#[cfg(not(any(PyPy, GraalPy)))] pub unsafe fn PySequence_Fast_GET_ITEM(seq: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { if PyList_Check(seq) == 1 { PyList_GET_ITEM(seq, i) @@ -259,6 +265,7 @@ pub unsafe fn PySequence_Fast_GET_ITEM(seq: *mut PyObject, i: Py_ssize_t) -> *mu } #[inline(always)] +#[cfg(not(any(PyPy, GraalPy)))] pub unsafe fn PySequence_Fast_ITEMS(seq: *mut PyObject) -> *mut *mut PyObject { if PyList_Check(seq) == 1 { (*seq.cast::()).ob_item From ba45e600565bdfe4f7611d91e89b5a7989bb6887 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 14 Apr 2026 09:07:17 +0100 Subject: [PATCH 7/8] use checked_shl to calculate `PY_VECTORCALL_ARGUMENTS_OFFSET` --- pyo3-ffi/src/abstract_.rs | 5 +++-- pyo3-ffi/src/cpython/abstract_.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index bde76dcd1fd..4162b81886e 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -85,8 +85,9 @@ extern_libpython! { } #[cfg(any(Py_3_12, not(Py_LIMITED_API)))] -pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = - (1 as size_t) << (8 as size_t * std::mem::size_of::() as size_t - 1); +pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = (1 as size_t) + .checked_shl((8 * std::mem::size_of::() - 1) as u32) + .expect("size_t should fit the flag bits"); extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")] diff --git a/pyo3-ffi/src/cpython/abstract_.rs b/pyo3-ffi/src/cpython/abstract_.rs index 9b838146b8a..aea3740ed3c 100644 --- a/pyo3-ffi/src/cpython/abstract_.rs +++ b/pyo3-ffi/src/cpython/abstract_.rs @@ -43,8 +43,9 @@ extern_libpython! { } #[cfg(not(Py_3_12))] -const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = - 1 << (8 * std::mem::size_of::() as size_t - 1); +const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = (1 as size_t) + .checked_shl((8 * std::mem::size_of::() - 1) as u32) + .expect("size_t should fit the flag bits"); #[cfg(Py_3_12)] // public API from 3.12 use crate::PY_VECTORCALL_ARGUMENTS_OFFSET; From a7cc72e5aad7c88c222e1a5242532b39c140f285 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 14 Apr 2026 20:37:03 +0100 Subject: [PATCH 8/8] fix return type of `PyVectorcall_NARGS` --- pyo3-ffi/src/abstract_.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index 4162b81886e..c2b141b0c6e 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -73,7 +73,7 @@ extern_libpython! { #[cfg(all(Py_3_12, Py_LIMITED_API))] // is an inline function in cpython/abstract.rs on version-specific ABI #[cfg_attr(PyPy, link_name = "PyPyVectorcall_NARGS")] - pub fn PyVectorcall_NARGS(nargsf: size_t) -> size_t; + pub fn PyVectorcall_NARGS(nargsf: size_t) -> Py_ssize_t; #[cfg_attr(not(any(Py_3_12, PyPy)), link_name = "_PyVectorcall_Call")] // symbol made public in 3.12 #[cfg_attr(PyPy, link_name = "PyPyVectorcall_Call")]