diff --git a/newsfragments/4237.changed.md b/newsfragments/4237.changed.md new file mode 100644 index 00000000..25dd922d --- /dev/null +++ b/newsfragments/4237.changed.md @@ -0,0 +1 @@ +Respect the Python "limited API" when building for the `abi3` feature on PyPy or GraalPy. diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 35c300da..9ef9f477 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -26,7 +26,7 @@ use target_lexicon::{Environment, OperatingSystem}; use crate::{ bail, ensure, errors::{Context, Error, Result}, - format_warn, warn, + warn, }; /// Minimum Python version PyO3 supports. @@ -171,20 +171,13 @@ impl InterpreterConfig { out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); } - if self.implementation.is_pypy() { - out.push("cargo:rustc-cfg=PyPy".to_owned()); - if self.abi3 { - out.push(format_warn!( - "PyPy does not yet support abi3 so the build artifacts will be version-specific. \ - See https://foss.heptapod.net/pypy/pypy/-/issues/3397 for more information." - )); - } - } else if self.implementation.is_graalpy() { - println!("cargo:rustc-cfg=GraalPy"); - if self.abi3 { - warn!("GraalPy does not support abi3 so the build artifacts will be version-specific."); - } - } else if self.abi3 { + match self.implementation { + PythonImplementation::CPython => {} + PythonImplementation::PyPy => out.push("cargo:rustc-cfg=PyPy".to_owned()), + PythonImplementation::GraalPy => out.push("cargo:rustc-cfg=GraalPy".to_owned()), + } + + if self.abi3 { out.push("cargo:rustc-cfg=Py_LIMITED_API".to_owned()); } @@ -2722,10 +2715,7 @@ mod tests { "cargo:rustc-cfg=Py_3_6".to_owned(), "cargo:rustc-cfg=Py_3_7".to_owned(), "cargo:rustc-cfg=PyPy".to_owned(), - "cargo:warning=PyPy does not yet support abi3 so the build artifacts \ - will be version-specific. See https://foss.heptapod.net/pypy/pypy/-/issues/3397 \ - for more information." - .to_owned(), + "cargo:rustc-cfg=Py_LIMITED_API".to_owned(), ] ); } diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index 0f4931d6..b4521678 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -4,7 +4,7 @@ use pyo3_build_config::{ cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config, InterpreterConfig, PythonVersion, }, - PythonImplementation, + warn, PythonImplementation, }; /// Minimum Python version PyO3 supports. @@ -104,6 +104,19 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { } } + if interpreter_config.abi3 { + match interpreter_config.implementation { + PythonImplementation::CPython => {} + PythonImplementation::PyPy => warn!( + "PyPy does not yet support abi3 so the build artifacts will be version-specific. \ + See https://foss.heptapod.net/pypy/pypy/-/issues/3397 for more information." + ), + PythonImplementation::GraalPy => warn!( + "GraalPy does not support abi3 so the build artifacts will be version-specific." + ), + } + } + Ok(()) } diff --git a/pyo3-ffi/src/cpython/pythonrun.rs b/pyo3-ffi/src/cpython/pythonrun.rs index 94863166..fe78f55c 100644 --- a/pyo3-ffi/src/cpython/pythonrun.rs +++ b/pyo3-ffi/src/cpython/pythonrun.rs @@ -135,13 +135,9 @@ extern "C" { } #[inline] -#[cfg(not(GraalPy))] +#[cfg(not(any(PyPy, GraalPy)))] pub unsafe fn Py_CompileString(string: *const c_char, p: *const c_char, s: c_int) -> *mut PyObject { - #[cfg(not(PyPy))] - return Py_CompileStringExFlags(string, p, s, std::ptr::null_mut(), -1); - - #[cfg(PyPy)] - Py_CompileStringFlags(string, p, s, std::ptr::null_mut()) + Py_CompileStringExFlags(string, p, s, std::ptr::null_mut(), -1) } #[inline] diff --git a/pyo3-ffi/src/pythonrun.rs b/pyo3-ffi/src/pythonrun.rs index 10985b60..e7ea2d2e 100644 --- a/pyo3-ffi/src/pythonrun.rs +++ b/pyo3-ffi/src/pythonrun.rs @@ -1,7 +1,7 @@ use crate::object::*; #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] use libc::FILE; -#[cfg(all(not(PyPy), any(Py_LIMITED_API, not(Py_3_10), GraalPy)))] +#[cfg(any(Py_LIMITED_API, not(Py_3_10), PyPy, GraalPy))] use std::os::raw::c_char; use std::os::raw::c_int; @@ -20,6 +20,28 @@ extern "C" { pub fn PyErr_DisplayException(exc: *mut PyObject); } +#[inline] +#[cfg(PyPy)] +pub unsafe fn Py_CompileString(string: *const c_char, p: *const c_char, s: c_int) -> *mut PyObject { + // PyPy's implementation of Py_CompileString always forwards to Py_CompileStringFlags; this + // is only available in the non-limited API and has a real definition for all versions in + // the cpython/ subdirectory. + #[cfg(Py_LIMITED_API)] + extern "C" { + #[link_name = "PyPy_CompileStringFlags"] + pub fn Py_CompileStringFlags( + string: *const c_char, + p: *const c_char, + s: c_int, + f: *mut std::os::raw::c_void, // Actually *mut Py_CompilerFlags in the real definition + ) -> *mut PyObject; + } + #[cfg(not(Py_LIMITED_API))] + use crate::Py_CompileStringFlags; + + Py_CompileStringFlags(string, p, s, std::ptr::null_mut()) +} + // skipped PyOS_InputHook pub const PYOS_STACK_MARGIN: c_int = 2048; diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 5aee1618..b7878c96 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -2,10 +2,7 @@ use crate::ffi::*; use crate::types::any::PyAnyMethods; use crate::Python; -#[cfg(all(PyPy, feature = "macros"))] -use crate::types::PyString; - -#[cfg(not(any(Py_LIMITED_API, PyPy)))] +#[cfg(all(not(Py_LIMITED_API), any(not(PyPy), feature = "macros")))] use crate::types::PyString; #[cfg(not(Py_LIMITED_API))] @@ -164,7 +161,6 @@ fn ascii_object_bitfield() { #[test] #[cfg(not(any(Py_LIMITED_API, PyPy)))] -#[cfg_attr(Py_3_10, allow(deprecated))] fn ascii() { Python::with_gil(|py| { // This test relies on implementation details of PyString. @@ -206,7 +202,6 @@ fn ascii() { #[test] #[cfg(not(any(Py_LIMITED_API, PyPy)))] -#[cfg_attr(Py_3_10, allow(deprecated))] fn ucs4() { Python::with_gil(|py| { let s = "哈哈🐈";