Skip to content

Commit

Permalink
Merge pull request #2143 from davidhewitt/pypy-3.9-dev
Browse files Browse the repository at this point in the history
pypy: support 3.9
  • Loading branch information
davidhewitt authored Feb 25, 2022
2 parents 7c865fc + efbf92b commit 1e1d676
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 22 deletions.
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

0 comments on commit 1e1d676

Please sign in to comment.