Experimental support for PyPy 3.6
Co-authored-by: Yuji Kanagawa <yuji.kngw.80s.revive@gmail.com>
This commit is contained in:
parent
3809e2b3e9
commit
e0376d24aa
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
10
build.rs
10
build.rs
|
@ -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),
|
||||
}
|
||||
} else {
|
||||
match version.implementation {
|
||||
PythonInterpreterKind::CPython => format!("python{}", ld_version),
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e -u -o pipefail
|
||||
|
||||
cargo test --features "${FEATURES:-} num-bigint num-complex"
|
||||
(cd pyo3-derive-backend; cargo test)
|
||||
|
||||
for example_dir in examples/*; do
|
||||
cd $example_dir
|
||||
tox -c "tox.ini" -e py
|
||||
cd -
|
||||
done
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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")]
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(windows, not(PyPy)))]
|
||||
pub static mut PyExc_WindowsError: *mut PyObject;
|
||||
|
||||
pub static mut PyExc_RecursionErrorInst: *mut PyObject;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -33,8 +33,7 @@ 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;
|
||||
|
@ -42,8 +41,7 @@ impl PyComplex {
|
|||
}
|
||||
}
|
||||
/// 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()
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>> {
|
||||
|
@ -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>> {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
|
|
@ -360,7 +360,9 @@ mod test {
|
|||
use crate::conversion::IntoPy;
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::types::dict::IntoPyDict;
|
||||
use crate::types::{PyDict, PyList, PyTuple};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::types::PyList;
|
||||
use crate::types::{PyDict, PyTuple};
|
||||
use crate::PyObject;
|
||||
use crate::Python;
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
|
@ -380,6 +382,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_from_sequence() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
@ -394,6 +397,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_from_sequence_err() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
|
|
@ -117,6 +117,7 @@ impl PyModule {
|
|||
/// Returns the module's filename.
|
||||
///
|
||||
/// May fail if the module does not have a `__file__` attribute.
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
pub fn filename(&self) -> PyResult<&str> {
|
||||
unsafe { self.str_from_ptr(ffi::PyModule_GetFilename(self.as_ptr())) }
|
||||
}
|
||||
|
|
253
src/types/num.rs
253
src/types/num.rs
|
@ -8,7 +8,7 @@ use crate::{
|
|||
};
|
||||
use std::convert::TryFrom;
|
||||
use std::i64;
|
||||
use std::os::raw::{c_int, c_long, c_uchar};
|
||||
use std::os::raw::c_long;
|
||||
|
||||
fn err_if_invalid_value<T: PartialEq>(
|
||||
py: Python,
|
||||
|
@ -46,61 +46,6 @@ macro_rules! int_fits_larger_int {
|
|||
};
|
||||
}
|
||||
|
||||
// manual implementation for 128bit integers
|
||||
#[cfg(target_endian = "little")]
|
||||
const IS_LITTLE_ENDIAN: c_int = 1;
|
||||
#[cfg(not(target_endian = "little"))]
|
||||
const IS_LITTLE_ENDIAN: c_int = 0;
|
||||
|
||||
// for 128bit Integers
|
||||
macro_rules! int_convert_128 {
|
||||
($rust_type: ty, $byte_size: expr, $is_signed: expr) => {
|
||||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
(*self).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let bytes = self.to_ne_bytes();
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const c_uchar,
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr_or_panic(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
unsafe {
|
||||
let num = ffi::PyNumber_Index(ob.as_ptr());
|
||||
if num.is_null() {
|
||||
return Err(PyErr::fetch(ob.py()));
|
||||
}
|
||||
let mut buffer = [0; $byte_size];
|
||||
let ok = ffi::_PyLong_AsByteArray(
|
||||
num as *mut ffi::PyLongObject,
|
||||
buffer.as_mut_ptr(),
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
if ok == -1 {
|
||||
Err(PyErr::fetch(ob.py()))
|
||||
} else {
|
||||
Ok(<$rust_type>::from_ne_bytes(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python `int` object.
|
||||
///
|
||||
/// You can usually avoid directly working with this type
|
||||
|
@ -216,17 +161,141 @@ int_convert_u64_or_i64!(
|
|||
ffi::PyLong_AsUnsignedLongLong
|
||||
);
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
int_convert_128!(i128, 16, 1);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
int_convert_128!(u128, 16, 0);
|
||||
// manual implementation for 128bit integers
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
mod int128_conversion {
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult,
|
||||
Python, ToPyObject,
|
||||
};
|
||||
use std::os::raw::{c_int, c_uchar};
|
||||
|
||||
#[cfg(all(feature = "num-bigint", not(Py_LIMITED_API)))]
|
||||
#[cfg(target_endian = "little")]
|
||||
const IS_LITTLE_ENDIAN: c_int = 1;
|
||||
#[cfg(not(target_endian = "little"))]
|
||||
const IS_LITTLE_ENDIAN: c_int = 0;
|
||||
|
||||
// for 128bit Integers
|
||||
macro_rules! int_convert_128 {
|
||||
($rust_type: ty, $byte_size: expr, $is_signed: expr) => {
|
||||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
(*self).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let bytes = self.to_ne_bytes();
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const c_uchar,
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr_or_panic(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
unsafe {
|
||||
let num = ffi::PyNumber_Index(ob.as_ptr());
|
||||
if num.is_null() {
|
||||
return Err(PyErr::fetch(ob.py()));
|
||||
}
|
||||
let mut buffer = [0; $byte_size];
|
||||
let ok = ffi::_PyLong_AsByteArray(
|
||||
num as *mut ffi::PyLongObject,
|
||||
buffer.as_mut_ptr(),
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
if ok == -1 {
|
||||
Err(PyErr::fetch(ob.py()))
|
||||
} else {
|
||||
Ok(<$rust_type>::from_ne_bytes(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_convert_128!(i128, 16, 1);
|
||||
int_convert_128!(u128, 16, 0);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Python, ToPyObject};
|
||||
|
||||
#[test]
|
||||
fn test_i128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<u64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_min() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MIN;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert!(obj.extract::<i64>(py).is_err());
|
||||
assert!(obj.extract::<u128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::u128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<i128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_overflow() {
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::c_uchar;
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
||||
unsafe {
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
overflow_bytes.as_ptr() as *const c_uchar,
|
||||
20,
|
||||
super::IS_LITTLE_ENDIAN,
|
||||
0,
|
||||
);
|
||||
let obj = PyObject::from_owned_ptr_or_panic(py, obj);
|
||||
let err = obj.extract::<u128>(py).unwrap_err();
|
||||
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "num-bigint", not(any(Py_LIMITED_API, PyPy))))]
|
||||
mod bigint_conversion {
|
||||
use super::*;
|
||||
use crate::{err, Py};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use std::os::raw::{c_int, c_uchar};
|
||||
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
unsafe fn extract(ob: &PyLong, buffer: &mut [c_uchar], is_signed: c_int) -> PyResult<()> {
|
||||
err::error_on_minusone(
|
||||
ob.py(),
|
||||
|
@ -497,64 +566,6 @@ mod test {
|
|||
assert!(obj.extract::<i64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_i128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<u64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_i128_min() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MIN;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert!(obj.extract::<i64>(py).is_err());
|
||||
assert!(obj.extract::<u128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_u128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::u128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<i128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_u128_overflow() {
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::c_uchar;
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
||||
unsafe {
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
overflow_bytes.as_ptr() as *const c_uchar,
|
||||
20,
|
||||
super::IS_LITTLE_ENDIAN,
|
||||
0,
|
||||
);
|
||||
let obj = PyObject::from_owned_ptr_or_panic(py, obj);
|
||||
let err = obj.extract::<u128>(py).unwrap_err();
|
||||
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test_common (
|
||||
($test_mod_name:ident, $t:ty) => (
|
||||
mod $test_mod_name {
|
||||
|
@ -605,8 +616,8 @@ mod test {
|
|||
test_common!(u64, u64);
|
||||
test_common!(isize, isize);
|
||||
test_common!(usize, usize);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
test_common!(i128, i128);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
test_common!(u128, u128);
|
||||
}
|
||||
|
|
|
@ -556,6 +556,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_seq_count() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
|
Loading…
Reference in New Issue