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