Skip to content

Commit

Permalink
Merge pull request #2262 from messense/pypy-0.15
Browse files Browse the repository at this point in the history
Backport PyPy 3.9 support for pyo3 0.15
  • Loading branch information
davidhewitt committed Apr 14, 2022
2 parents eb5059a + 5016202 commit a9f7259
Show file tree
Hide file tree
Showing 45 changed files with 242 additions and 140 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/bench.yml
Expand Up @@ -4,6 +4,10 @@ on:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

name: Benchmark

jobs:
Expand Down
31 changes: 21 additions & 10 deletions .github/workflows/ci.yml
Expand Up @@ -6,6 +6,10 @@ on:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

Expand All @@ -15,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install black==20.8b1
- run: pip install black==22.3.0
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
Expand All @@ -41,7 +45,8 @@ jobs:
needs: [fmt]
runs-on: ubuntu-latest
strategy:
fail-fast: false # If one platform fails, allow the rest to keep testing.
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
target: [powerpc64le-unknown-linux-gnu, s390x-unknown-linux-gnu, wasm32-wasi]
name: check-${{ matrix.target }}
Expand Down Expand Up @@ -73,10 +78,11 @@ jobs:
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} ${{ matrix.msrv }}
runs-on: ${{ matrix.platform.os }}
strategy:
fail-fast: false # If one platform fails, allow the rest to keep testing.
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
rust: [stable]
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy-3.6, pypy-3.7, pypy-3.8]
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy-3.6, pypy-3.7, pypy-3.8, pypy-3.9]
platform:
[
{
Expand All @@ -94,11 +100,6 @@ jobs:
python-architecture: "x64",
rust-target: "x86_64-pc-windows-msvc",
},
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
},
]
exclude:
# PyPy 3.6 is EOL and not working on macos-latest (now macos-11)
Expand All @@ -107,7 +108,7 @@ jobs:
# There is no 64-bit pypy on windows for pypy-3.6
- python-version: pypy-3.6
platform: { os: "windows-latest", python-architecture: "x64" }
# PyPy 3.7 on Windows doesn't release 32-bit builds any more
# PyPy doesn't release 32-bit Windows builds any more
- python-version: pypy-3.7
platform: { os: "windows-latest", python-architecture: "x86" }
- python-version: pypy-3.8
Expand All @@ -132,6 +133,15 @@ jobs:
rust-target: "x86_64-unknown-linux-gnu",
}
msrv: "MSRV"
# Test 32-bit Windows only with the latest Python version
- rust: stable
python-version: "3.10"
platform:
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
}

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -184,6 +194,7 @@ jobs:
run: |
cargo update -p indexmap --precise 1.6.2
cargo update -p hashbrown:0.11.2 --precise 0.9.1
cargo update -p clap --precise 2.33.4
- name: Build docs
run: cargo doc --no-deps --no-default-features --features "${{ steps.settings.outputs.all_additive_features }}"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/guide.yml
Expand Up @@ -8,6 +8,10 @@ on:
release:
types: [published]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Expand Up @@ -43,13 +43,16 @@ serde = { version = "1.0", optional = true }
assert_approx_eq = "1.1.0"
# O.3.5 uses the matches! macro, which isn't compatible with Rust 1.41
criterion = "=0.3.4"
# 2.34 uses the matches! macro, which isn't compatible with Rust 1.41
clap = "=2.33"
# half and bitflags use if/match in const fn, which isn't compatible with Rust 1.41
half = "=1.7.1"
bitflags = "=1.2.1"
trybuild = "1.0.49"
rustversion = "1.0"
# 1.0.0 requires Rust 1.50
proptest = { version = "0.10.1", default-features = false, features = ["std"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.61"

# features needed to run the PyO3 test suite
Expand Down
1 change: 1 addition & 0 deletions examples/pyo3-benchmarks/src/lib.rs
@@ -1,3 +1,4 @@
#![allow(clippy::needless_option_as_deref)]
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};

Expand Down
12 changes: 8 additions & 4 deletions examples/pyo3-pytests/src/buf_and_str.rs
Expand Up @@ -14,22 +14,26 @@ impl BytesExtractor {
BytesExtractor {}
}

pub fn from_bytes(&mut self, bytes: &PyBytes) -> PyResult<usize> {
#[staticmethod]
pub fn from_bytes(bytes: &PyBytes) -> PyResult<usize> {
let byte_vec: Vec<u8> = bytes.extract()?;
Ok(byte_vec.len())
}

pub fn from_str(&mut self, string: &PyString) -> PyResult<usize> {
#[staticmethod]
pub fn from_str(string: &PyString) -> PyResult<usize> {
let rust_string: String = string.extract()?;
Ok(rust_string.len())
}

pub fn from_str_lossy(&mut self, string: &PyString) -> PyResult<usize> {
#[staticmethod]
pub fn from_str_lossy(string: &PyString) -> PyResult<usize> {
let rust_string_lossy: String = string.to_string_lossy().to_string();
Ok(rust_string_lossy.len())
}

pub fn from_buffer(&mut self, buf: &PyAny) -> PyResult<usize> {
#[staticmethod]
pub fn from_buffer(buf: &PyAny) -> PyResult<usize> {
let buf = PyBuffer::<u8>::get(buf)?;
Ok(buf.item_count())
}
Expand Down
1 change: 1 addition & 0 deletions examples/pyo3-pytests/src/datetime.rs
@@ -1,3 +1,4 @@
#![allow(clippy::needless_option_as_deref)]
use pyo3::prelude::*;
use pyo3::types::{
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTuple,
Expand Down
4 changes: 2 additions & 2 deletions examples/pyo3-pytests/tests/test_othermod.py
Expand Up @@ -3,14 +3,14 @@

from pyo3_pytests import othermod

INTEGER32_ST = st.integers(min_value=(-(2 ** 31)), max_value=(2 ** 31 - 1))
INTEGER32_ST = st.integers(min_value=(-(2**31)), max_value=(2**31 - 1))
USIZE_ST = st.integers(min_value=othermod.USIZE_MIN, max_value=othermod.USIZE_MAX)


@given(x=INTEGER32_ST)
def test_double(x):
expected = x * 2
assume(-(2 ** 31) <= expected <= (2 ** 31 - 1))
assume(-(2**31) <= expected <= (2**31 - 1))
assert othermod.double(x) == expected


Expand Down
28 changes: 24 additions & 4 deletions pyo3-build-config/src/impl_.rs
Expand Up @@ -911,7 +911,7 @@ fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Vec<Pat
} else {
"python3.".into()
};
for f in fs::read_dir(path).expect("Path does not exist").into_iter() {
for f in fs::read_dir(path).expect("Path does not exist") {
sysconfig_paths.extend(match &f {
// Python 3.6 sysconfigdata without platform specifics
Ok(f) if f.file_name() == "_sysconfigdata.py" => vec![f.path()],
Expand Down Expand Up @@ -1062,7 +1062,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 @@ -1168,6 +1177,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 @@ -1472,11 +1486,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
1 change: 0 additions & 1 deletion pyo3-macros-backend/src/lib.rs
Expand Up @@ -3,7 +3,6 @@

#![cfg_attr(docsrs, feature(doc_cfg))]
#![recursion_limit = "1024"]

// Listed first so that macros in this module are available in the rest of the crate.
#[macro_use]
mod utils;
Expand Down
11 changes: 4 additions & 7 deletions pyo3-macros-backend/src/method.rs
Expand Up @@ -29,9 +29,7 @@ impl<'a> FnArg<'a> {
/// Transforms a rust fn arg parsed with syn into a method::FnArg
pub fn parse(arg: &'a mut syn::FnArg) -> Result<Self> {
match arg {
syn::FnArg::Receiver(recv) => {
bail_spanned!(recv.span() => "unexpected receiver")
} // checked in parse_fn_type
syn::FnArg::Receiver(recv) => bail_spanned!(recv.span() => "unexpected receiver"), // checked in parse_fn_type
syn::FnArg::Typed(cap) => {
if let syn::Type::ImplTrait(_) = &*cap.ty {
bail_spanned!(cap.ty.span() => IMPL_TRAIT_ERR);
Expand Down Expand Up @@ -101,9 +99,7 @@ impl FnType {
cls.expect("no class given for Fn with a \"self\" receiver"),
error_mode,
),
FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => {
quote!()
}
FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => quote!(),
FnType::FnClass => {
quote! {
let _slf = ::pyo3::types::PyType::from_type_ptr(_py, _slf as *mut ::pyo3::ffi::PyTypeObject);
Expand Down Expand Up @@ -352,7 +348,8 @@ impl<'a> FnSpec<'a> {
parse_method_receiver(first_arg)
};

#[allow(clippy::manual_strip)] // for strip_prefix replacement supporting rust < 1.45
#[allow(clippy::manual_strip)]
// for strip_prefix replacement supporting rust < 1.45
// strip get_ or set_
let strip_fn_name = |prefix: &'static str| {
let ident = name.unraw().to_string();
Expand Down
4 changes: 2 additions & 2 deletions pyo3-macros-backend/src/params.rs
Expand Up @@ -266,7 +266,7 @@ fn impl_arg_param(
}
};

return if let syn::Type::Reference(tref) = unwrap_ty_group(arg.optional.unwrap_or(ty)) {
if let syn::Type::Reference(tref) = unwrap_ty_group(arg.optional.unwrap_or(ty)) {
let mut tref = remove_lifetime(tref);
if let Some(cls) = self_ {
replace_self(&mut tref.elem, cls);
Expand Down Expand Up @@ -298,5 +298,5 @@ fn impl_arg_param(
Ok(quote_arg_span! {
let #arg_name = #arg_value_or_default;
})
};
}
}
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/pyimpl.rs
Expand Up @@ -36,7 +36,7 @@ pub fn build_py_methods(

pub fn impl_methods(
ty: &syn::Type,
impls: &mut Vec<syn::ImplItem>,
impls: &mut [syn::ImplItem],
methods_type: PyClassMethodsType,
) -> syn::Result<TokenStream> {
let mut trait_impls = Vec::new();
Expand Down
4 changes: 1 addition & 3 deletions pyo3-macros-backend/src/pymethod.rs
Expand Up @@ -138,9 +138,7 @@ pub fn gen_py_method(
cls,
PropertyType::Function { self_type, spec },
)?),
(_, FnType::FnModule) => {
unreachable!("methods cannot be FnModule")
}
(_, FnType::FnModule) => unreachable!("methods cannot be FnModule"),
})
}

Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/pyproto.rs
Expand Up @@ -45,7 +45,7 @@ pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {

fn impl_proto_impl(
ty: &syn::Type,
impls: &mut Vec<syn::ImplItem>,
impls: &mut [syn::ImplItem],
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
let mut trait_impls = TokenStream::new();
Expand Down
6 changes: 1 addition & 5 deletions pyo3-macros-backend/src/utils.rs
Expand Up @@ -79,11 +79,7 @@ pub fn get_doc(
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
if let Some((python_name, text_signature)) = text_signature {
// create special doc string lines to set `__text_signature__`
let signature_lines = format!(
"{}{}\n--\n\n",
python_name.to_string(),
text_signature.lit.value()
);
let signature_lines = format!("{}{}\n--\n\n", python_name, text_signature.lit.value());
signature_lines.to_tokens(tokens);
comma.to_tokens(tokens);
}
Expand Down
4 changes: 4 additions & 0 deletions src/buffer.rs
Expand Up @@ -168,6 +168,10 @@ fn is_matching_endian(c: u8) -> bool {
}

/// Trait implemented for possible element types of `PyBuffer`.
///
/// # Safety
///
/// This trait must only be implemented for types which represent valid elements of Python buffers.
pub unsafe trait Element: Copy {
/// Gets whether the element specified in the format string is potentially compatible.
/// Alignment and size are checked separately from this function.
Expand Down
6 changes: 4 additions & 2 deletions src/conversion.rs
Expand Up @@ -195,7 +195,6 @@ where
/// }
/// }
/// }
/// # fn main() {
/// # Python::with_gil(|py| {
/// # let v = Value::Integer(73).into_py(py);
/// # let v = v.extract::<i32>(py).unwrap();
Expand All @@ -206,7 +205,6 @@ where
/// # let v = Value::None.into_py(py);
/// # let v = v.extract::<Option<Vec<i32>>>(py).unwrap();
/// # });
/// # }
/// ```
/// Python code will see this as any of the `int`, `string` or `None` objects.
#[cfg_attr(docsrs, doc(alias = "IntoPyCallbackOutput"))]
Expand Down Expand Up @@ -463,6 +461,10 @@ impl IntoPy<Py<PyTuple>> for () {
}

/// Raw level conversion between `*mut ffi::PyObject` and PyO3 types.
///
/// # Safety
///
/// See safety notes on individual functions.
pub unsafe trait FromPyPointer<'p>: Sized {
/// Convert from an arbitrary `PyObject`.
///
Expand Down

0 comments on commit a9f7259

Please sign in to comment.