diff --git a/pyo3-ffi-check/build.rs b/pyo3-ffi-check/build.rs index cbaa336df24..8f144364808 100644 --- a/pyo3-ffi-check/build.rs +++ b/pyo3-ffi-check/build.rs @@ -32,6 +32,8 @@ fn main() { println!("cargo:rerun-if-changed=definitions"); println!("cargo:rerun-if-changed=../pyo3-ffi"); + pyo3_build_config::use_pyo3_cfgs(); + // Because `pyo3-ffi` is a dependency, libpython is linked, this ensures `main.rs` can run. // Slightly needless (no symbols from libpython are actually called), but simple to do. pyo3_build_config::add_libpython_rpath_link_args(); diff --git a/pyo3-ffi-check/definitions/build.rs b/pyo3-ffi-check/definitions/build.rs index aa9f940116b..80e5a37fa56 100644 --- a/pyo3-ffi-check/definitions/build.rs +++ b/pyo3-ffi-check/definitions/build.rs @@ -19,6 +19,19 @@ impl bindgen::callbacks::ParseCallbacks for ParseCallbacks { } } +#[derive(Debug)] +struct PyPyReplaceCallbacks; + +impl bindgen::callbacks::ParseCallbacks for PyPyReplaceCallbacks { + fn item_name(&self, item_info: ItemInfo<'_>) -> Option { + if item_info.name.starts_with("PyPy") || item_info.name.starts_with("_PyPy") { + Some(item_info.name.replacen("PyPy", "Py", 1)) + } else { + None + } + } +} + fn main() { let config = pyo3_build_config::get(); @@ -44,11 +57,20 @@ fn main() { println!("cargo:rerun-if-changed=wrapper.h"); - let bindings = bindgen::Builder::default() + let mut builder = bindgen::Builder::default() .header("wrapper.h") .clang_args(clang_args) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .parse_callbacks(Box::new(ParseCallbacks)) + .parse_callbacks(Box::new(ParseCallbacks)); + + if matches!( + config.implementation, + pyo3_build_config::PythonImplementation::PyPy + ) { + builder = builder.parse_callbacks(Box::new(PyPyReplaceCallbacks)); + } + + let bindings = builder // blocklist some values which apparently have conflicting definitions on unix .blocklist_item("FP_NORMAL") .blocklist_item("FP_SUBNORMAL") diff --git a/pyo3-ffi-check/macro/src/lib.rs b/pyo3-ffi-check/macro/src/lib.rs index 7434f4dbc2b..25b343dbd72 100644 --- a/pyo3-ffi-check/macro/src/lib.rs +++ b/pyo3-ffi-check/macro/src/lib.rs @@ -11,26 +11,11 @@ use quote::quote; /// Macro which expands to multiple macro calls, one per pyo3-ffi struct. #[proc_macro] pub fn for_all_structs(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: TokenStream = input.into(); - let mut input = input.into_iter(); - - let macro_name = match input.next() { - Some(TokenTree::Ident(i)) => i, - _ => { - return quote!(compile_error!( - "for_all_structs!() takes only a single ident as input" - )) - .into() - } + let macro_name = match get_macro_name_from_input("for_all_structs", input) { + Ok(name) => name, + Err(err) => return err.into(), }; - if input.next().is_some() { - return quote!(compile_error!( - "for_all_structs!() takes only a single ident as input" - )) - .into(); - } - let doc_dir = get_doc_dir(); let structs_glob = format!("{}/pyo3_ffi/struct.*.html", doc_dir.display()); @@ -207,3 +192,362 @@ fn get_fields_from_file(path: &Path) -> Vec { }) .collect() } + +// C Macros are re-exported in pyo3-ffi as functions with the same name to get roughly equivalent semantics, +// these are excluded here. +const MACRO_EXCLUSIONS: &[&str] = &[ + "PyAnySet_Check", + "PyAnySet_CheckExact", + "PyAsyncGen_CheckExact", + "PyBool_Check", + "PyByteArray_Check", + "PyByteArray_CheckExact", + "PyBytes_AS_STRING", + "PyBytes_Check", + "PyBytes_CheckExact", + "PyCFunction_Check", + "PyCFunction_CheckExact", + "PyCFunction_GET_CLASS", + "PyCFunction_GET_FLAGS", + "PyCFunction_GET_FUNCTION", + "PyCFunction_GET_SELF", + "PyCMethod_Check", + "PyCMethod_CheckExact", + "PyCallIter_Check", + "PyCapsule_CheckExact", + "PyCode_Check", + "PyComplex_Check", + "PyComplex_CheckExact", + "PyContext_CheckExact", + "PyContextToken_CheckExact", + "PyContextVar_CheckExact", + "PyCoro_CheckExact", + "PyDate_Check", + "PyDate_CheckExact", + "PyDateTimeAPI", + "PyDateTime_Check", + "PyDateTime_CheckExact", + "PyDateTime_DATE_GET_FOLD", + "PyDateTime_DATE_GET_HOUR", + "PyDateTime_DATE_GET_MICROSECOND", + "PyDateTime_DATE_GET_MINUTE", + "PyDateTime_DATE_GET_SECOND", + "PyDateTime_DATE_GET_TZINFO", + "PyDateTime_DELTA_GET_DAYS", + "PyDateTime_DELTA_GET_MICROSECONDS", + "PyDateTime_DELTA_GET_SECONDS", + "PyDateTime_FromTimestamp", + "PyDateTime_GET_DAY", + "PyDateTime_GET_MONTH", + "PyDateTime_GET_YEAR", + "PyDateTime_IMPORT", + "PyDateTime_TIME_GET_FOLD", + "PyDateTime_TIME_GET_HOUR", + "PyDateTime_TIME_GET_MICROSECOND", + "PyDateTime_TIME_GET_MINUTE", + "PyDateTime_TIME_GET_SECOND", + "PyDateTime_TIME_GET_TZINFO", + "PyDateTime_TimeZone_UTC", + "PyDate_FromTimestamp", + "PyDelta_Check", + "PyDelta_CheckExact", + "PyDict_Check", + "PyDict_CheckExact", + "PyDictItems_Check", + "PyDictKeys_Check", + "PyDictValues_Check", + "PyDictViewSet_Check", + "PyExceptionClass_Check", + "PyExceptionInstance_Check", + "PyExceptionInstance_Class", + "PyFloat_AS_DOUBLE", + "PyFloat_Check", + "PyFloat_CheckExact", + "PyFrame_BlockSetup", + "PyFrame_Check", + "PyFrameLocalsProxy_Check", + "PyFrozenSet_Check", + "PyFrozenSet_CheckExact", + "PyFunction_Check", + "PyGen_Check", + "PyGen_CheckExact", + "PyImport_ImportModuleEx", + "PyList_Check", + "PyList_CheckExact", + "PyList_GET_ITEM", + "PyList_GET_SIZE", + "PyList_SET_ITEM", + "PyLong_Check", + "PyLong_CheckExact", + "PyMapping_DelItem", + "PyMapping_DelItemString", + "PyMarshal_ReadLastObjectFromFile", + "PyMarshal_ReadLongFromFile", + "PyMarshal_ReadObjectFromFile", + "PyMarshal_ReadObjectFromString", + "PyMarshal_ReadShortFromFile", + "PyMarshal_WriteLongToFile", + "PyMarshal_WriteObjectToFile", + "PyMarshal_WriteObjectToString", + "PyMemoryView_Check", + "PyModule_Check", + "PyModule_CheckExact", + "PyModule_Create", + "PyModule_FromDefAndSpec", + "PyObject_CallMethodNoArgs", + "PyObject_CallMethodOneArg", + "PyObject_GC_New", + "PyObject_GC_NewVar", + "PyObject_GC_Resize", + "PyObject_New", + "PyObject_NewVar", + "PyObject_TypeCheck", + "PyRange_Check", + "PySeqIter_Check", + "PySet_Check", + "PySet_CheckExact", + "PySet_GET_SIZE", + "PySlice_Check", + "PyStructSequence_GET_ITEM", + "PyStructSequence_SET_ITEM", + "PySys_AddWarnOption", + "PySys_AddWarnOptionUnicode", + "PySys_AddXOption", + "PySys_HasWarnOptions", + "PySys_SetPath", + "PyTZInfo_Check", + "PyTZInfo_CheckExact", + "PyThreadState_GET", + "PyTime_Check", + "PyTime_CheckExact", + "PyTimeZone_FromOffset", + "PyTimeZone_FromOffsetAndName", + "PyTraceBack_Check", + "PyTuple_Check", + "PyTuple_CheckExact", + "PyTuple_GET_ITEM", + "PyTuple_GET_SIZE", + "PyTuple_SET_ITEM", + "PyType_Check", + "PyType_CheckExact", + "PyType_FastSubclass", + "PyType_HasFeature", + "PyType_IS_GC", + "PyUnicode_1BYTE_DATA", + "PyUnicode_2BYTE_DATA", + "PyUnicode_4BYTE_DATA", + "PyUnicode_Check", + "PyUnicode_CheckExact", + "PyUnicode_ClearFreeList", + "PyUnicode_Encode", + "PyUnicode_EncodeASCII", + "PyUnicode_EncodeCharmap", + "PyUnicode_EncodeDecimal", + "PyUnicode_EncodeLatin1", + "PyUnicode_EncodeRawUnicodeEscape", + "PyUnicode_EncodeUTF16", + "PyUnicode_EncodeUTF32", + "PyUnicode_EncodeUTF7", + "PyUnicode_EncodeUTF8", + "PyUnicode_EncodeUnicodeEscape", + "PyUnicode_GET_LENGTH", + "PyUnicode_IS_READY", + "PyUnicode_READY", + "PyUnicode_TransformDecimalToASCII", + "PyUnicode_TranslateCharmap", + "PyWeakref_Check", + "PyWeakref_CheckProxy", + "PyWeakref_CheckRef", + "PyWeakref_CheckRefExact", + "Py_CLEAR", + "Py_CompileStringFlags", + "Py_DECREF", + "Py_Ellipsis", + "Py_False", + "Py_INCREF", + "Py_IS_TYPE", + "Py_None", + "Py_NotImplemented", + "Py_SIZE", + "Py_True", + "Py_XDECREF", + "Py_XINCREF", + // CPython deprecated these but the symbols still exist, pyo3-ffi will probably clean them up anyway + "_PyCode_GetExtra", + "_PyCode_SetExtra", + "_PyEval_RequestCodeExtraIndex", + // FIXME: probably outdated definitions that fail to build, need investigation, + // temporarily here to make the build pass to get CI running + "_PyFloat_CAST", + "_PyObject_CallNoArg", + "_PyObject_FastCall", + "_PyObject_FastCallTstate", + "_PyObject_MakeTpCall", + "_PyObject_VectorcallTstate", + "_PyRun_AnyFileObject", + "_PyRun_InteractiveLoopObject", + "_PyRun_SimpleFileObject", + "_PySequence_IterSearch", + "_PySet_NextEntry", + "_PyUnicode_CheckConsistency", + "_Py_CheckFunctionResult", + "PyCode_New", + "PyCode_NewWithPosOnlyArgs", +]; + +#[proc_macro] +pub fn for_all_functions(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let macro_name = match get_macro_name_from_input("for_all_functions", _input) { + Ok(name) => name, + Err(err) => return err.into(), + }; + + let doc_dir = get_doc_dir(); + let functions_glob = format!("{}/pyo3_ffi/fn.*.html", doc_dir.display()); + + let mut output = TokenStream::new(); + + for entry in glob::glob(&functions_glob).expect("Failed to read glob pattern") { + let entry = entry.unwrap(); + + let file_name = entry.file_name().unwrap().to_string_lossy().into_owned(); + let function_name = file_name + .strip_prefix("fn.") + .unwrap() + .strip_suffix(".html") + .unwrap(); + + if MACRO_EXCLUSIONS.contains(&function_name) { + continue; + } + + if pyo3_build_config::get().implementation == pyo3_build_config::PythonImplementation::PyPy + { + // If the function doesn't exist in PyPy, for now we don't care: + // - For PyO3 inline functions it's probably fine to include anyway + // - For extern symbols - PyPy may add them in a future release + let bingen_path = doc_dir.join(format!("bindgen/fn.{}.html", function_name)); + if !bingen_path.exists() { + continue; + } + } + + let FunctionInfo { + modifiers, + arg_count, + variadic, + } = get_function_info(function_name, &entry); + + let function_ident = Ident::new(function_name, Span::call_site()); + + let arg_types = std::iter::repeat_n(quote!(_), arg_count); + + let vararg = if variadic { Some(quote!(, ...)) } else { None }; + + if !modifiers.to_string().contains(r#"extern "C""#) { + // if the function is not extern "C", it's a static inline function, pyo3-ffi uses the Rust abi, + // bindgen uses the C abi still + output + .extend(quote!(#macro_name!(@inline #function_ident, (#(#arg_types),*) #vararg);)); + } else { + output.extend( + quote!(#macro_name!(#function_ident, [#modifiers] (#(#arg_types),* #vararg));), + ); + } + } + + output.into() +} + +struct FunctionInfo { + modifiers: TokenStream, // e.g. `unsafe extern "C"`, empty for no modifiers + arg_count: usize, // not including the "..." for variadic functions + variadic: bool, +} + +fn get_function_info(name: &str, path: &Path) -> FunctionInfo { + let html = fs::read_to_string(path).unwrap(); + let html = scraper::Html::parse_document(&html); + let selector = scraper::Selector::parse("pre.item-decl code").unwrap(); + + let code_el = html.select(&selector).next().unwrap(); + let text = code_el.text().collect::(); + + // skip "pub " prefix + let text = text.strip_prefix("pub ").unwrap(); + + // find modifiers, e.g. `unsafe extern "C"` + let left_paren = text.find('(').unwrap(); + let modifiers = text[..left_paren] + .strip_suffix(name) + .unwrap() + .strip_suffix(" fn ") + .unwrap() + .parse() + .unwrap(); + + // Extract text between parens + let start = left_paren + 1; + + // some functions might have function pointer arguments with their own parens, + // so find the matching closing paren + let mut depth = 1; + let mut end = 0; + let mut last_arg_start = 0; + let mut arg_count = 0; + let args_begin = text[start..].trim_start(); + for (i, c) in args_begin.char_indices() { + match c { + '(' => depth += 1, + ')' if depth == 1 => { + end = i; + break; + } + ')' => { + depth -= 1; + } + ',' if depth == 1 => { + arg_count += 1; + last_arg_start = i + 1; + } + _ => (), + } + } + + let args = &args_begin[..end].trim(); + let variadic = args.ends_with("..."); + + if last_arg_start < end && !variadic && !args_begin[last_arg_start..end].trim_end().is_empty() { + // additional argument after the last comma (i.e. no trailing comma) + arg_count += 1; + } + + FunctionInfo { + modifiers, + arg_count, + variadic, + } +} + +fn get_macro_name_from_input( + proc_macro: &str, + input: proc_macro::TokenStream, +) -> Result { + let input: TokenStream = input.into(); + let mut input = input.into_iter(); + + let macro_name = match input.next() { + Some(TokenTree::Ident(i)) => i, + _ => { + let error_message = format!("{}!() takes only a single ident as input", proc_macro); + return Err(quote!(compile_error!(#error_message))); + } + }; + + if input.next().is_some() { + let error_message = format!("{}!() takes only a single ident as input", proc_macro); + return Err(quote!(compile_error!(#error_message))); + } + + Ok(macro_name) +} diff --git a/pyo3-ffi-check/src/main.rs b/pyo3-ffi-check/src/main.rs index cce3903fd57..0a88cfaf3b3 100644 --- a/pyo3-ffi-check/src/main.rs +++ b/pyo3-ffi-check/src/main.rs @@ -71,6 +71,25 @@ fn main() { pyo3_ffi_check_macro::for_all_structs!(check_struct); + // This macro attempts to check that both functions exist and have the same number of arguments, it is + // difficult to check the argument types match. + macro_rules! check_function { + ($name:ident, [$($modifiers:tt)*] ($($arg_types:tt)*)) => {{ + #[allow(deprecated)] + { pyo3_ffi::$name as $($modifiers)* fn($($arg_types)*) -> _ }; + bindings::$name as $($modifiers)* fn($($arg_types)*) -> _; + }}; + // case when the function is an inline function in the headers, in which case pyo3-ffi will use the + // Rust abi and the extern symbol uses the C abi + (@inline $name:ident, ($($arg_types:tt)*)) => {{ + #[allow(deprecated)] + { pyo3_ffi::$name as unsafe fn($($arg_types)*) -> _ }; + bindings::$name as unsafe extern "C" fn($($arg_types)*) -> _; + }}; + } + + pyo3_ffi_check_macro::for_all_functions!(check_function); + if failed { exit(1); } else { diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index 714e6d0d3f0..8903dbaac0c 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -24,7 +24,6 @@ pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_ extern_libpython! { #[cfg(all( - not(PyPy), any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 ))] #[cfg_attr(PyPy, link_name = "PyPyObject_CallNoArgs")] @@ -138,7 +137,7 @@ extern_libpython! { pub fn PyIter_NextItem(iter: *mut PyObject, item: *mut *mut PyObject) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyIter_Next")] pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; - #[cfg(all(not(PyPy), Py_3_10))] + #[cfg(Py_3_10)] #[cfg_attr(PyPy, link_name = "PyPyIter_Send")] pub fn PyIter_Send( iter: *mut PyObject, diff --git a/pyo3-ffi/src/compat/py_3_9.rs b/pyo3-ffi/src/compat/py_3_9.rs index 6b3521cc167..e9abed78c73 100644 --- a/pyo3-ffi/src/compat/py_3_9.rs +++ b/pyo3-ffi/src/compat/py_3_9.rs @@ -1,8 +1,6 @@ compat_function!( - originally_defined_for(all( - not(PyPy), - any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 - )); + // Added to python in 3.9 but to limited API in 3.10 + originally_defined_for(any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9))); #[inline] pub unsafe fn PyObject_CallNoArgs(obj: *mut crate::PyObject) -> *mut crate::PyObject { diff --git a/pyo3-ffi/src/cpython/longobject.rs b/pyo3-ffi/src/cpython/longobject.rs index 92bb4c6150d..a460f42e935 100644 --- a/pyo3-ffi/src/cpython/longobject.rs +++ b/pyo3-ffi/src/cpython/longobject.rs @@ -1,11 +1,14 @@ +#[cfg(not(Py_3_13))] use crate::longobject::*; use crate::object::*; #[cfg(Py_3_13)] use crate::pyport::Py_ssize_t; use libc::size_t; +use std::ffi::c_int; +#[cfg(not(Py_3_13))] +use std::ffi::c_uchar; #[cfg(Py_3_13)] use std::ffi::c_void; -use std::ffi::{c_int, c_uchar}; #[cfg(Py_3_13)] extern_libpython! { @@ -53,7 +56,9 @@ extern_libpython! { // skipped PyUnstable_Long_IsCompact // skipped PyUnstable_Long_CompactValue + #[cfg(not(Py_3_13))] // PyO3 uses this function before 3.13, PyLong_AsNativeBytes should be preferred for 3.13 and later #[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")] + #[doc(hidden)] pub fn _PyLong_FromByteArray( bytes: *const c_uchar, n: size_t, @@ -61,7 +66,9 @@ extern_libpython! { is_signed: c_int, ) -> *mut PyObject; + #[cfg(not(Py_3_13))] // PyO3 uses this function before 3.13, PyLong_AsNativeBytes should be preferred for 3.13 and later #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] + #[doc(hidden)] pub fn _PyLong_AsByteArray( v: *mut PyLongObject, bytes: *mut c_uchar, diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index d6747317a31..56d37c7b280 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -540,7 +540,7 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { #[cfg(not(any(GraalPy, Py_3_14)))] #[inline] -pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { +unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { if PyUnicode_IS_ASCII(op) != 0 { (op as *mut PyASCIIObject).offset(1) as *mut c_void } else { @@ -548,9 +548,9 @@ pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { } } -#[cfg(not(any(GraalPy, PyPy)))] +#[cfg(not(any(GraalPy, PyPy, Py_3_14)))] #[inline] -pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void { +unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void { debug_assert!(!(*(op as *mut PyUnicodeObject)).data.any.is_null()); (*(op as *mut PyUnicodeObject)).data.any @@ -627,8 +627,9 @@ pub unsafe fn PyUnicode_READY(op: *mut PyObject) -> c_int { extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyUnicode_New")] pub fn PyUnicode_New(size: Py_ssize_t, maxchar: Py_UCS4) -> *mut PyObject; + #[cfg(not(any(Py_3_12, GraalPy)))] #[cfg_attr(PyPy, link_name = "_PyPyUnicode_Ready")] - pub fn _PyUnicode_Ready(unicode: *mut PyObject) -> c_int; + fn _PyUnicode_Ready(unicode: *mut PyObject) -> c_int; // skipped _PyUnicode_Copy diff --git a/pyo3-ffi/src/moduleobject.rs b/pyo3-ffi/src/moduleobject.rs index 7ba7701aab9..a52c7b5f1b3 100644 --- a/pyo3-ffi/src/moduleobject.rs +++ b/pyo3-ffi/src/moduleobject.rs @@ -25,14 +25,14 @@ extern_libpython! { pub fn PyModule_New(name: *const c_char) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyModule_GetDict")] pub fn PyModule_GetDict(arg1: *mut PyObject) -> *mut PyObject; - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyModule_GetNameObject")] pub fn PyModule_GetNameObject(arg1: *mut PyObject) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyModule_GetName")] pub fn PyModule_GetName(arg1: *mut PyObject) -> *const c_char; #[cfg(not(all(windows, PyPy)))] #[deprecated(note = "Python 3.2")] pub fn PyModule_GetFilename(arg1: *mut PyObject) -> *const c_char; - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyModule_GetFilenameObject")] pub fn PyModule_GetFilenameObject(arg1: *mut PyObject) -> *mut PyObject; // skipped non-limited _PyModule_Clear // skipped non-limited _PyModule_ClearDict diff --git a/pyo3-ffi/src/pyerrors.rs b/pyo3-ffi/src/pyerrors.rs index 2d4835033c7..4515eb55d1d 100644 --- a/pyo3-ffi/src/pyerrors.rs +++ b/pyo3-ffi/src/pyerrors.rs @@ -303,9 +303,26 @@ extern_libpython! { arg2: *mut PyObject, arg3: *mut PyObject, ) -> *mut PyObject; + #[cfg(PyPy)] #[cfg_attr(PyPy, link_name = "PyPyErr_BadInternalCall")] pub fn PyErr_BadInternalCall(); - pub fn _PyErr_BadInternalCall(filename: *const c_char, lineno: c_int); + #[cfg(not(PyPy))] + fn _PyErr_BadInternalCall(filename: *const c_char, lineno: c_int); +} + +#[cfg(not(PyPy))] +#[inline] +#[track_caller] +pub unsafe fn PyErr_BadInternalCall() { + let location = std::panic::Location::caller(); + let file = location.file(); + let line = location.line(); + let file_c_string = std::ffi::CString::new(file) + .unwrap_or_else(|_| std::ffi::CString::new("").unwrap()); + _PyErr_BadInternalCall(file_c_string.as_ptr(), line.try_into().unwrap_or(-1)); +} + +extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyErr_NewException")] pub fn PyErr_NewException( name: *const c_char, diff --git a/pyo3-ffi/src/pyhash.rs b/pyo3-ffi/src/pyhash.rs index b96c40a11e5..0694b39795c 100644 --- a/pyo3-ffi/src/pyhash.rs +++ b/pyo3-ffi/src/pyhash.rs @@ -1,6 +1,6 @@ -#[cfg(not(any(Py_LIMITED_API, PyPy)))] +#[cfg(not(any(Py_LIMITED_API, PyPy, Py_3_14)))] use crate::pyport::{Py_hash_t, Py_ssize_t}; -#[cfg(not(any(Py_LIMITED_API, PyPy)))] +#[cfg(not(any(Py_LIMITED_API, PyPy, Py_3_14)))] use std::ffi::c_void; use std::ffi::{c_int, c_ulong}; @@ -10,7 +10,7 @@ extern_libpython! { // skipped non-limited _Py_HashPointer // skipped non-limited _Py_HashPointerRaw - #[cfg(not(any(Py_LIMITED_API, PyPy)))] + #[cfg(not(any(Py_LIMITED_API, PyPy, Py_3_14)))] pub fn _Py_HashBytes(src: *const c_void, len: Py_ssize_t) -> Py_hash_t; } diff --git a/pyo3-ffi/src/pystate.rs b/pyo3-ffi/src/pystate.rs index 381862964b5..575f76beb9a 100644 --- a/pyo3-ffi/src/pystate.rs +++ b/pyo3-ffi/src/pystate.rs @@ -1,15 +1,11 @@ use crate::moduleobject::PyModuleDef; use crate::object::PyObject; use crate::pytypedefs::{PyInterpreterState, PyThreadState}; -use std::ffi::c_int; +use std::ffi::{c_int, c_long}; #[cfg(any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_10))] -#[cfg(not(PyPy))] use crate::PyFrameObject; -#[cfg(not(PyPy))] -use std::ffi::c_long; - pub const MAX_CO_EXTRA_USERS: c_int = 255; extern_libpython! { @@ -26,7 +22,7 @@ extern_libpython! { #[cfg(not(PyPy))] pub fn PyInterpreterState_GetDict(arg1: *mut PyInterpreterState) -> *mut PyObject; - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyInterpreterState_GetID")] pub fn PyInterpreterState_GetID(arg1: *mut PyInterpreterState) -> i64; #[cfg_attr(PyPy, link_name = "PyPyState_AddModule")] @@ -58,17 +54,17 @@ extern_libpython! { pub fn PyThreadState_Swap(arg1: *mut PyThreadState) -> *mut PyThreadState; #[cfg_attr(PyPy, link_name = "PyPyThreadState_GetDict")] pub fn PyThreadState_GetDict() -> *mut PyObject; - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyThreadState_SetAsyncExc")] pub fn PyThreadState_SetAsyncExc(arg1: c_long, arg2: *mut PyObject) -> c_int; #[cfg(any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_10))] - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyThreadState_GetInterpreter")] pub fn PyThreadState_GetInterpreter(arg1: *mut PyThreadState) -> *mut PyInterpreterState; #[cfg(any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_10))] - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyThreadState_GetFrame")] pub fn PyThreadState_GetFrame(arg1: *mut PyThreadState) -> *mut PyFrameObject; #[cfg(any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_10))] - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyThreadState_GetRecursionLimit")] pub fn PyThreadState_GetID(arg1: *mut PyThreadState) -> i64; } @@ -145,6 +141,6 @@ pub use self::raw::PyGILState_Ensure; extern_libpython! { #[cfg_attr(PyPy, link_name = "PyPyGILState_Release")] pub fn PyGILState_Release(arg1: PyGILState_STATE); - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyGILState_GetThisThreadState")] pub fn PyGILState_GetThisThreadState() -> *mut PyThreadState; } diff --git a/pyo3-ffi/src/refcount.rs b/pyo3-ffi/src/refcount.rs index d404660b03a..51b18789da9 100644 --- a/pyo3-ffi/src/refcount.rs +++ b/pyo3-ffi/src/refcount.rs @@ -343,18 +343,20 @@ pub unsafe fn Py_XDECREF(op: *mut PyObject) { } extern_libpython! { - #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] + #[cfg(all(Py_3_10, Py_LIMITED_API))] #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] + #[cfg_attr(PyPy, link_name = "PyPy_NewRef")] pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; - #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] + #[cfg(all(Py_3_10, Py_LIMITED_API))] #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] + #[cfg_attr(PyPy, link_name = "PyPy_XNewRef")] pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; } // macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here // macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here -#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] +#[cfg(all(Py_3_10, not(Py_LIMITED_API)))] #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] #[inline] pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { @@ -362,7 +364,7 @@ pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { obj } -#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] +#[cfg(all(Py_3_10, not(Py_LIMITED_API)))] #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] #[inline] pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { diff --git a/pyo3-ffi/src/structseq.rs b/pyo3-ffi/src/structseq.rs index 70a97f5b215..855c24996dc 100644 --- a/pyo3-ffi/src/structseq.rs +++ b/pyo3-ffi/src/structseq.rs @@ -1,5 +1,4 @@ use crate::object::{PyObject, PyTypeObject}; -#[cfg(not(PyPy))] use crate::pyport::Py_ssize_t; use std::ffi::{c_char, c_int}; @@ -36,7 +35,7 @@ extern_libpython! { desc: *mut PyStructSequence_Desc, ) -> c_int; - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyStructSequence_NewType")] pub fn PyStructSequence_NewType(desc: *mut PyStructSequence_Desc) -> *mut PyTypeObject; #[cfg_attr(PyPy, link_name = "PyPyStructSequence_New")] pub fn PyStructSequence_New(_type: *mut PyTypeObject) -> *mut PyObject; @@ -58,9 +57,9 @@ pub unsafe fn PyStructSequence_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mu } extern_libpython! { - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyStructSequence_SetItem")] pub fn PyStructSequence_SetItem(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject); - #[cfg(not(PyPy))] + #[cfg_attr(PyPy, link_name = "PyPyStructSequence_GetItem")] pub fn PyStructSequence_GetItem(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject; } diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 203669083c0..142166c153b 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -200,7 +200,6 @@ fn ascii() { // 2 and 4 byte macros return nonsense for this string instance. assert_eq!(PyUnicode_KIND(ptr), PyUnicode_1BYTE_KIND); - // _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings. assert!(!PyUnicode_DATA(ptr).is_null()); assert_eq!(PyUnicode_GET_LENGTH(ptr), s.len().unwrap() as Py_ssize_t); @@ -241,7 +240,6 @@ fn ucs4() { assert!(!PyUnicode_4BYTE_DATA(ptr).is_null()); assert_eq!(PyUnicode_KIND(ptr), PyUnicode_4BYTE_KIND); - // _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings. assert!(!PyUnicode_DATA(ptr).is_null()); assert_eq!(