pyo3/conversions/std/
num.rs

1use crate::ffi_ptr_ext::FfiPtrExt;
2#[cfg(feature = "experimental-inspect")]
3use crate::inspect::types::TypeInfo;
4use crate::types::any::PyAnyMethods;
5use crate::{
6    exceptions, ffi, Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python,
7    ToPyObject,
8};
9use std::num::{
10    NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
11    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
12};
13use std::os::raw::c_long;
14
15macro_rules! int_fits_larger_int {
16    ($rust_type:ty, $larger_type:ty) => {
17        impl ToPyObject for $rust_type {
18            #[inline]
19            fn to_object(&self, py: Python<'_>) -> PyObject {
20                (*self as $larger_type).into_py(py)
21            }
22        }
23        impl IntoPy<PyObject> for $rust_type {
24            fn into_py(self, py: Python<'_>) -> PyObject {
25                (self as $larger_type).into_py(py)
26            }
27
28            #[cfg(feature = "experimental-inspect")]
29            fn type_output() -> TypeInfo {
30                <$larger_type>::type_output()
31            }
32        }
33
34        impl FromPyObject<'_> for $rust_type {
35            fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
36                let val: $larger_type = obj.extract()?;
37                <$rust_type>::try_from(val)
38                    .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
39            }
40
41            #[cfg(feature = "experimental-inspect")]
42            fn type_input() -> TypeInfo {
43                <$larger_type>::type_input()
44            }
45        }
46    };
47}
48
49macro_rules! extract_int {
50    ($obj:ident, $error_val:expr, $pylong_as:expr) => {
51        extract_int!($obj, $error_val, $pylong_as, false)
52    };
53
54    ($obj:ident, $error_val:expr, $pylong_as:expr, $force_index_call: literal) => {
55        // In python 3.8+ `PyLong_AsLong` and friends takes care of calling `PyNumber_Index`,
56        // however 3.8 & 3.9 do lossy conversion of floats, hence we only use the
57        // simplest logic for 3.10+ where that was fixed - python/cpython#82180.
58        // `PyLong_AsUnsignedLongLong` does not call `PyNumber_Index`, hence the `force_index_call` argument
59        // See https://github.com/PyO3/pyo3/pull/3742 for detials
60        if cfg!(Py_3_10) && !$force_index_call {
61            err_if_invalid_value($obj.py(), $error_val, unsafe { $pylong_as($obj.as_ptr()) })
62        } else if let Ok(long) = $obj.downcast::<crate::types::PyLong>() {
63            // fast path - checking for subclass of `int` just checks a bit in the type $object
64            err_if_invalid_value($obj.py(), $error_val, unsafe { $pylong_as(long.as_ptr()) })
65        } else {
66            unsafe {
67                let num = ffi::PyNumber_Index($obj.as_ptr()).assume_owned_or_err($obj.py())?;
68                err_if_invalid_value($obj.py(), $error_val, $pylong_as(num.as_ptr()))
69            }
70        }
71    };
72}
73
74macro_rules! int_convert_u64_or_i64 {
75    ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ll_or_ull:expr, $force_index_call:literal) => {
76        impl ToPyObject for $rust_type {
77            #[inline]
78            fn to_object(&self, py: Python<'_>) -> PyObject {
79                unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(*self)) }
80            }
81        }
82        impl IntoPy<PyObject> for $rust_type {
83            #[inline]
84            fn into_py(self, py: Python<'_>) -> PyObject {
85                unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) }
86            }
87
88            #[cfg(feature = "experimental-inspect")]
89            fn type_output() -> TypeInfo {
90                TypeInfo::builtin("int")
91            }
92        }
93        impl FromPyObject<'_> for $rust_type {
94            fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
95                extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call)
96            }
97
98            #[cfg(feature = "experimental-inspect")]
99            fn type_input() -> TypeInfo {
100                Self::type_output()
101            }
102        }
103    };
104}
105
106macro_rules! int_fits_c_long {
107    ($rust_type:ty) => {
108        impl ToPyObject for $rust_type {
109            fn to_object(&self, py: Python<'_>) -> PyObject {
110                unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) }
111            }
112        }
113        impl IntoPy<PyObject> for $rust_type {
114            fn into_py(self, py: Python<'_>) -> PyObject {
115                unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) }
116            }
117
118            #[cfg(feature = "experimental-inspect")]
119            fn type_output() -> TypeInfo {
120                TypeInfo::builtin("int")
121            }
122        }
123
124        impl<'py> FromPyObject<'py> for $rust_type {
125            fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
126                let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?;
127                <$rust_type>::try_from(val)
128                    .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
129            }
130
131            #[cfg(feature = "experimental-inspect")]
132            fn type_input() -> TypeInfo {
133                Self::type_output()
134            }
135        }
136    };
137}
138
139int_fits_c_long!(i8);
140int_fits_c_long!(u8);
141int_fits_c_long!(i16);
142int_fits_c_long!(u16);
143int_fits_c_long!(i32);
144
145// If c_long is 64-bits, we can use more types with int_fits_c_long!:
146#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
147int_fits_c_long!(u32);
148#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
149int_fits_larger_int!(u32, u64);
150
151#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
152int_fits_c_long!(i64);
153
154// manual implementation for i64 on systems with 32-bit long
155#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
156int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong, false);
157
158#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
159int_fits_c_long!(isize);
160#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
161int_fits_larger_int!(isize, i64);
162
163int_fits_larger_int!(usize, u64);
164
165// u64 has a manual implementation as it never fits into signed long
166int_convert_u64_or_i64!(
167    u64,
168    ffi::PyLong_FromUnsignedLongLong,
169    ffi::PyLong_AsUnsignedLongLong,
170    true
171);
172
173#[cfg(all(not(Py_LIMITED_API), not(GraalPy)))]
174mod fast_128bit_int_conversion {
175    use super::*;
176
177    // for 128bit Integers
178    macro_rules! int_convert_128 {
179        ($rust_type: ty, $is_signed: literal) => {
180            impl ToPyObject for $rust_type {
181                #[inline]
182                fn to_object(&self, py: Python<'_>) -> PyObject {
183                    (*self).into_py(py)
184                }
185            }
186            impl IntoPy<PyObject> for $rust_type {
187                fn into_py(self, py: Python<'_>) -> PyObject {
188                    #[cfg(not(Py_3_13))]
189                    {
190                        let bytes = self.to_le_bytes();
191                        unsafe {
192                            ffi::_PyLong_FromByteArray(
193                                bytes.as_ptr().cast(),
194                                bytes.len(),
195                                1,
196                                $is_signed.into(),
197                            )
198                            .assume_owned(py)
199                            .unbind()
200                        }
201                    }
202                    #[cfg(Py_3_13)]
203                    {
204                        let bytes = self.to_ne_bytes();
205
206                        if $is_signed {
207                            unsafe {
208                                ffi::PyLong_FromNativeBytes(
209                                    bytes.as_ptr().cast(),
210                                    bytes.len(),
211                                    ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN,
212                                )
213                                .assume_owned(py)
214                            }
215                        } else {
216                            unsafe {
217                                ffi::PyLong_FromUnsignedNativeBytes(
218                                    bytes.as_ptr().cast(),
219                                    bytes.len(),
220                                    ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN,
221                                )
222                                .assume_owned(py)
223                            }
224                        }
225                        .unbind()
226                    }
227                }
228
229                #[cfg(feature = "experimental-inspect")]
230                fn type_output() -> TypeInfo {
231                    TypeInfo::builtin("int")
232                }
233            }
234
235            impl FromPyObject<'_> for $rust_type {
236                fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
237                    let num =
238                        unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? };
239                    let mut buffer = [0u8; std::mem::size_of::<$rust_type>()];
240                    #[cfg(not(Py_3_13))]
241                    {
242                        crate::err::error_on_minusone(ob.py(), unsafe {
243                            ffi::_PyLong_AsByteArray(
244                                num.as_ptr() as *mut ffi::PyLongObject,
245                                buffer.as_mut_ptr(),
246                                buffer.len(),
247                                1,
248                                $is_signed.into(),
249                            )
250                        })?;
251                        Ok(<$rust_type>::from_le_bytes(buffer))
252                    }
253                    #[cfg(Py_3_13)]
254                    {
255                        let mut flags = ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN;
256                        if !$is_signed {
257                            flags |= ffi::Py_ASNATIVEBYTES_UNSIGNED_BUFFER
258                                | ffi::Py_ASNATIVEBYTES_REJECT_NEGATIVE;
259                        }
260                        let actual_size: usize = unsafe {
261                            ffi::PyLong_AsNativeBytes(
262                                num.as_ptr(),
263                                buffer.as_mut_ptr().cast(),
264                                buffer
265                                    .len()
266                                    .try_into()
267                                    .expect("length of buffer fits in Py_ssize_t"),
268                                flags,
269                            )
270                        }
271                        .try_into()
272                        .map_err(|_| PyErr::fetch(ob.py()))?;
273                        if actual_size as usize > buffer.len() {
274                            return Err(crate::exceptions::PyOverflowError::new_err(
275                                "Python int larger than 128 bits",
276                            ));
277                        }
278                        Ok(<$rust_type>::from_ne_bytes(buffer))
279                    }
280                }
281
282                #[cfg(feature = "experimental-inspect")]
283                fn type_input() -> TypeInfo {
284                    Self::type_output()
285                }
286            }
287        };
288    }
289
290    int_convert_128!(i128, true);
291    int_convert_128!(u128, false);
292}
293
294// For ABI3 we implement the conversion manually.
295#[cfg(any(Py_LIMITED_API, GraalPy))]
296mod slow_128bit_int_conversion {
297    use super::*;
298    const SHIFT: usize = 64;
299
300    // for 128bit Integers
301    macro_rules! int_convert_128 {
302        ($rust_type: ty, $half_type: ty) => {
303            impl ToPyObject for $rust_type {
304                #[inline]
305                fn to_object(&self, py: Python<'_>) -> PyObject {
306                    (*self).into_py(py)
307                }
308            }
309
310            impl IntoPy<PyObject> for $rust_type {
311                fn into_py(self, py: Python<'_>) -> PyObject {
312                    let lower = (self as u64).into_py(py);
313                    let upper = ((self >> SHIFT) as $half_type).into_py(py);
314                    let shift = SHIFT.into_py(py);
315                    unsafe {
316                        let shifted = PyObject::from_owned_ptr(
317                            py,
318                            ffi::PyNumber_Lshift(upper.as_ptr(), shift.as_ptr()),
319                        );
320                        PyObject::from_owned_ptr(
321                            py,
322                            ffi::PyNumber_Or(shifted.as_ptr(), lower.as_ptr()),
323                        )
324                    }
325                }
326
327                #[cfg(feature = "experimental-inspect")]
328                fn type_output() -> TypeInfo {
329                    TypeInfo::builtin("int")
330                }
331            }
332
333            impl FromPyObject<'_> for $rust_type {
334                fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
335                    let py = ob.py();
336                    unsafe {
337                        let lower = err_if_invalid_value(
338                            py,
339                            -1 as _,
340                            ffi::PyLong_AsUnsignedLongLongMask(ob.as_ptr()),
341                        )? as $rust_type;
342                        let shift = SHIFT.into_py(py);
343                        let shifted = PyObject::from_owned_ptr_or_err(
344                            py,
345                            ffi::PyNumber_Rshift(ob.as_ptr(), shift.as_ptr()),
346                        )?;
347                        let upper: $half_type = shifted.extract(py)?;
348                        Ok((<$rust_type>::from(upper) << SHIFT) | lower)
349                    }
350                }
351
352                #[cfg(feature = "experimental-inspect")]
353                fn type_input() -> TypeInfo {
354                    Self::type_output()
355                }
356            }
357        };
358    }
359
360    int_convert_128!(i128, i64);
361    int_convert_128!(u128, u64);
362}
363
364fn err_if_invalid_value<T: PartialEq>(
365    py: Python<'_>,
366    invalid_value: T,
367    actual_value: T,
368) -> PyResult<T> {
369    if actual_value == invalid_value {
370        if let Some(err) = PyErr::take(py) {
371            return Err(err);
372        }
373    }
374
375    Ok(actual_value)
376}
377
378macro_rules! nonzero_int_impl {
379    ($nonzero_type:ty, $primitive_type:ty) => {
380        impl ToPyObject for $nonzero_type {
381            fn to_object(&self, py: Python<'_>) -> PyObject {
382                self.get().to_object(py)
383            }
384        }
385
386        impl IntoPy<PyObject> for $nonzero_type {
387            fn into_py(self, py: Python<'_>) -> PyObject {
388                self.get().into_py(py)
389            }
390        }
391
392        impl FromPyObject<'_> for $nonzero_type {
393            fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
394                let val: $primitive_type = obj.extract()?;
395                <$nonzero_type>::try_from(val)
396                    .map_err(|_| exceptions::PyValueError::new_err("invalid zero value"))
397            }
398
399            #[cfg(feature = "experimental-inspect")]
400            fn type_input() -> TypeInfo {
401                <$primitive_type>::type_input()
402            }
403        }
404    };
405}
406
407nonzero_int_impl!(NonZeroI8, i8);
408nonzero_int_impl!(NonZeroI16, i16);
409nonzero_int_impl!(NonZeroI32, i32);
410nonzero_int_impl!(NonZeroI64, i64);
411nonzero_int_impl!(NonZeroI128, i128);
412nonzero_int_impl!(NonZeroIsize, isize);
413nonzero_int_impl!(NonZeroU8, u8);
414nonzero_int_impl!(NonZeroU16, u16);
415nonzero_int_impl!(NonZeroU32, u32);
416nonzero_int_impl!(NonZeroU64, u64);
417nonzero_int_impl!(NonZeroU128, u128);
418nonzero_int_impl!(NonZeroUsize, usize);
419
420#[cfg(test)]
421mod test_128bit_integers {
422    use super::*;
423
424    #[cfg(not(target_arch = "wasm32"))]
425    use crate::types::PyDict;
426
427    #[cfg(not(target_arch = "wasm32"))]
428    use crate::types::dict::PyDictMethods;
429
430    #[cfg(not(target_arch = "wasm32"))]
431    use proptest::prelude::*;
432
433    #[cfg(not(target_arch = "wasm32"))]
434    proptest! {
435        #[test]
436        fn test_i128_roundtrip(x: i128) {
437            Python::with_gil(|py| {
438                let x_py = x.into_py(py);
439                let locals = PyDict::new_bound(py);
440                locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
441                py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
442                let roundtripped: i128 = x_py.extract(py).unwrap();
443                assert_eq!(x, roundtripped);
444            })
445        }
446
447        #[test]
448        fn test_nonzero_i128_roundtrip(
449            x in any::<i128>()
450                .prop_filter("Values must not be 0", |x| x != &0)
451                .prop_map(|x| NonZeroI128::new(x).unwrap())
452        ) {
453            Python::with_gil(|py| {
454                let x_py = x.into_py(py);
455                let locals = PyDict::new_bound(py);
456                locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
457                py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
458                let roundtripped: NonZeroI128 = x_py.extract(py).unwrap();
459                assert_eq!(x, roundtripped);
460            })
461        }
462    }
463
464    #[cfg(not(target_arch = "wasm32"))]
465    proptest! {
466        #[test]
467        fn test_u128_roundtrip(x: u128) {
468            Python::with_gil(|py| {
469                let x_py = x.into_py(py);
470                let locals = PyDict::new_bound(py);
471                locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
472                py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
473                let roundtripped: u128 = x_py.extract(py).unwrap();
474                assert_eq!(x, roundtripped);
475            })
476        }
477
478        #[test]
479        fn test_nonzero_u128_roundtrip(
480            x in any::<u128>()
481                .prop_filter("Values must not be 0", |x| x != &0)
482                .prop_map(|x| NonZeroU128::new(x).unwrap())
483        ) {
484            Python::with_gil(|py| {
485                let x_py = x.into_py(py);
486                let locals = PyDict::new_bound(py);
487                locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
488                py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
489                let roundtripped: NonZeroU128 = x_py.extract(py).unwrap();
490                assert_eq!(x, roundtripped);
491            })
492        }
493    }
494
495    #[test]
496    fn test_i128_max() {
497        Python::with_gil(|py| {
498            let v = i128::MAX;
499            let obj = v.to_object(py);
500            assert_eq!(v, obj.extract::<i128>(py).unwrap());
501            assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
502            assert!(obj.extract::<u64>(py).is_err());
503        })
504    }
505
506    #[test]
507    fn test_i128_min() {
508        Python::with_gil(|py| {
509            let v = i128::MIN;
510            let obj = v.to_object(py);
511            assert_eq!(v, obj.extract::<i128>(py).unwrap());
512            assert!(obj.extract::<i64>(py).is_err());
513            assert!(obj.extract::<u128>(py).is_err());
514        })
515    }
516
517    #[test]
518    fn test_u128_max() {
519        Python::with_gil(|py| {
520            let v = u128::MAX;
521            let obj = v.to_object(py);
522            assert_eq!(v, obj.extract::<u128>(py).unwrap());
523            assert!(obj.extract::<i128>(py).is_err());
524        })
525    }
526
527    #[test]
528    fn test_i128_overflow() {
529        Python::with_gil(|py| {
530            let obj = py.eval_bound("(1 << 130) * -1", None, None).unwrap();
531            let err = obj.extract::<i128>().unwrap_err();
532            assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
533        })
534    }
535
536    #[test]
537    fn test_u128_overflow() {
538        Python::with_gil(|py| {
539            let obj = py.eval_bound("1 << 130", None, None).unwrap();
540            let err = obj.extract::<u128>().unwrap_err();
541            assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
542        })
543    }
544
545    #[test]
546    fn test_nonzero_i128_max() {
547        Python::with_gil(|py| {
548            let v = NonZeroI128::new(i128::MAX).unwrap();
549            let obj = v.to_object(py);
550            assert_eq!(v, obj.extract::<NonZeroI128>(py).unwrap());
551            assert_eq!(
552                NonZeroU128::new(v.get() as u128).unwrap(),
553                obj.extract::<NonZeroU128>(py).unwrap()
554            );
555            assert!(obj.extract::<NonZeroU64>(py).is_err());
556        })
557    }
558
559    #[test]
560    fn test_nonzero_i128_min() {
561        Python::with_gil(|py| {
562            let v = NonZeroI128::new(i128::MIN).unwrap();
563            let obj = v.to_object(py);
564            assert_eq!(v, obj.extract::<NonZeroI128>(py).unwrap());
565            assert!(obj.extract::<NonZeroI64>(py).is_err());
566            assert!(obj.extract::<NonZeroU128>(py).is_err());
567        })
568    }
569
570    #[test]
571    fn test_nonzero_u128_max() {
572        Python::with_gil(|py| {
573            let v = NonZeroU128::new(u128::MAX).unwrap();
574            let obj = v.to_object(py);
575            assert_eq!(v, obj.extract::<NonZeroU128>(py).unwrap());
576            assert!(obj.extract::<NonZeroI128>(py).is_err());
577        })
578    }
579
580    #[test]
581    fn test_nonzero_i128_overflow() {
582        Python::with_gil(|py| {
583            let obj = py.eval_bound("(1 << 130) * -1", None, None).unwrap();
584            let err = obj.extract::<NonZeroI128>().unwrap_err();
585            assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
586        })
587    }
588
589    #[test]
590    fn test_nonzero_u128_overflow() {
591        Python::with_gil(|py| {
592            let obj = py.eval_bound("1 << 130", None, None).unwrap();
593            let err = obj.extract::<NonZeroU128>().unwrap_err();
594            assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
595        })
596    }
597
598    #[test]
599    fn test_nonzero_i128_zero_value() {
600        Python::with_gil(|py| {
601            let obj = py.eval_bound("0", None, None).unwrap();
602            let err = obj.extract::<NonZeroI128>().unwrap_err();
603            assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
604        })
605    }
606
607    #[test]
608    fn test_nonzero_u128_zero_value() {
609        Python::with_gil(|py| {
610            let obj = py.eval_bound("0", None, None).unwrap();
611            let err = obj.extract::<NonZeroU128>().unwrap_err();
612            assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
613        })
614    }
615}
616
617#[cfg(test)]
618mod tests {
619    use crate::Python;
620    use crate::ToPyObject;
621    use std::num::*;
622
623    #[test]
624    fn test_u32_max() {
625        Python::with_gil(|py| {
626            let v = u32::MAX;
627            let obj = v.to_object(py);
628            assert_eq!(v, obj.extract::<u32>(py).unwrap());
629            assert_eq!(u64::from(v), obj.extract::<u64>(py).unwrap());
630            assert!(obj.extract::<i32>(py).is_err());
631        });
632    }
633
634    #[test]
635    fn test_i64_max() {
636        Python::with_gil(|py| {
637            let v = i64::MAX;
638            let obj = v.to_object(py);
639            assert_eq!(v, obj.extract::<i64>(py).unwrap());
640            assert_eq!(v as u64, obj.extract::<u64>(py).unwrap());
641            assert!(obj.extract::<u32>(py).is_err());
642        });
643    }
644
645    #[test]
646    fn test_i64_min() {
647        Python::with_gil(|py| {
648            let v = i64::MIN;
649            let obj = v.to_object(py);
650            assert_eq!(v, obj.extract::<i64>(py).unwrap());
651            assert!(obj.extract::<i32>(py).is_err());
652            assert!(obj.extract::<u64>(py).is_err());
653        });
654    }
655
656    #[test]
657    fn test_u64_max() {
658        Python::with_gil(|py| {
659            let v = u64::MAX;
660            let obj = v.to_object(py);
661            assert_eq!(v, obj.extract::<u64>(py).unwrap());
662            assert!(obj.extract::<i64>(py).is_err());
663        });
664    }
665
666    macro_rules! test_common (
667        ($test_mod_name:ident, $t:ty) => (
668            mod $test_mod_name {
669                use crate::exceptions;
670                use crate::ToPyObject;
671                use crate::Python;
672
673                #[test]
674                fn from_py_string_type_error() {
675                    Python::with_gil(|py| {
676                    let obj = ("123").to_object(py);
677                    let err = obj.extract::<$t>(py).unwrap_err();
678                    assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
679                    });
680                }
681
682                #[test]
683                fn from_py_float_type_error() {
684                    Python::with_gil(|py| {
685                    let obj = (12.3).to_object(py);
686                    let err = obj.extract::<$t>(py).unwrap_err();
687                    assert!(err.is_instance_of::<exceptions::PyTypeError>(py));});
688                }
689
690                #[test]
691                fn to_py_object_and_back() {
692                    Python::with_gil(|py| {
693                    let val = 123 as $t;
694                    let obj = val.to_object(py);
695                    assert_eq!(obj.extract::<$t>(py).unwrap(), val as $t);});
696                }
697            }
698        )
699    );
700
701    test_common!(i8, i8);
702    test_common!(u8, u8);
703    test_common!(i16, i16);
704    test_common!(u16, u16);
705    test_common!(i32, i32);
706    test_common!(u32, u32);
707    test_common!(i64, i64);
708    test_common!(u64, u64);
709    test_common!(isize, isize);
710    test_common!(usize, usize);
711    test_common!(i128, i128);
712    test_common!(u128, u128);
713
714    #[test]
715    fn test_nonzero_u32_max() {
716        Python::with_gil(|py| {
717            let v = NonZeroU32::new(u32::MAX).unwrap();
718            let obj = v.to_object(py);
719            assert_eq!(v, obj.extract::<NonZeroU32>(py).unwrap());
720            assert_eq!(NonZeroU64::from(v), obj.extract::<NonZeroU64>(py).unwrap());
721            assert!(obj.extract::<NonZeroI32>(py).is_err());
722        });
723    }
724
725    #[test]
726    fn test_nonzero_i64_max() {
727        Python::with_gil(|py| {
728            let v = NonZeroI64::new(i64::MAX).unwrap();
729            let obj = v.to_object(py);
730            assert_eq!(v, obj.extract::<NonZeroI64>(py).unwrap());
731            assert_eq!(
732                NonZeroU64::new(v.get() as u64).unwrap(),
733                obj.extract::<NonZeroU64>(py).unwrap()
734            );
735            assert!(obj.extract::<NonZeroU32>(py).is_err());
736        });
737    }
738
739    #[test]
740    fn test_nonzero_i64_min() {
741        Python::with_gil(|py| {
742            let v = NonZeroI64::new(i64::MIN).unwrap();
743            let obj = v.to_object(py);
744            assert_eq!(v, obj.extract::<NonZeroI64>(py).unwrap());
745            assert!(obj.extract::<NonZeroI32>(py).is_err());
746            assert!(obj.extract::<NonZeroU64>(py).is_err());
747        });
748    }
749
750    #[test]
751    fn test_nonzero_u64_max() {
752        Python::with_gil(|py| {
753            let v = NonZeroU64::new(u64::MAX).unwrap();
754            let obj = v.to_object(py);
755            assert_eq!(v, obj.extract::<NonZeroU64>(py).unwrap());
756            assert!(obj.extract::<NonZeroI64>(py).is_err());
757        });
758    }
759
760    macro_rules! test_nonzero_common (
761        ($test_mod_name:ident, $t:ty) => (
762            mod $test_mod_name {
763                use crate::exceptions;
764                use crate::ToPyObject;
765                use crate::Python;
766                use std::num::*;
767
768                #[test]
769                fn from_py_string_type_error() {
770                    Python::with_gil(|py| {
771                    let obj = ("123").to_object(py);
772                    let err = obj.extract::<$t>(py).unwrap_err();
773                    assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
774                    });
775                }
776
777                #[test]
778                fn from_py_float_type_error() {
779                    Python::with_gil(|py| {
780                    let obj = (12.3).to_object(py);
781                    let err = obj.extract::<$t>(py).unwrap_err();
782                    assert!(err.is_instance_of::<exceptions::PyTypeError>(py));});
783                }
784
785                #[test]
786                fn to_py_object_and_back() {
787                    Python::with_gil(|py| {
788                    let val = <$t>::new(123).unwrap();
789                    let obj = val.to_object(py);
790                    assert_eq!(obj.extract::<$t>(py).unwrap(), val);});
791                }
792            }
793        )
794    );
795
796    test_nonzero_common!(nonzero_i8, NonZeroI8);
797    test_nonzero_common!(nonzero_u8, NonZeroU8);
798    test_nonzero_common!(nonzero_i16, NonZeroI16);
799    test_nonzero_common!(nonzero_u16, NonZeroU16);
800    test_nonzero_common!(nonzero_i32, NonZeroI32);
801    test_nonzero_common!(nonzero_u32, NonZeroU32);
802    test_nonzero_common!(nonzero_i64, NonZeroI64);
803    test_nonzero_common!(nonzero_u64, NonZeroU64);
804    test_nonzero_common!(nonzero_isize, NonZeroIsize);
805    test_nonzero_common!(nonzero_usize, NonZeroUsize);
806    test_nonzero_common!(nonzero_i128, NonZeroI128);
807    test_nonzero_common!(nonzero_u128, NonZeroU128);
808
809    #[test]
810    fn test_i64_bool() {
811        Python::with_gil(|py| {
812            let obj = true.to_object(py);
813            assert_eq!(1, obj.extract::<i64>(py).unwrap());
814            let obj = false.to_object(py);
815            assert_eq!(0, obj.extract::<i64>(py).unwrap());
816        })
817    }
818
819    #[test]
820    fn test_i64_f64() {
821        Python::with_gil(|py| {
822            let obj = 12.34f64.to_object(py);
823            let err = obj.extract::<i64>(py).unwrap_err();
824            assert!(err.is_instance_of::<crate::exceptions::PyTypeError>(py));
825            // with no remainder
826            let obj = 12f64.to_object(py);
827            let err = obj.extract::<i64>(py).unwrap_err();
828            assert!(err.is_instance_of::<crate::exceptions::PyTypeError>(py));
829        })
830    }
831}