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