Skip to main content

pyo3/conversions/std/
num.rs

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