pyo3/conversions/std/
num.rs

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