Skip to content
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
41 changes: 34 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,62 @@ jobs:
strategy:
max-parallel: 12
matrix:
python-version: [3.5, 3.6, 3.7, 3.8]
python-version: [3.5, 3.6, 3.7, 3.8, pypy3]
platform: [
{ os: "macOS-latest", python-architecture: "x64", rust-target: "x86_64-apple-darwin" },
{ os: "windows-latest", python-architecture: "x64", rust-target: "x86_64-pc-windows-msvc" },
{ os: "windows-latest", python-architecture: "x86", rust-target: "i686-pc-windows-msvc" },
]
exclude:
# There is no 64-bit pypy on windows
- python-version: pypy3
platform: { os: "windows-latest", python-architecture: "x64" }


steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install Rust
architecture: ${{ matrix.platform.python-architecture }}

- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
target: ${{ matrix.platform.rust-target }}

- run: rustup set default-host ${{ matrix.platform.rust-target }}

- name: Build without default features
run: cargo build --no-default-features --verbose

- name: Build with default features
run: cargo build --verbose
- name: Install test dependencies
run: cargo build --features "num-bigint num-complex" --verbose

# Run tests (except on PyPy, because no embedding API).
- if: matrix.python-version != 'pypy3'
name: Test
run: cargo test --features "num-bigint num-complex"

- name: Test proc-macro code
run: cargo test --manifest-path=pyo3-derive-backend/Cargo.toml

- name: Install python test dependencies
run: |
python -m pip install -U pip setuptools
pip install setuptools-rust pytest pytest-benchmark tox tox-venv
- name: Test
run: ci/actions/test.sh

