From 80179be2b0ee589dc6e10d90ba0093066ce0e31f Mon Sep 17 00:00:00 2001 From: ijl Date: Fri, 22 Mar 2019 13:07:41 +0000 Subject: [PATCH] Merge num3, num_common to num --- src/types/mod.rs | 9 +- src/types/{num_common.rs => num.rs} | 196 ++++++++++++++++++++++++++- src/types/num3.rs | 202 ---------------------------- 3 files changed, 198 insertions(+), 209 deletions(-) rename src/types/{num_common.rs => num.rs} (50%) delete mode 100644 src/types/num3.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 96a442ce49a..f6e8a843b06 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -15,8 +15,8 @@ pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; pub use self::list::PyList; pub use self::module::PyModule; -pub use self::num3::PyLong; -pub use self::num3::PyLong as PyInt; +pub use self::num::PyLong; +pub use self::num::PyLong as PyInt; pub use self::sequence::PySequence; pub use self::set::{PyFrozenSet, PySet}; pub use self::slice::{PySlice, PySliceIndices}; @@ -156,9 +156,6 @@ macro_rules! pyobject_native_type_convert( }; ); -#[macro_use] -mod num_common; - mod any; mod boolobject; mod bytearray; @@ -169,7 +166,7 @@ mod floatob; mod iterator; mod list; mod module; -mod num3; +mod num; mod sequence; mod set; mod slice; diff --git a/src/types/num_common.rs b/src/types/num.rs similarity index 50% rename from src/types/num_common.rs rename to src/types/num.rs index e0874553b25..164a8b1005e 100644 --- a/src/types/num_common.rs +++ b/src/types/num.rs @@ -1,8 +1,20 @@ -//! common macros for num2.rs and num3.rs +// Copyright (c) 2017-present PyO3 Project and Contributors +// +// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython use crate::err::{PyErr, PyResult}; +use crate::exceptions; +use crate::ffi; +use crate::instance::PyNativeType; +use crate::object::PyObject; +use crate::types::PyAny; +use crate::AsPyPointer; use crate::Python; +use crate::{FromPyObject, IntoPyObject, ToPyObject}; +use num_traits::cast::cast; +use std::i64; use std::os::raw::c_int; +use std::os::raw::{c_long, c_uchar}; pub(super) fn err_if_invalid_value( py: Python, @@ -102,6 +114,133 @@ pub(super) const IS_LITTLE_ENDIAN: c_int = 1; #[cfg(not(target_endian = "little"))] pub(super) const IS_LITTLE_ENDIAN: c_int = 0; +/// Represents a Python `int` object. +/// +/// You can usually avoid directly working with this type +/// by using [`ToPyObject`](trait.ToPyObject.html) +/// and [extract](struct.PyObject.html#method.extract) +/// with the primitive Rust integer types. +#[repr(transparent)] +pub struct PyLong(PyObject); + +pyobject_native_type!(PyLong, ffi::PyLong_Type, ffi::PyLong_Check); + +macro_rules! int_fits_c_long ( + ($rust_type:ty) => ( + impl ToPyObject for $rust_type { + #![cfg_attr(feature="cargo-clippy", allow(clippy::cast_lossless))] + fn to_object(&self, py: Python) -> PyObject { + unsafe { + PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(*self as c_long)) + } + } + } + impl IntoPyObject for $rust_type { + #![cfg_attr(feature="cargo-clippy", allow(clippy::cast_lossless))] + fn into_object(self, py: Python) -> PyObject { + unsafe { + PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(self as c_long)) + } + } + } + + impl<'source> FromPyObject<'source> for $rust_type { + fn extract(obj: &'source PyAny) -> PyResult { + let ptr = obj.as_ptr(); + let val = unsafe { + let num = ffi::PyNumber_Index(ptr); + if num.is_null() { + Err(PyErr::fetch(obj.py())) + } else { + let val = err_if_invalid_value(obj.py(), -1, ffi::PyLong_AsLong(num)); + ffi::Py_DECREF(num); + val + } + }?; + match cast::(val) { + Some(v) => Ok(v), + None => Err(exceptions::OverflowError.into()) + } + } + } + ) +); + +macro_rules! int_convert_u64_or_i64 ( + ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ll_or_ull:expr) => ( + impl ToPyObject for $rust_type { + #[inline] + fn to_object(&self, py: Python) -> PyObject { + unsafe { + PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self)) + } + } + } + impl IntoPyObject for $rust_type { + #[inline] + fn into_object(self, py: Python) -> PyObject { + unsafe { + PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(self)) + } + } + } + impl<'source> FromPyObject<'source> for $rust_type { + fn extract(ob: &'source PyAny) -> PyResult<$rust_type> + { + let ptr = ob.as_ptr(); + unsafe { + let num = ffi::PyNumber_Index(ptr); + if num.is_null() { + Err(PyErr::fetch(ob.py())) + } else { + let result = err_if_invalid_value(ob.py(), !0, $pylong_as_ll_or_ull(num)); + ffi::Py_DECREF(num); + result + } + } + } + } + ) +); + +int_fits_c_long!(i8); +int_fits_c_long!(u8); +int_fits_c_long!(i16); +int_fits_c_long!(u16); +int_fits_c_long!(i32); + +// If c_long is 64-bits, we can use more types with int_fits_c_long!: +#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] +int_fits_c_long!(u32); +#[cfg(any(target_pointer_width = "32", target_os = "windows"))] +int_fits_larger_int!(u32, u64); + +#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] +int_fits_c_long!(i64); + +// manual implementation for i64 on systems with 32-bit long +#[cfg(any(target_pointer_width = "32", target_os = "windows"))] +int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong); + +#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] +int_fits_c_long!(isize); +#[cfg(any(target_pointer_width = "32", target_os = "windows"))] +int_fits_larger_int!(isize, i64); + +int_fits_larger_int!(usize, u64); + +// u64 has a manual implementation as it never fits into signed long +int_convert_u64_or_i64!( + u64, + ffi::PyLong_FromUnsignedLongLong, + ffi::PyLong_AsUnsignedLongLong +); + +#[cfg(not(Py_LIMITED_API))] +int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); +#[cfg(not(Py_LIMITED_API))] +int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); + #[cfg(test)] mod test { use crate::Python; @@ -207,4 +346,59 @@ mod test { assert!(err.is_instance::(py)); } } + + macro_rules! test_common ( + ($test_mod_name:ident, $t:ty) => ( + mod $test_mod_name { + use crate::exceptions; + use crate::ToPyObject; + use crate::Python; + + #[test] + fn from_py_string_type_error() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = ("123").to_object(py); + let err = obj.extract::<$t>(py).unwrap_err(); + assert!(err.is_instance::(py)); + } + + #[test] + fn from_py_float_type_error() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = (12.3).to_object(py); + let err = obj.extract::<$t>(py).unwrap_err(); + assert!(err.is_instance::(py)); + } + + #[test] + fn to_py_object_and_back() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let val = 123 as $t; + let obj = val.to_object(py); + assert_eq!(obj.extract::<$t>(py).unwrap(), val as $t); + } + } + ) + ); + + test_common!(i8, i8); + test_common!(u8, u8); + test_common!(i16, i16); + test_common!(u16, u16); + test_common!(i32, i32); + test_common!(u32, u32); + test_common!(i64, i64); + test_common!(u64, u64); + test_common!(isize, isize); + test_common!(usize, usize); + #[cfg(not(Py_LIMITED_API))] + test_common!(i128, i128); + #[cfg(not(Py_LIMITED_API))] + test_common!(u128, u128); } diff --git a/src/types/num3.rs b/src/types/num3.rs deleted file mode 100644 index 0bbd9b931a4..00000000000 --- a/src/types/num3.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2017-present PyO3 Project and Contributors -// -// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython - -use super::num_common::{err_if_invalid_value, IS_LITTLE_ENDIAN}; -use crate::err::{PyErr, PyResult}; -use crate::exceptions; -use crate::ffi; -use crate::instance::PyNativeType; -use crate::object::PyObject; -use crate::types::PyAny; -use crate::AsPyPointer; -use crate::Python; -use crate::{FromPyObject, IntoPyObject, ToPyObject}; -use num_traits::cast::cast; -use std::i64; -use std::os::raw::{c_long, c_uchar}; - -/// Represents a Python `int` object. -/// -/// You can usually avoid directly working with this type -/// by using [`ToPyObject`](trait.ToPyObject.html) -/// and [extract](struct.PyObject.html#method.extract) -/// with the primitive Rust integer types. -#[repr(transparent)] -pub struct PyLong(PyObject); - -pyobject_native_type!(PyLong, ffi::PyLong_Type, ffi::PyLong_Check); - -macro_rules! int_fits_c_long ( - ($rust_type:ty) => ( - impl ToPyObject for $rust_type { - #![cfg_attr(feature="cargo-clippy", allow(clippy::cast_lossless))] - fn to_object(&self, py: Python) -> PyObject { - unsafe { - PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(*self as c_long)) - } - } - } - impl IntoPyObject for $rust_type { - #![cfg_attr(feature="cargo-clippy", allow(clippy::cast_lossless))] - fn into_object(self, py: Python) -> PyObject { - unsafe { - PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(self as c_long)) - } - } - } - - impl<'source> FromPyObject<'source> for $rust_type { - fn extract(obj: &'source PyAny) -> PyResult { - let ptr = obj.as_ptr(); - let val = unsafe { - let num = ffi::PyNumber_Index(ptr); - if num.is_null() { - Err(PyErr::fetch(obj.py())) - } else { - let val = err_if_invalid_value(obj.py(), -1, ffi::PyLong_AsLong(num)); - ffi::Py_DECREF(num); - val - } - }?; - match cast::(val) { - Some(v) => Ok(v), - None => Err(exceptions::OverflowError.into()) - } - } - } - ) -); - -macro_rules! int_convert_u64_or_i64 ( - ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ll_or_ull:expr) => ( - impl ToPyObject for $rust_type { - #[inline] - fn to_object(&self, py: Python) -> PyObject { - unsafe { - PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self)) - } - } - } - impl IntoPyObject for $rust_type { - #[inline] - fn into_object(self, py: Python) -> PyObject { - unsafe { - PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(self)) - } - } - } - impl<'source> FromPyObject<'source> for $rust_type { - fn extract(ob: &'source PyAny) -> PyResult<$rust_type> - { - let ptr = ob.as_ptr(); - unsafe { - let num = ffi::PyNumber_Index(ptr); - if num.is_null() { - Err(PyErr::fetch(ob.py())) - } else { - let result = err_if_invalid_value(ob.py(), !0, $pylong_as_ll_or_ull(num)); - ffi::Py_DECREF(num); - result - } - } - } - } - ) -); - -int_fits_c_long!(i8); -int_fits_c_long!(u8); -int_fits_c_long!(i16); -int_fits_c_long!(u16); -int_fits_c_long!(i32); - -// If c_long is 64-bits, we can use more types with int_fits_c_long!: -#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] -int_fits_c_long!(u32); -#[cfg(any(target_pointer_width = "32", target_os = "windows"))] -int_fits_larger_int!(u32, u64); - -#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] -int_fits_c_long!(i64); - -// manual implementation for i64 on systems with 32-bit long -#[cfg(any(target_pointer_width = "32", target_os = "windows"))] -int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong); - -#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))] -int_fits_c_long!(isize); -#[cfg(any(target_pointer_width = "32", target_os = "windows"))] -int_fits_larger_int!(isize, i64); - -int_fits_larger_int!(usize, u64); - -// u64 has a manual implementation as it never fits into signed long -int_convert_u64_or_i64!( - u64, - ffi::PyLong_FromUnsignedLongLong, - ffi::PyLong_AsUnsignedLongLong -); - -#[cfg(not(Py_LIMITED_API))] -int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); -#[cfg(not(Py_LIMITED_API))] -int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); - -#[cfg(test)] -mod test { - macro_rules! test_common ( - ($test_mod_name:ident, $t:ty) => ( - mod $test_mod_name { - use crate::exceptions; - use crate::ToPyObject; - use crate::Python; - - #[test] - fn from_py_string_type_error() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = ("123").to_object(py); - let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py)); - } - - #[test] - fn from_py_float_type_error() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = (12.3).to_object(py); - let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py)); - } - - #[test] - fn to_py_object_and_back() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let val = 123 as $t; - let obj = val.to_object(py); - assert_eq!(obj.extract::<$t>(py).unwrap(), val as $t); - } - } - ) - ); - - test_common!(i8, i8); - test_common!(u8, u8); - test_common!(i16, i16); - test_common!(u16, u16); - test_common!(i32, i32); - test_common!(u32, u32); - test_common!(i64, i64); - test_common!(u64, u64); - test_common!(isize, isize); - test_common!(usize, usize); - #[cfg(not(Py_LIMITED_API))] - test_common!(i128, i128); - #[cfg(not(Py_LIMITED_API))] - test_common!(u128, u128); -}