Experimental support for PyPy 3.6

Co-authored-by: Yuji Kanagawa <yuji.kngw.80s.revive@gmail.com>
This commit is contained in:
David Hewitt 2020-08-07 01:50:11 +01:00
parent 3809e2b3e9
commit e0376d24aa
18 changed files with 215 additions and 179 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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),
@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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")]

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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();

View File

@ -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();

View File

@ -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())) }
}

View File

@ -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);
}

View File

@ -556,6 +556,7 @@ mod test {
}
#[test]
#[cfg(not(PyPy))]
fn test_seq_count() {
let gil = Python::acquire_gil();
let py = gil.python();