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