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 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 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<[<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 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 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 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 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#[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#[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
405int_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 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 unsafe { ffi::PyNumber_Index(obj.as_ptr()).assume_owned_or_err(obj.py()) }.cast_into()
561}
562
563#[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 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 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}