- name: Test example extension modules
shell: bash
run: |
for example_dir in examples/*; do
cd $example_dir
tox -c "tox.ini" -e py
cd -
done

env:
RUST_BACKTRACE: 1
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Don't rely on the order of structmembers to compute offsets in PyCell. Related to
[#1058](https://github.com/PyO3/pyo3/pull/1058). [#1059](https://github.com/PyO3/pyo3/pull/1059)
- Allows `&Self` as a `#[pymethods]` argument again. [#1071](https://github.com/PyO3/pyo3/pull/1071)
- Fix best-effort build against PyPy 3.6. #[1092](https://github.com/PyO3/pyo3/pull/1092)

## [0.11.1] - 2020-06-30
### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste

PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.0.

PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
Building with PyPy is also possible (via cpyext) for Python 3.6, targeted PyPy version is 7.3+.
Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html).

You can either write a native Python module in Rust, or use Python from a Rust binary.
Expand Down
12 changes: 6 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,7 @@ fn get_library_link_name(version: &PythonVersion, ld_version: &str) -> String {
Some(minor) => format!("{}", minor),
None => String::new(),
};
match version.implementation {
PythonInterpreterKind::CPython => {
format!("python{}{}", version.major, minor_or_empty_string)
}
PythonInterpreterKind::PyPy => format!("pypy{}-c", version.major),
}
format!("python{}{}", version.major, minor_or_empty_string)
} else {
match version.implementation {
PythonInterpreterKind::CPython => format!("python{}", ld_version),
Expand All @@ -338,6 +333,11 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {

#[cfg(target_os = "macos")]
fn get_macos_linkmodel(config: &InterpreterConfig) -> Result<String> {
// PyPy 3.6 ships with a shared library, but doesn't have Py_ENABLE_SHARED.
if config.version.implementation == PythonInterpreterKind::PyPy {
return Ok("shared".to_string());
}

let script = r#"
import sysconfig
Expand Down
12 changes: 0 additions & 12 deletions ci/actions/test.sh

This file was deleted.

4 changes: 3 additions & 1 deletion src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,11 @@ impl_native_exception!(
);
impl_native_exception!(PyTimeoutError, TimeoutError, PyExc_TimeoutError);

#[cfg(not(all(windows, PyPy)))]
impl_native_exception!(PyEnvironmentError, EnvironmentError, PyExc_EnvironmentError);
#[cfg(not(all(windows, PyPy)))]
impl_native_exception!(PyIOError, IOError, PyExc_IOError);
#[cfg(target_os = "windows")]
#[cfg(all(windows, not(PyPy)))]
impl_native_exception!(PyWindowsError, WindowsError, PyExc_WindowsError);

impl PyUnicodeDecodeError {
Expand Down
1 change: 1 addition & 0 deletions src/ffi/dictobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::os::raw::{c_char, c_int};

#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyDict_Type")]
pub static mut PyDict_Type: PyTypeObject;
pub static mut PyDictIterKey_Type: PyTypeObject;
pub static mut PyDictIterValue_Type: PyTypeObject;
Expand Down
2 changes: 2 additions & 0 deletions src/ffi/longobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extern "C" {

#[cfg(not(Py_LIMITED_API))]
extern "C" {
#[cfg(not(PyPy))]
pub fn _PyLong_NumBits(obj: *mut PyObject) -> c_int;

#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
Expand All @@ -91,6 +92,7 @@ extern "C" {
is_signed: c_int,
) -> *mut PyObject;

#[cfg(not(PyPy))]
pub fn _PyLong_AsByteArray(
v: *mut PyLongObject,
bytes: *mut c_uchar,
Expand Down
4 changes: 2 additions & 2 deletions src/ffi/marshal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use super::PyObject;
use std::os::raw::{c_char, c_int};

extern "C" {
#[cfg_attr(PyPy, link_name = "PyMarshal_WriteObjectToString")]
#[cfg_attr(PyPy, link_name = "PyPyMarshal_WriteObjectToString")]
pub fn PyMarshal_WriteObjectToString(object: *mut PyObject, version: c_int) -> *mut PyObject;

#[cfg_attr(PyPy, link_name = "PyMarshal_ReadObjectFromString")]
#[cfg_attr(PyPy, link_name = "PyPyMarshal_ReadObjectFromString")]
pub fn PyMarshal_ReadObjectFromString(data: *const c_char, len: isize) -> *mut PyObject;
}
2 changes: 2 additions & 0 deletions src/ffi/moduleobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyModule_GetDict")]
pub fn PyModule_GetDict(arg1: *mut PyObject) -> *mut PyObject;
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)))]
pub fn PyModule_GetFilename(arg1: *mut PyObject) -> *const c_char;
pub fn PyModule_GetFilenameObject(arg1: *mut PyObject) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyModule_GetDef")]
Expand Down
5 changes: 4 additions & 1 deletion src/ffi/pyerrors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyExc_ImportError")]
pub static mut PyExc_ImportError: *mut PyObject;
#[cfg(Py_3_6)]
#[cfg_attr(PyPy, link_name = "PyPyExc_ModuleNotFoundError")]
pub static mut PyExc_ModuleNotFoundError: *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyExc_IndexError")]
pub static mut PyExc_IndexError: *mut PyObject;
Expand Down Expand Up @@ -281,9 +282,11 @@ extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyExc_TimeoutError")]
pub static mut PyExc_TimeoutError: *mut PyObject;

#[cfg(not(all(windows, PyPy)))]
pub static mut PyExc_EnvironmentError: *mut PyObject;
#[cfg(not(all(windows, PyPy)))]
pub static mut PyExc_IOError: *mut PyObject;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since IOError is just a alias of OSError on Python 3, I think we can expose PyExc_IOError as PyPyExc_OSError?

Use case: https://github.com/milesgranger/pyrus-cramjam/runs/2227542030?check_suite_focus=true

Installing collected packages: cramjam
Successfully installed cramjam-2.2.0
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: /opt/hostedtoolcache/PyPy/3.6.12/x64/site-packages/cramjam.pypy36-pp73-x86_64-linux-gnu.so: undefined symbol: PyExc_IOError

milesgranger/cramjam@f60849c#diff-76866598ce8fd16261a27ac58a84b2825e6e77fc37c163a6afa60f0f4477e569R343

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but does it seem that PyPy takes a different strategy for that error?
https://foss.heptapod.net/pypy/pypy/-/blob/branch/default/pypy/module/exceptions/moduledef.py#L21

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but it seems to be the same on the Python side on my local PyPy installation.

Python 3.7.10 (7035471788c8, Mar 19 2021, 02:02:14)
[PyPy 7.3.5-alpha0 with GCC Apple LLVM 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``finally, mercurial migration is
happening!''
>>>> IOError
<class 'OSError'>
>>>> OSError
<class 'OSError'>
>>>> IOError is OSError
True

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I got it, thanks.

#[cfg(windows)]
#[cfg(all(windows, not(PyPy)))]
pub static mut PyExc_WindowsError: *mut PyObject;

pub static mut PyExc_RecursionErrorInst: *mut PyObject;
Expand Down
2 changes: 1 addition & 1 deletion src/ffi/setobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ extern "C" {
pub fn PySet_Pop(set: *mut PyObject) -> *mut PyObject;

#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(PyPy, link_name = "_PySet_NextEntry")]
#[cfg_attr(PyPy, link_name = "_PyPySet_NextEntry")]
pub fn _PySet_NextEntry(
set: *mut PyObject,
pos: *mut Py_ssize_t,
Expand Down
1 change: 1 addition & 0 deletions src/ffi/warnings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::ffi::pyport::Py_ssize_t;
use std::os::raw::{c_char, c_int};

extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyErr_WarnEx")]
pub fn PyErr_WarnEx(
category: *mut PyObject,
message: *const c_char,
Expand Down
44 changes: 18 additions & 26 deletions src/types/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,15 @@ impl PyComplex {
unsafe { ffi::PyComplex_ImagAsDouble(self.as_ptr()) }
}
/// Returns `|self|`.
#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
pub fn abs(&self) -> c_double {
unsafe {
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
ffi::_Py_c_abs(val)
}
}
/// Returns `self ** other`
#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
pub fn pow(&self, other: &PyComplex) -> &PyComplex {
unsafe {
self.py()
Expand All @@ -52,8 +50,7 @@ impl PyComplex {
}
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[inline(always)]
unsafe fn complex_operation(
l: &PyComplex,
Expand All @@ -65,8 +62,7 @@ unsafe fn complex_operation(
ffi::PyComplex_FromCComplex(operation(l_val, r_val))
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
impl<'py> Add for &'py PyComplex {
type Output = &'py PyComplex;
fn add(self, other: &'py PyComplex) -> &'py PyComplex {
Expand All @@ -77,8 +73,7 @@ impl<'py> Add for &'py PyComplex {
}
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
impl<'py> Sub for &'py PyComplex {
type Output = &'py PyComplex;
fn sub(self, other: &'py PyComplex) -> &'py PyComplex {
Expand All @@ -89,8 +84,7 @@ impl<'py> Sub for &'py PyComplex {
}
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
impl<'py> Mul for &'py PyComplex {
type Output = &'py PyComplex;
fn mul(self, other: &'py PyComplex) -> &'py PyComplex {
Expand All @@ -101,8 +95,7 @@ impl<'py> Mul for &'py PyComplex {
}
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
impl<'py> Div for &'py PyComplex {
type Output = &'py PyComplex;
fn div(self, other: &'py PyComplex) -> &'py PyComplex {
Expand All @@ -113,8 +106,7 @@ impl<'py> Div for &'py PyComplex {
}
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(PyPy))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
impl<'py> Neg for &'py PyComplex {
type Output = &'py PyComplex;
fn neg(self) -> &'py PyComplex {
Expand All @@ -129,7 +121,7 @@ impl<'py> Neg for &'py PyComplex {
#[cfg(feature = "num-complex")]
mod complex_conversion {
use super::*;
use crate::{FromPyObject, PyAny, PyErr, PyObject, PyResult, ToPyObject};
use crate::{FromPyObject, PyErr, PyNativeType, PyObject, PyResult, ToPyObject};
use num_complex::Complex;

impl PyComplex {
Expand Down Expand Up @@ -161,7 +153,7 @@ mod complex_conversion {
}
}
}
#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
Expand All @@ -175,7 +167,7 @@ mod complex_conversion {
}
}
}
#[cfg(Py_LIMITED_API)]
#[cfg(any(Py_LIMITED_API, PyPy))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
Expand Down Expand Up @@ -239,7 +231,7 @@ mod test {
assert_approx_eq!(complex.imag(), 1.2);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_add() {
let gil = Python::acquire_gil();
Expand All @@ -251,7 +243,7 @@ mod test {
assert_approx_eq!(res.imag(), 3.8);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_sub() {
let gil = Python::acquire_gil();
Expand All @@ -263,7 +255,7 @@ mod test {
assert_approx_eq!(res.imag(), -1.4);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_mul() {
let gil = Python::acquire_gil();
Expand All @@ -275,7 +267,7 @@ mod test {
assert_approx_eq!(res.imag(), 9.0);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_div() {
let gil = Python::acquire_gil();
Expand All @@ -287,7 +279,7 @@ mod test {
assert_approx_eq!(res.imag(), -0.850_515_463_917_525_7);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_neg() {
let gil = Python::acquire_gil();
Expand All @@ -298,7 +290,7 @@ mod test {
assert_approx_eq!(res.imag(), -1.2);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_abs() {
let gil = Python::acquire_gil();
Expand All @@ -307,7 +299,7 @@ mod test {
assert_approx_eq!(val.abs(), 3.231_098_884_280_702_2);
}

#[cfg(not(Py_LIMITED_API))]
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_pow() {
let gil = Python::acquire_gil();
Expand Down
Loading