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