Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pypy: support 3.9 #2143

Merged
merged 7 commits into from
Feb 25, 2022
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
13 changes: 12 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,16 @@ jobs:
fail-fast: false # If one platform fails, allow the rest to keep testing.
matrix:
rust: [stable]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev", "pypy-3.7-v7.3.7", "pypy-3.8"]
python-version: [
"3.7",
"3.8",
"3.9",
"3.10",
"3.11-dev",
"pypy-3.7-v7.3.7",
"pypy-3.8",
"pypy-3.9"
]
platform:
[
{
Expand Down Expand Up @@ -108,6 +117,8 @@ jobs:
platform: { os: "windows-latest", python-architecture: "x86" }
- python-version: pypy-3.8
platform: { os: "windows-latest", python-architecture: "x86" }
- python-version: pypy-3.9
platform: { os: "windows-latest", python-architecture: "x86" }
include:
# Test minimal supported Rust version
- rust: 1.48.0
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update `inventory` optional dependency to 0.2. [#2019](https://github.com/PyO3/pyo3/pull/2019)
- Drop `paste` dependency. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- The bindings found `pyo3::ffi` are now a re-export of the new `pyo3-ffi` crate. [#2126](https://github.com/PyO3/pyo3/pull/2126)
- Support PyPy 3.9. [#2143](https://github.com/PyO3/pyo3/pull/2143)


### Added

Expand Down Expand Up @@ -89,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use the Rust function path for `wrap_pymodule!` of a `#[pymodule]` with a `#[pyo3(name = "..")]` attribute, not the Python name. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- Fix panic in `#[pyfunction]` generated code when a required argument following an `Option` was not provided. [#2093](https://github.com/PyO3/pyo3/pull/2093)
- Fixed undefined behaviour caused by incorrect `ExactSizeIterator` implementations. [#2124](https://github.com/PyO3/pyo3/pull/2124)
- Fix missing FFI definition `PyCMethod_New` on Python 3.9 and up. [#2143](https://github.com/PyO3/pyo3/pull/2143)
- Add missing FFI definitions `_PyLong_NumBits` and `_PyLong_AsByteArray` on PyPy. [#2146](https://github.com/PyO3/pyo3/pull/2146)
- Fix memory leak in implementation of `AsPyPointer` for `Option<T>`. [#2160](https://github.com/PyO3/pyo3/pull/2160)
- Fix the signature of `_PyLong_NumBits` [#2161](https://github.com/PyO3/pyo3/pull/2161)
Expand Down
26 changes: 23 additions & 3 deletions pyo3-build-config/src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,16 @@ fn default_lib_name_unix(
Some(ld_version) => format!("python{}", ld_version),
None => format!("python{}.{}", version.major, version.minor),
},
PythonImplementation::PyPy => format!("pypy{}-c", version.major),
PythonImplementation::PyPy => {
if version >= (PythonVersion { major: 3, minor: 9 }) {
match ld_version {
Some(ld_version) => format!("pypy{}-c", ld_version),
None => format!("pypy{}.{}-c", version.major, version.minor),
}
} else {
format!("pypy{}-c", version.major)
}
}
}
}

Expand Down Expand Up @@ -1237,6 +1246,11 @@ fn fixup_config_for_abi3(
config: &mut InterpreterConfig,
abi3_version: Option<PythonVersion>,
) -> Result<()> {
// PyPy doesn't support abi3; don't adjust the version
if config.implementation.is_pypy() {
return Ok(());
}

if let Some(version) = abi3_version {
ensure!(
version <= config.version,
Expand Down Expand Up @@ -1567,11 +1581,17 @@ mod tests {
"python3.7md",
);

// PyPy ignores ldversion
// PyPy 3.7 ignores ldversion
assert_eq!(
super::default_lib_name_unix(PythonVersion { major: 3, minor: 9 }, PyPy, Some("3.7md")),
super::default_lib_name_unix(PythonVersion { major: 3, minor: 7 }, PyPy, Some("3.7md")),
"pypy3-c",
);

// PyPy 3.9 includes ldversion
assert_eq!(
super::default_lib_name_unix(PythonVersion { major: 3, minor: 9 }, PyPy, Some("3.9d")),
"pypy3.9d-c",
);
}

#[test]
Expand Down
30 changes: 24 additions & 6 deletions pyo3-ffi/src/cpython/abstract_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
pyport::PY_SSIZE_T_MAX, vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET,
PyTuple_Check, PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL,
};
#[cfg(all(Py_3_8, not(PyPy)))]
#[cfg(Py_3_8)]
use libc::size_t;

extern "C" {
Expand Down Expand Up @@ -101,17 +101,29 @@ pub unsafe fn PyObject_Vectorcall(
}

extern "C" {
#[cfg(all(Py_3_8, not(PyPy)))]
#[cfg_attr(not(Py_3_9), link_name = "_PyObject_VectorcallDict")]
#[cfg(all(PyPy, Py_3_8))]
#[cfg_attr(not(Py_3_9), link_name = "_PyPyObject_Vectorcall")]
pub fn PyObject_Vectorcall(
callable: *mut PyObject,
args: *const *mut PyObject,
nargsf: size_t,
kwnames: *mut PyObject,
) -> *mut PyObject;

#[cfg(all(Py_3_8))]
#[cfg_attr(all(not(PyPy), 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,
kwargs: *mut PyObject,
kwdict: *mut PyObject,
) -> *mut PyObject;

#[cfg(all(Py_3_8, not(PyPy)))]
#[cfg_attr(not(Py_3_9), link_name = "_PyVectorcall_Call")]
#[cfg(all(Py_3_8))]
#[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,
Expand Down Expand Up @@ -152,6 +164,12 @@ pub unsafe fn _PyObject_CallNoArg(func: *mut PyObject) -> *mut PyObject {
)
}

extern "C" {
#[cfg(PyPy)]
#[link_name = "_PyPyObject_CallNoArg"]
pub fn _PyObject_CallNoArg(func: *mut PyObject) -> *mut PyObject;
}

#[cfg(all(Py_3_8, not(PyPy)))]
#[inline(always)]
pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject {
Expand Down
28 changes: 27 additions & 1 deletion pyo3-ffi/src/methodobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub union PyMethodDefPointer {
const _: () =
[()][mem::size_of::<PyMethodDefPointer>() - mem::size_of::<Option<extern "C" fn()>>()];

#[cfg(not(Py_3_9))]
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyCFunction_New")]
pub fn PyCFunction_New(ml: *mut PyMethodDef, slf: *mut PyObject) -> *mut PyObject;
Expand All @@ -129,7 +130,32 @@ extern "C" {
) -> *mut PyObject;
}

// skipped non-limited / 3.9 PyCMethod_New
#[cfg(Py_3_9)]
#[inline]
pub unsafe fn PyCFunction_New(ml: *mut PyMethodDef, slf: *mut PyObject) -> *mut PyObject {
PyCFunction_NewEx(ml, slf, std::ptr::null_mut())
}

#[cfg(Py_3_9)]
#[inline]
pub unsafe fn PyCFunction_NewEx(
ml: *mut PyMethodDef,
slf: *mut PyObject,
module: *mut PyObject,
) -> *mut PyObject {
PyCMethod_New(ml, slf, module, std::ptr::null_mut())
}

#[cfg(Py_3_9)]
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyCMethod_New")]
pub fn PyCMethod_New(
ml: *mut PyMethodDef,
slf: *mut PyObject,
module: *mut PyObject,
cls: *mut PyTypeObject,
) -> *mut PyObject;
}

/* Flag passed to newmethodobject */
pub const METH_VARARGS: c_int = 0x0001;
Expand Down
4 changes: 2 additions & 2 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ impl<T> Py<T> {
/// This is equivalent to the Python expression `self()`.
pub fn call0(&self, py: Python) -> PyResult<PyObject> {
cfg_if::cfg_if! {
if #[cfg(Py_3_9)] {
if #[cfg(all(Py_3_9, not(PyPy)))] {
// Optimized path on python 3.9+
unsafe {
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_CallNoArgs(self.as_ptr()))
Expand Down Expand Up @@ -636,7 +636,7 @@ impl<T> Py<T> {
/// This is equivalent to the Python expression `self.name()`.
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy))))] {
// Optimized path on python 3.9+
unsafe {
let name = name.into_py(py);
Expand Down
14 changes: 7 additions & 7 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn type_object_creation_failed(py: Python, e: PyErr, name: &'static str) -> ! {
/// Additional type initializations necessary before Python 3.10
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
unsafe fn tp_init_additional(
type_object: *mut ffi::PyTypeObject,
_type_object: *mut ffi::PyTypeObject,
_tp_doc: &str,
#[cfg(not(Py_3_9))] buffer_procs: &ffi::PyBufferProcs,
#[cfg(not(Py_3_9))] dict_offset: Option<ffi::Py_ssize_t>,
Expand All @@ -236,26 +236,26 @@ unsafe fn tp_init_additional(
// heap-types, and it removed the text_signature value from it.
// We go in after the fact and replace tp_doc with something
// that _does_ include the text_signature value!
ffi::PyObject_Free((*type_object).tp_doc as _);
ffi::PyObject_Free((*_type_object).tp_doc as _);
let data = ffi::PyObject_Malloc(_tp_doc.len());
data.copy_from(_tp_doc.as_ptr() as _, _tp_doc.len());
(*type_object).tp_doc = data as _;
(*_type_object).tp_doc = data as _;
}
}

// Setting buffer protocols, tp_dictoffset and tp_weaklistoffset via slots doesn't work until
// Python 3.9, so on older versions we must manually fixup the type object.
#[cfg(not(Py_3_9))]
{
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer_procs.bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer_procs.bf_releasebuffer;
(*(*_type_object).tp_as_buffer).bf_getbuffer = buffer_procs.bf_getbuffer;
(*(*_type_object).tp_as_buffer).bf_releasebuffer = buffer_procs.bf_releasebuffer;

if let Some(dict_offset) = dict_offset {
(*type_object).tp_dictoffset = dict_offset;
(*_type_object).tp_dictoffset = dict_offset;
}

if let Some(weaklist_offset) = weaklist_offset {
(*type_object).tp_weaklistoffset = weaklist_offset;
(*_type_object).tp_weaklistoffset = weaklist_offset;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ impl PyAny {
/// This is equivalent to the Python expression `help()`.
pub fn call0(&self) -> PyResult<&PyAny> {
cfg_if::cfg_if! {
if #[cfg(Py_3_9)] {
if #[cfg(all(Py_3_9, not(PyPy)))] {
// Optimized path on python 3.9+
unsafe {
self.py().from_owned_ptr_or_err(ffi::PyObject_CallNoArgs(self.as_ptr()))
Expand Down Expand Up @@ -461,7 +461,7 @@ impl PyAny {
/// ```
pub fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy))))] {
// Optimized path on python 3.9+
unsafe {
let name = name.into_py(self.py());
Expand Down