1use crate::ffi::{self, Py_ssize_t};
2use crate::ffi_ptr_ext::FfiPtrExt;
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::{type_hint_subscript, PyStaticExpr};
5use crate::instance::Borrowed;
6use crate::internal_tricks::get_ssize_index;
7#[cfg(feature = "experimental-inspect")]
8use crate::type_object::PyTypeInfo;
9use crate::types::{sequence::PySequenceMethods, PyList, PySequence};
10#[cfg(all(
11 not(any(PyPy, GraalPy)),
12 any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
13))]
14use crate::BoundObject;
15use crate::{
16 exceptions, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, PyResult, Python,
17};
18#[cfg(RustPython)]
19use crate::{
20 py_result_ext::PyResultExt,
21 sync::PyOnceLock,
22 types::{PyType, PyTypeMethods},
23 Py,
24};
25use core::iter::FusedIterator;
26#[cfg(feature = "nightly")]
27use core::num::NonZero;
28
29#[cfg(all(
30 not(any(PyPy, GraalPy)),
31 any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
32))]
33use libc::size_t;
34
35#[inline]
36#[track_caller]
37#[cfg_attr(RustPython, allow(unused_mut))]
38fn try_new_from_iter<'py>(
39 py: Python<'py>,
40 mut elements: impl ExactSizeIterator<Item = PyResult<Bound<'py, PyAny>>>,
41) -> PyResult<Bound<'py, PyTuple>> {
42 let len: Py_ssize_t = elements
44 .len()
45 .try_into()
46 .expect("out of range integral type conversion attempted on `elements.len()`");
47
48 #[cfg(not(RustPython))]
49 let (tup, counter) = unsafe {
50 let ptr = ffi::PyTuple_New(len);
51
52 let tup = ptr.assume_owned(py).cast_into_unchecked();
55
56 let mut counter: Py_ssize_t = 0;
57
58 for obj in (&mut elements).take(len as usize) {
59 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
60 ffi::PyTuple_SET_ITEM(ptr, counter, obj?.into_ptr());
61 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
62 ffi::PyTuple_SetItem(ptr, counter, obj?.into_ptr());
63 counter += 1;
64 }
65
66 (tup, counter)
67 };
68
69 #[cfg(RustPython)]
70 let (tup, counter) = unsafe {
71 let elements = (&mut elements)
72 .take(len as _)
73 .collect::<PyResult<Vec<_>>>()?;
74 let tup = ffi::PyTuple_FromArray(elements.as_ptr().cast(), elements.len() as _)
76 .assume_owned_or_err(py)
77 .cast_into_unchecked()?;
78
79 (tup, elements.len() as Py_ssize_t)
80 };
81
82 assert!(elements.next().is_none(), "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation.");
83 assert_eq!(len, counter, "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation.");
84
85 Ok(tup)
86}
87
88#[repr(transparent)]
96pub struct PyTuple(PyAny);
97
98#[cfg(not(RustPython))]
99pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), "builtins", "tuple", #checkfunction=ffi::PyTuple_Check);
100
101#[cfg(RustPython)]
102pyobject_native_type_core!(
103 PyTuple,
104 |py| {
105 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
106 TYPE.import(py, "builtins", "tuple").unwrap().as_type_ptr()
107 },
108 "builtins",
109 "tuple",
110 #checkfunction=ffi::PyTuple_Check
111);
112
113impl PyTuple {
114 #[track_caller]
149 pub fn new<'py, T, U>(
150 py: Python<'py>,
151 elements: impl IntoIterator<Item = T, IntoIter = U>,
152 ) -> PyResult<Bound<'py, PyTuple>>
153 where
154 T: IntoPyObject<'py>,
155 U: ExactSizeIterator<Item = T>,
156 {
157 let elements = elements.into_iter().map(|e| e.into_bound_py_any(py));
158 try_new_from_iter(py, elements)
159 }
160
161 pub fn empty(py: Python<'_>) -> Bound<'_, PyTuple> {
163 unsafe { ffi::PyTuple_New(0).assume_owned(py).cast_into_unchecked() }
164 }
165}
166
167#[doc(alias = "PyTuple")]
173pub trait PyTupleMethods<'py>: crate::sealed::Sealed {
174 fn len(&self) -> usize;
176
177 fn is_empty(&self) -> bool;
179
180 fn as_sequence(&self) -> &Bound<'py, PySequence>;
182
183 fn into_sequence(self) -> Bound<'py, PySequence>;
185
186 fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple>;
191
192 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
207
208 fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>>;
211
212 unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
219
220 unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny>;
227
228 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
230 fn as_slice(&self) -> &[Bound<'py, PyAny>];
231
232 fn contains<V>(&self, value: V) -> PyResult<bool>
236 where
237 V: IntoPyObject<'py>;
238
239 fn index<V>(&self, value: V) -> PyResult<usize>
243 where
244 V: IntoPyObject<'py>;
245
246 fn iter(&self) -> BoundTupleIterator<'py>;
248
249 fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py>;
252
253 fn to_list(&self) -> Bound<'py, PyList>;
257}
258
259impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
260 fn len(&self) -> usize {
261 unsafe {
262 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
263 let size = ffi::PyTuple_GET_SIZE(self.as_ptr());
264 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
265 let size = ffi::PyTuple_Size(self.as_ptr());
266 size as usize
268 }
269 }
270
271 fn is_empty(&self) -> bool {
272 self.len() == 0
273 }
274
275 fn as_sequence(&self) -> &Bound<'py, PySequence> {
276 unsafe { self.cast_unchecked() }
277 }
278
279 fn into_sequence(self) -> Bound<'py, PySequence> {
280 unsafe { self.cast_into_unchecked() }
281 }
282
283 fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple> {
284 unsafe {
285 ffi::PyTuple_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
286 .assume_owned(self.py())
287 .cast_into_unchecked()
288 }
289 }
290
291 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
292 self.get_borrowed_item(index).map(Borrowed::to_owned)
293 }
294
295 fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
296 self.as_borrowed().get_borrowed_item(index)
297 }
298
299 unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
300 unsafe { self.get_borrowed_item_unchecked(index).to_owned() }
301 }
302
303 unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny> {
304 unsafe { self.as_borrowed().get_borrowed_item_unchecked(index) }
305 }
306
307 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
308 fn as_slice(&self) -> &[Bound<'py, PyAny>] {
309 let items = unsafe { &(*self.as_ptr().cast::<ffi::PyTupleObject>()).ob_item };
311 unsafe { core::slice::from_raw_parts(items.as_ptr().cast(), self.len()) }
313 }
314
315 #[inline]
316 fn contains<V>(&self, value: V) -> PyResult<bool>
317 where
318 V: IntoPyObject<'py>,
319 {
320 self.as_sequence().contains(value)
321 }
322
323 #[inline]
324 fn index<V>(&self, value: V) -> PyResult<usize>
325 where
326 V: IntoPyObject<'py>,
327 {
328 self.as_sequence().index(value)
329 }
330
331 fn iter(&self) -> BoundTupleIterator<'py> {
332 BoundTupleIterator::new(self.clone())
333 }
334
335 fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
336 self.as_borrowed().iter_borrowed()
337 }
338
339 fn to_list(&self) -> Bound<'py, PyList> {
340 self.as_sequence()
341 .to_list()
342 .expect("failed to convert tuple to list")
343 }
344}
345
346impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
347 fn get_borrowed_item(self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
348 unsafe {
349 ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
350 .assume_borrowed_or_err(self.py())
351 }
352 }
353
354 unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
358 cfg_select! {
359 not(any(Py_LIMITED_API, PyPy, GraalPy)) => unsafe {
361 ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t)
362 .assume_borrowed_unchecked(self.py())
363 },
364 any(Py_LIMITED_API, PyPy, GraalPy) => unsafe {
366 ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
367 .assume_borrowed_unchecked(self.py())
368 }
369 }
370 }
371
372 pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
373 BorrowedTupleIterator::new(self)
374 }
375}
376
377pub struct BoundTupleIterator<'py> {
379 tuple: Bound<'py, PyTuple>,
380 index: usize,
381 length: usize,
382}
383
384impl<'py> BoundTupleIterator<'py> {
385 fn new(tuple: Bound<'py, PyTuple>) -> Self {
386 let length = tuple.len();
387 BoundTupleIterator {
388 tuple,
389 index: 0,
390 length,
391 }
392 }
393}
394
395impl<'py> Iterator for BoundTupleIterator<'py> {
396 type Item = Bound<'py, PyAny>;
397
398 #[inline]
399 fn next(&mut self) -> Option<Self::Item> {
400 if self.index < self.length {
401 let item = unsafe { self.tuple.get_item_unchecked(self.index) };
403 self.index += 1;
404 Some(item)
405 } else {
406 None
407 }
408 }
409
410 #[inline]
411 fn size_hint(&self) -> (usize, Option<usize>) {
412 let len = self.len();
413 (len, Some(len))
414 }
415
416 #[inline]
417 fn count(self) -> usize
418 where
419 Self: Sized,
420 {
421 self.len()
422 }
423
424 #[inline]
425 fn last(mut self) -> Option<Self::Item>
426 where
427 Self: Sized,
428 {
429 self.next_back()
430 }
431
432 #[inline]
433 #[cfg(not(feature = "nightly"))]
434 fn nth(&mut self, n: usize) -> Option<Self::Item> {
435 if let Some(target_index) = self.index.checked_add(n) {
436 if target_index < self.length {
437 let item = unsafe { self.tuple.get_item_unchecked(target_index) };
439 self.index = target_index + 1;
441 return Some(item);
442 }
443 }
444
445 self.index = self.length;
448 None
449 }
450
451 #[inline]
452 #[cfg(feature = "nightly")]
453 fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
454 let items_left = self.length.saturating_sub(self.index);
455 if let Some(overflow) = NonZero::new(n.saturating_sub(items_left)) {
456 self.index = self.length;
458 return Err(overflow);
459 }
460
461 self.index += n;
463 Ok(())
464 }
465}
466
467impl DoubleEndedIterator for BoundTupleIterator<'_> {
468 #[inline]
469 fn next_back(&mut self) -> Option<Self::Item> {
470 if self.index < self.length {
471 let target_index = self.length - 1;
472 let item = unsafe { self.tuple.get_item_unchecked(target_index) };
474 self.length = target_index;
475 Some(item)
476 } else {
477 None
478 }
479 }
480
481 #[inline]
482 #[cfg(not(feature = "nightly"))]
483 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
484 if let Some(index_after_item) = self.length.checked_sub(n) {
485 if self.index < index_after_item {
486 let target_index = index_after_item - 1;
488 let item = unsafe { self.tuple.get_item_unchecked(target_index) };
490 self.length = target_index;
491 return Some(item);
492 }
493 }
494
495 self.length = self.index;
498 None
499 }
500
501 #[inline]
502 #[cfg(feature = "nightly")]
503 fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
504 let items_left = self.length.saturating_sub(self.index);
505 if let Some(overflow) = NonZero::new(n.saturating_sub(items_left)) {
506 self.length = self.index;
508 return Err(overflow);
509 }
510
511 self.length -= n;
513 Ok(())
514 }
515}
516
517impl ExactSizeIterator for BoundTupleIterator<'_> {
518 fn len(&self) -> usize {
519 self.length.saturating_sub(self.index)
520 }
521}
522
523impl FusedIterator for BoundTupleIterator<'_> {}
524
525impl<'py> IntoIterator for Bound<'py, PyTuple> {
526 type Item = Bound<'py, PyAny>;
527 type IntoIter = BoundTupleIterator<'py>;
528
529 fn into_iter(self) -> Self::IntoIter {
530 BoundTupleIterator::new(self)
531 }
532}
533
534impl<'py> IntoIterator for &Bound<'py, PyTuple> {
535 type Item = Bound<'py, PyAny>;
536 type IntoIter = BoundTupleIterator<'py>;
537
538 fn into_iter(self) -> Self::IntoIter {
539 self.iter()
540 }
541}
542
543pub struct BorrowedTupleIterator<'a, 'py> {
545 tuple: Borrowed<'a, 'py, PyTuple>,
546 index: usize,
547 length: usize,
548}
549
550impl<'a, 'py> BorrowedTupleIterator<'a, 'py> {
551 fn new(tuple: Borrowed<'a, 'py, PyTuple>) -> Self {
552 let length = tuple.len();
553 BorrowedTupleIterator {
554 tuple,
555 index: 0,
556 length,
557 }
558 }
559}
560
561impl<'a, 'py> Iterator for BorrowedTupleIterator<'a, 'py> {
562 type Item = Borrowed<'a, 'py, PyAny>;
563
564 #[inline]
565 fn next(&mut self) -> Option<Self::Item> {
566 if self.index < self.length {
567 let item = unsafe { self.tuple.get_borrowed_item_unchecked(self.index) };
569 self.index += 1;
570 Some(item)
571 } else {
572 None
573 }
574 }
575
576 #[inline]
577 fn size_hint(&self) -> (usize, Option<usize>) {
578 let len = self.len();
579 (len, Some(len))
580 }
581
582 #[inline]
583 fn count(self) -> usize
584 where
585 Self: Sized,
586 {
587 self.len()
588 }
589
590 #[inline]
591 fn last(mut self) -> Option<Self::Item>
592 where
593 Self: Sized,
594 {
595 self.next_back()
596 }
597}
598
599impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> {
600 #[inline]
601 fn next_back(&mut self) -> Option<Self::Item> {
602 if self.index < self.length {
603 let target_index = self.length - 1;
605 let item = unsafe { self.tuple.get_borrowed_item_unchecked(target_index) };
607 self.length = target_index;
608 Some(item)
609 } else {
610 None
611 }
612 }
613}
614
615impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> {
616 fn len(&self) -> usize {
617 self.length.saturating_sub(self.index)
618 }
619}
620
621impl FusedIterator for BorrowedTupleIterator<'_, '_> {}
622
623#[cold]
624fn wrong_tuple_length(t: Borrowed<'_, '_, PyTuple>, expected_length: usize) -> PyErr {
625 let msg = format!(
626 "expected tuple of length {}, but got tuple of length {}",
627 expected_length,
628 t.len()
629 );
630 exceptions::PyValueError::new_err(msg)
631}
632
633macro_rules! tuple_conversion (($length:expr, $(($n:tt, $T:ident)),+) => {
634 impl <'py, $($T),+> IntoPyObject<'py> for ($($T,)+)
635 where
636 $($T: IntoPyObject<'py>,)+
637 {
638 type Target = PyTuple;
639 type Output = Bound<'py, Self::Target>;
640 type Error = PyErr;
641
642 #[cfg(feature = "experimental-inspect")]
643 const OUTPUT_TYPE: PyStaticExpr = type_hint_subscript!(
644 PyTuple::TYPE_HINT,
645 $($T::OUTPUT_TYPE),+
646 );
647
648 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
649 Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
650 }
651 }
652
653 impl <'a, 'py, $($T),+> IntoPyObject<'py> for &'a ($($T,)+)
654 where
655 $(&'a $T: IntoPyObject<'py>,)+
656 {
657 type Target = PyTuple;
658 type Output = Bound<'py, Self::Target>;
659 type Error = PyErr;
660
661 #[cfg(feature = "experimental-inspect")]
662 const OUTPUT_TYPE: PyStaticExpr = type_hint_subscript!(
663 PyTuple::TYPE_HINT,
664 $(<&$T>::OUTPUT_TYPE ),+
665 );
666
667 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
668 Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
669 }
670 }
671
672 impl<'py, $($T),+> crate::call::private::Sealed for ($($T,)+) where $($T: IntoPyObject<'py>,)+ {}
673 impl<'py, $($T),+> crate::call::PyCallArgs<'py> for ($($T,)+)
674 where
675 $($T: IntoPyObject<'py>,)+
676 {
677 #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
678 fn call(
679 self,
680 function: Borrowed<'_, 'py, PyAny>,
681 kwargs: Borrowed<'_, '_, crate::types::PyDict>,
682 _: crate::call::private::Token,
683 ) -> PyResult<Bound<'py, PyAny>> {
684 let py = function.py();
685 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
687 let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
689 unsafe {
690 ffi::PyObject_VectorcallDict(
691 function.as_ptr(),
692 args.as_mut_ptr().add(1),
693 const { with_vectorcall_arguments_offset($length) },
694 kwargs.as_ptr(),
695 )
696 .assume_owned_or_err(py)
697 }
698 }
699
700 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
701 fn call_positional(
702 self,
703 function: Borrowed<'_, 'py, PyAny>,
704 _: crate::call::private::Token,
705 ) -> PyResult<Bound<'py, PyAny>> {
706 let py = function.py();
707 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
708
709 #[cfg(not(Py_LIMITED_API))]
710 if $length == 1 {
711 return unsafe {
712 ffi::PyObject_CallOneArg(
713 function.as_ptr(),
714 args_objects.0.as_ptr()
715 )
716 .assume_owned_or_err(py)
717 };
718 }
719
720 let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
722 unsafe {
723 ffi::PyObject_Vectorcall(
724 function.as_ptr(),
725 args.as_mut_ptr().add(1),
726 const { with_vectorcall_arguments_offset($length) },
727 core::ptr::null_mut(),
728 )
729 .assume_owned_or_err(py)
730 }
731 }
732
733 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
734 fn call_method_positional(
735 self,
736 object: Borrowed<'_, 'py, PyAny>,
737 method_name: Borrowed<'_, 'py, crate::types::PyString>,
738 _: crate::call::private::Token,
739 ) -> PyResult<Bound<'py, PyAny>> {
740 let py = object.py();
741 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
742
743 #[cfg(not(Py_LIMITED_API))]
744 if $length == 1 {
745 return unsafe {
746 ffi::PyObject_CallMethodOneArg(
747 object.as_ptr(),
748 method_name.as_ptr(),
749 args_objects.0.as_ptr()
750 )
751 .assume_owned_or_err(py)
752 };
753 }
754
755 let mut args = [object.as_ptr(), $(args_objects.$n.as_ptr()),*];
756 unsafe {
757 ffi::PyObject_VectorcallMethod(
758 method_name.as_ptr(),
759 args.as_mut_ptr(),
760 const { with_vectorcall_arguments_offset(1 + $length) },
762 core::ptr::null_mut(),
763 )
764 .assume_owned_or_err(py)
765 }
766
767 }
768
769 #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
770 fn call(
771 self,
772 function: Borrowed<'_, 'py, PyAny>,
773 kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
774 token: crate::call::private::Token,
775 ) -> PyResult<Bound<'py, PyAny>> {
776 self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
777 }
778
779 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
780 fn call_positional(
781 self,
782 function: Borrowed<'_, 'py, PyAny>,
783 token: crate::call::private::Token,
784 ) -> PyResult<Bound<'py, PyAny>> {
785 self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
786 }
787
788 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
789 fn call_method_positional(
790 self,
791 object: Borrowed<'_, 'py, PyAny>,
792 method_name: Borrowed<'_, 'py, crate::types::PyString>,
793 token: crate::call::private::Token,
794 ) -> PyResult<Bound<'py, PyAny>> {
795 self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
796 }
797 }
798
799 impl<'a, 'py, $($T),+> crate::call::private::Sealed for &'a ($($T,)+) where $(&'a $T: IntoPyObject<'py>,)+ {}
800 impl<'a, 'py, $($T),+> crate::call::PyCallArgs<'py> for &'a ($($T,)+)
801 where
802 $(&'a $T: IntoPyObject<'py>,)+
803 {
804 #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
805 fn call(
806 self,
807 function: Borrowed<'_, 'py, PyAny>,
808 kwargs: Borrowed<'_, '_, crate::types::PyDict>,
809 _: crate::call::private::Token,
810 ) -> PyResult<Bound<'py, PyAny>> {
811 let py = function.py();
812 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
813 let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
815 unsafe {
816 ffi::PyObject_VectorcallDict(
817 function.as_ptr(),
818 args.as_mut_ptr().add(1),
819 const { with_vectorcall_arguments_offset($length) },
820 kwargs.as_ptr(),
821 )
822 .assume_owned_or_err(py)
823 }
824 }
825
826 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
827 fn call_positional(
828 self,
829 function: Borrowed<'_, 'py, PyAny>,
830 _: crate::call::private::Token,
831 ) -> PyResult<Bound<'py, PyAny>> {
832 let py = function.py();
833 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
834
835 #[cfg(not(Py_LIMITED_API))]
836 if $length == 1 {
837 return unsafe {
838 ffi::PyObject_CallOneArg(
839 function.as_ptr(),
840 args_objects.0.as_ptr()
841 )
842 .assume_owned_or_err(py)
843 };
844 }
845
846 let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
848 unsafe {
849 ffi::PyObject_Vectorcall(
850 function.as_ptr(),
851 args.as_mut_ptr().add(1),
852 const { with_vectorcall_arguments_offset($length) },
853 core::ptr::null_mut(),
854 )
855 .assume_owned_or_err(py)
856 }
857 }
858
859 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
860 fn call_method_positional(
861 self,
862 object: Borrowed<'_, 'py, PyAny>,
863 method_name: Borrowed<'_, 'py, crate::types::PyString>,
864 _: crate::call::private::Token,
865 ) -> PyResult<Bound<'py, PyAny>> {
866 let py = object.py();
867 let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
868
869 #[cfg(not(Py_LIMITED_API))]
870 if $length == 1 {
871 return unsafe {
872 ffi::PyObject_CallMethodOneArg(
873 object.as_ptr(),
874 method_name.as_ptr(),
875 args_objects.0.as_ptr(),
876 )
877 .assume_owned_or_err(py)
878 };
879 }
880
881 let mut args = [object.as_ptr(), $(args_objects.$n.as_ptr()),*];
882 unsafe {
883 ffi::PyObject_VectorcallMethod(
884 method_name.as_ptr(),
885 args.as_mut_ptr(),
886 const { with_vectorcall_arguments_offset(1 + $length) },
888 core::ptr::null_mut(),
889 )
890 .assume_owned_or_err(py)
891 }
892 }
893
894 #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
895 fn call(
896 self,
897 function: Borrowed<'_, 'py, PyAny>,
898 kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
899 token: crate::call::private::Token,
900 ) -> PyResult<Bound<'py, PyAny>> {
901 self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
902 }
903
904 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
905 fn call_positional(
906 self,
907 function: Borrowed<'_, 'py, PyAny>,
908 token: crate::call::private::Token,
909 ) -> PyResult<Bound<'py, PyAny>> {
910 self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
911 }
912
913 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
914 fn call_method_positional(
915 self,
916 object: Borrowed<'_, 'py, PyAny>,
917 method_name: Borrowed<'_, 'py, crate::types::PyString>,
918 token: crate::call::private::Token,
919 ) -> PyResult<Bound<'py, PyAny>> {
920 self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
921 }
922 }
923
924 impl<'a, 'py, $($T: FromPyObject<'a, 'py>),+> FromPyObject<'a, 'py> for ($($T,)+) {
925 type Error = PyErr;
926
927 #[cfg(feature = "experimental-inspect")]
928 const INPUT_TYPE: PyStaticExpr = type_hint_subscript!(
929 PyTuple::TYPE_HINT,
930 $($T::INPUT_TYPE ),+
931 );
932
933 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error>
934 {
935 let t = obj.cast::<PyTuple>()?;
936 if t.len() == $length {
937 Ok(($(
938 unsafe { t.get_borrowed_item_unchecked($n) }
940 .extract::<$T>()
941 .map_err(Into::into)?,
942 )+))
943 } else {
944 Err(wrong_tuple_length(t, $length))
945 }
946 }
947 }
948});
949
950fn array_into_tuple<'py, const N: usize>(
951 py: Python<'py>,
952 array: [Bound<'py, PyAny>; N],
953) -> Bound<'py, PyTuple> {
954 #[cfg(not(RustPython))]
955 unsafe {
956 let ptr = ffi::PyTuple_New(N.try_into().expect("0 < N <= 12"));
957 let tup = ptr.assume_owned(py).cast_into_unchecked();
958 for (index, obj) in array.into_iter().enumerate() {
959 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
960 ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
961 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
962 ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
963 }
964 tup
965 }
966
967 #[cfg(RustPython)]
970 unsafe {
971 ffi::PyTuple_FromArray(array.as_ptr().cast(), N.try_into().expect("0 < N <= 12"))
972 .assume_owned(py)
973 .cast_into_unchecked()
974 }
975}
976
977#[cfg(all(
981 not(any(PyPy, GraalPy)),
982 any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
983))]
984const fn with_vectorcall_arguments_offset(n: size_t) -> size_t {
985 n.checked_add(ffi::PY_VECTORCALL_ARGUMENTS_OFFSET)
986 .expect("overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET")
987}
988
989tuple_conversion!(1, (0, T0));
990tuple_conversion!(2, (0, T0), (1, T1));
991tuple_conversion!(3, (0, T0), (1, T1), (2, T2));
992tuple_conversion!(4, (0, T0), (1, T1), (2, T2), (3, T3));
993tuple_conversion!(5, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
994tuple_conversion!(6, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
995tuple_conversion!(
996 7,
997 (0, T0),
998 (1, T1),
999 (2, T2),
1000 (3, T3),
1001 (4, T4),
1002 (5, T5),
1003 (6, T6)
1004);
1005tuple_conversion!(
1006 8,
1007 (0, T0),
1008 (1, T1),
1009 (2, T2),
1010 (3, T3),
1011 (4, T4),
1012 (5, T5),
1013 (6, T6),
1014 (7, T7)
1015);
1016tuple_conversion!(
1017 9,
1018 (0, T0),
1019 (1, T1),
1020 (2, T2),
1021 (3, T3),
1022 (4, T4),
1023 (5, T5),
1024 (6, T6),
1025 (7, T7),
1026 (8, T8)
1027);
1028tuple_conversion!(
1029 10,
1030 (0, T0),
1031 (1, T1),
1032 (2, T2),
1033 (3, T3),
1034 (4, T4),
1035 (5, T5),
1036 (6, T6),
1037 (7, T7),
1038 (8, T8),
1039 (9, T9)
1040);
1041tuple_conversion!(
1042 11,
1043 (0, T0),
1044 (1, T1),
1045 (2, T2),
1046 (3, T3),
1047 (4, T4),
1048 (5, T5),
1049 (6, T6),
1050 (7, T7),
1051 (8, T8),
1052 (9, T9),
1053 (10, T10)
1054);
1055tuple_conversion!(
1056 12,
1057 (0, T0),
1058 (1, T1),
1059 (2, T2),
1060 (3, T3),
1061 (4, T4),
1062 (5, T5),
1063 (6, T6),
1064 (7, T7),
1065 (8, T8),
1066 (9, T9),
1067 (10, T10),
1068 (11, T11)
1069);
1070
1071#[cfg(test)]
1072mod tests {
1073 use crate::platform::HashSet;
1074 use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyList, PyTuple};
1075 use crate::{Bound, IntoPyObject, PyAny, Python};
1076 #[cfg(feature = "nightly")]
1077 use core::num::NonZero;
1078 use core::ops::Range;
1079
1080 #[test]
1081 fn test_new() {
1082 Python::attach(|py| {
1083 let ob = PyTuple::new(py, [1, 2, 3]).unwrap();
1084 assert_eq!(3, ob.len());
1085 let ob = ob.as_any();
1086 assert_eq!((1, 2, 3), ob.extract().unwrap());
1087
1088 let mut map = HashSet::new();
1089 map.insert(1);
1090 map.insert(2);
1091 PyTuple::new(py, map).unwrap();
1092 });
1093 }
1094
1095 #[test]
1096 fn test_len() {
1097 Python::attach(|py| {
1098 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1099 let tuple = ob.cast::<PyTuple>().unwrap();
1100 assert_eq!(3, tuple.len());
1101 assert!(!tuple.is_empty());
1102 let ob = tuple.as_any();
1103 assert_eq!((1, 2, 3), ob.extract().unwrap());
1104 });
1105 }
1106
1107 #[test]
1108 fn test_empty() {
1109 Python::attach(|py| {
1110 let tuple = PyTuple::empty(py);
1111 assert!(tuple.is_empty());
1112 assert_eq!(0, tuple.len());
1113 });
1114 }
1115
1116 #[test]
1117 fn test_slice() {
1118 Python::attach(|py| {
1119 let tup = PyTuple::new(py, [2, 3, 5, 7]).unwrap();
1120 let slice = tup.get_slice(1, 3);
1121 assert_eq!(2, slice.len());
1122 let slice = tup.get_slice(1, 7);
1123 assert_eq!(3, slice.len());
1124 });
1125 }
1126
1127 #[test]
1128 fn test_iter() {
1129 Python::attach(|py| {
1130 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1131 let tuple = ob.cast::<PyTuple>().unwrap();
1132 assert_eq!(3, tuple.len());
1133 let mut iter = tuple.iter();
1134
1135 assert_eq!(iter.size_hint(), (3, Some(3)));
1136
1137 assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1138 assert_eq!(iter.size_hint(), (2, Some(2)));
1139
1140 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1141 assert_eq!(iter.size_hint(), (1, Some(1)));
1142
1143 assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1144 assert_eq!(iter.size_hint(), (0, Some(0)));
1145
1146 assert!(iter.next().is_none());
1147 assert!(iter.next().is_none());
1148 });
1149 }
1150
1151 #[test]
1152 fn test_iter_rev() {
1153 Python::attach(|py| {
1154 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1155 let tuple = ob.cast::<PyTuple>().unwrap();
1156 assert_eq!(3, tuple.len());
1157 let mut iter = tuple.iter().rev();
1158
1159 assert_eq!(iter.size_hint(), (3, Some(3)));
1160
1161 assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1162 assert_eq!(iter.size_hint(), (2, Some(2)));
1163
1164 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1165 assert_eq!(iter.size_hint(), (1, Some(1)));
1166
1167 assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1168 assert_eq!(iter.size_hint(), (0, Some(0)));
1169
1170 assert!(iter.next().is_none());
1171 assert!(iter.next().is_none());
1172 });
1173 }
1174
1175 #[test]
1176 fn test_bound_iter() {
1177 Python::attach(|py| {
1178 let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1179 assert_eq!(3, tuple.len());
1180 let mut iter = tuple.iter();
1181
1182 assert_eq!(iter.size_hint(), (3, Some(3)));
1183
1184 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1185 assert_eq!(iter.size_hint(), (2, Some(2)));
1186
1187 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1188 assert_eq!(iter.size_hint(), (1, Some(1)));
1189
1190 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1191 assert_eq!(iter.size_hint(), (0, Some(0)));
1192
1193 assert!(iter.next().is_none());
1194 assert!(iter.next().is_none());
1195 });
1196 }
1197
1198 #[test]
1199 fn test_bound_iter_rev() {
1200 Python::attach(|py| {
1201 let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1202 assert_eq!(3, tuple.len());
1203 let mut iter = tuple.iter().rev();
1204
1205 assert_eq!(iter.size_hint(), (3, Some(3)));
1206
1207 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1208 assert_eq!(iter.size_hint(), (2, Some(2)));
1209
1210 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1211 assert_eq!(iter.size_hint(), (1, Some(1)));
1212
1213 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1214 assert_eq!(iter.size_hint(), (0, Some(0)));
1215
1216 assert!(iter.next().is_none());
1217 assert!(iter.next().is_none());
1218 });
1219 }
1220
1221 #[test]
1222 fn test_into_iter() {
1223 Python::attach(|py| {
1224 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1225 let tuple = ob.cast::<PyTuple>().unwrap();
1226 assert_eq!(3, tuple.len());
1227
1228 for (i, item) in tuple.iter().enumerate() {
1229 assert_eq!(i + 1, item.extract::<'_, usize>().unwrap());
1230 }
1231 });
1232 }
1233
1234 #[test]
1235 fn test_into_iter_bound() {
1236 Python::attach(|py| {
1237 let tuple = (1, 2, 3).into_pyobject(py).unwrap();
1238 assert_eq!(3, tuple.len());
1239
1240 let mut items = vec![];
1241 for item in tuple {
1242 items.push(item.extract::<usize>().unwrap());
1243 }
1244 assert_eq!(items, vec![1, 2, 3]);
1245 });
1246 }
1247
1248 #[test]
1249 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
1250 fn test_as_slice() {
1251 Python::attach(|py| {
1252 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1253 let tuple = ob.cast::<PyTuple>().unwrap();
1254
1255 let slice = tuple.as_slice();
1256 assert_eq!(3, slice.len());
1257 assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap());
1258 assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap());
1259 assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap());
1260 });
1261 }
1262
1263 #[test]
1264 fn test_tuple_lengths_up_to_12() {
1265 Python::attach(|py| {
1266 let t0 = (0,).into_pyobject(py).unwrap();
1267 let t1 = (0, 1).into_pyobject(py).unwrap();
1268 let t2 = (0, 1, 2).into_pyobject(py).unwrap();
1269 let t3 = (0, 1, 2, 3).into_pyobject(py).unwrap();
1270 let t4 = (0, 1, 2, 3, 4).into_pyobject(py).unwrap();
1271 let t5 = (0, 1, 2, 3, 4, 5).into_pyobject(py).unwrap();
1272 let t6 = (0, 1, 2, 3, 4, 5, 6).into_pyobject(py).unwrap();
1273 let t7 = (0, 1, 2, 3, 4, 5, 6, 7).into_pyobject(py).unwrap();
1274 let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).into_pyobject(py).unwrap();
1275 let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).into_pyobject(py).unwrap();
1276 let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
1277 .into_pyobject(py)
1278 .unwrap();
1279 let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
1280 .into_pyobject(py)
1281 .unwrap();
1282
1283 assert_eq!(t0.extract::<(i32,)>().unwrap(), (0,));
1284 assert_eq!(t1.extract::<(i32, i32)>().unwrap(), (0, 1,));
1285 assert_eq!(t2.extract::<(i32, i32, i32)>().unwrap(), (0, 1, 2,));
1286 assert_eq!(
1287 t3.extract::<(i32, i32, i32, i32,)>().unwrap(),
1288 (0, 1, 2, 3,)
1289 );
1290 assert_eq!(
1291 t4.extract::<(i32, i32, i32, i32, i32,)>().unwrap(),
1292 (0, 1, 2, 3, 4,)
1293 );
1294 assert_eq!(
1295 t5.extract::<(i32, i32, i32, i32, i32, i32,)>().unwrap(),
1296 (0, 1, 2, 3, 4, 5,)
1297 );
1298 assert_eq!(
1299 t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>()
1300 .unwrap(),
1301 (0, 1, 2, 3, 4, 5, 6,)
1302 );
1303 assert_eq!(
1304 t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>()
1305 .unwrap(),
1306 (0, 1, 2, 3, 4, 5, 6, 7,)
1307 );
1308 assert_eq!(
1309 t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1310 .unwrap(),
1311 (0, 1, 2, 3, 4, 5, 6, 7, 8,)
1312 );
1313 assert_eq!(
1314 t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1315 .unwrap(),
1316 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9,)
1317 );
1318 assert_eq!(
1319 t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1320 .unwrap(),
1321 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,)
1322 );
1323 assert_eq!(
1324 t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1325 .unwrap(),
1326 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,)
1327 );
1328 })
1329 }
1330
1331 #[test]
1332 fn test_tuple_get_item_invalid_index() {
1333 Python::attach(|py| {
1334 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1335 let tuple = ob.cast::<PyTuple>().unwrap();
1336 let obj = tuple.get_item(5);
1337 assert!(obj.is_err());
1338 assert_eq!(
1339 obj.unwrap_err().to_string(),
1340 "IndexError: tuple index out of range"
1341 );
1342 });
1343 }
1344
1345 #[test]
1346 fn test_tuple_get_item_sanity() {
1347 Python::attach(|py| {
1348 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1349 let tuple = ob.cast::<PyTuple>().unwrap();
1350 let obj = tuple.get_item(0);
1351 assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
1352 });
1353 }
1354
1355 #[test]
1356 fn test_tuple_get_item_unchecked_sanity() {
1357 Python::attach(|py| {
1358 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1359 let tuple = ob.cast::<PyTuple>().unwrap();
1360 let obj = unsafe { tuple.get_item_unchecked(0) };
1361 assert_eq!(obj.extract::<i32>().unwrap(), 1);
1362 });
1363 }
1364
1365 #[test]
1366 fn test_tuple_contains() {
1367 Python::attach(|py| {
1368 let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1369 let tuple = ob.cast::<PyTuple>().unwrap();
1370 assert_eq!(6, tuple.len());
1371
1372 let bad_needle = 7i32.into_pyobject(py).unwrap();
1373 assert!(!tuple.contains(&bad_needle).unwrap());
1374
1375 let good_needle = 8i32.into_pyobject(py).unwrap();
1376 assert!(tuple.contains(&good_needle).unwrap());
1377
1378 let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
1379 assert!(tuple.contains(&type_coerced_needle).unwrap());
1380 });
1381 }
1382
1383 #[test]
1384 fn test_tuple_index() {
1385 Python::attach(|py| {
1386 let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1387 let tuple = ob.cast::<PyTuple>().unwrap();
1388 assert_eq!(0, tuple.index(1i32).unwrap());
1389 assert_eq!(2, tuple.index(2i32).unwrap());
1390 assert_eq!(3, tuple.index(3i32).unwrap());
1391 assert_eq!(4, tuple.index(5i32).unwrap());
1392 assert_eq!(5, tuple.index(8i32).unwrap());
1393 assert!(tuple.index(42i32).is_err());
1394 });
1395 }
1396
1397 struct FaultyIter(Range<usize>, usize);
1400
1401 impl Iterator for FaultyIter {
1402 type Item = usize;
1403
1404 fn next(&mut self) -> Option<Self::Item> {
1405 self.0.next()
1406 }
1407 }
1408
1409 impl ExactSizeIterator for FaultyIter {
1410 fn len(&self) -> usize {
1411 self.1
1412 }
1413 }
1414
1415 #[test]
1416 #[should_panic(
1417 expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1418 )]
1419 fn too_long_iterator() {
1420 Python::attach(|py| {
1421 let iter = FaultyIter(0..usize::MAX, 73);
1422 let _tuple = PyTuple::new(py, iter);
1423 })
1424 }
1425
1426 #[test]
1427 #[should_panic(
1428 expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1429 )]
1430 fn too_short_iterator() {
1431 Python::attach(|py| {
1432 let iter = FaultyIter(0..35, 73);
1433 let _tuple = PyTuple::new(py, iter);
1434 })
1435 }
1436
1437 #[test]
1438 #[should_panic(
1439 expected = "out of range integral type conversion attempted on `elements.len()`"
1440 )]
1441 fn overflowing_size() {
1442 Python::attach(|py| {
1443 let iter = FaultyIter(0..0, usize::MAX);
1444
1445 let _tuple = PyTuple::new(py, iter);
1446 })
1447 }
1448
1449 #[test]
1450 #[cfg(panic = "unwind")]
1451 fn bad_intopyobject_doesnt_cause_leaks() {
1452 use crate::types::PyInt;
1453 use core::convert::Infallible;
1454 use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1455
1456 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1457
1458 struct Bad(usize);
1459
1460 impl Drop for Bad {
1461 fn drop(&mut self) {
1462 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1463 }
1464 }
1465
1466 impl<'py> IntoPyObject<'py> for Bad {
1467 type Target = PyInt;
1468 type Output = crate::Bound<'py, Self::Target>;
1469 type Error = Infallible;
1470
1471 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1472 assert_ne!(self.0, 42);
1474 self.0.into_pyobject(py)
1475 }
1476 }
1477
1478 struct FaultyIter(Range<usize>, usize);
1479
1480 impl Iterator for FaultyIter {
1481 type Item = Bad;
1482
1483 fn next(&mut self) -> Option<Self::Item> {
1484 self.0.next().map(|i| {
1485 NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1486 Bad(i)
1487 })
1488 }
1489 }
1490
1491 impl ExactSizeIterator for FaultyIter {
1492 fn len(&self) -> usize {
1493 self.1
1494 }
1495 }
1496
1497 Python::attach(|py| {
1498 std::panic::catch_unwind(|| {
1499 let iter = FaultyIter(0..50, 50);
1500 let _tuple = PyTuple::new(py, iter);
1501 })
1502 .unwrap_err();
1503 });
1504
1505 assert_eq!(
1506 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1507 0,
1508 "Some destructors did not run"
1509 );
1510 }
1511
1512 #[test]
1513 #[cfg(panic = "unwind")]
1514 fn bad_intopyobject_doesnt_cause_leaks_2() {
1515 use crate::types::PyInt;
1516 use core::convert::Infallible;
1517 use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1518
1519 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1520
1521 struct Bad(usize);
1522
1523 impl Drop for Bad {
1524 fn drop(&mut self) {
1525 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1526 }
1527 }
1528
1529 impl<'py> IntoPyObject<'py> for &Bad {
1530 type Target = PyInt;
1531 type Output = crate::Bound<'py, Self::Target>;
1532 type Error = Infallible;
1533
1534 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1535 assert_ne!(self.0, 3);
1537 self.0.into_pyobject(py)
1538 }
1539 }
1540
1541 let s = (Bad(1), Bad(2), Bad(3), Bad(4));
1542 NEEDS_DESTRUCTING_COUNT.store(4, SeqCst);
1543 Python::attach(|py| {
1544 std::panic::catch_unwind(|| {
1545 let _tuple = (&s).into_pyobject(py).unwrap();
1546 })
1547 .unwrap_err();
1548 });
1549 drop(s);
1550
1551 assert_eq!(
1552 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1553 0,
1554 "Some destructors did not run"
1555 );
1556 }
1557
1558 #[test]
1559 fn test_tuple_to_list() {
1560 Python::attach(|py| {
1561 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1562 let list = tuple.to_list();
1563 let list_expected = PyList::new(py, vec![1, 2, 3]).unwrap();
1564 assert!(list.eq(list_expected).unwrap());
1565 })
1566 }
1567
1568 #[test]
1569 fn test_tuple_as_sequence() {
1570 Python::attach(|py| {
1571 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1572 let sequence = tuple.as_sequence();
1573 assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
1574 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1575
1576 assert_eq!(tuple.len(), 3);
1577 assert_eq!(sequence.len().unwrap(), 3);
1578 })
1579 }
1580
1581 #[test]
1582 fn test_tuple_into_sequence() {
1583 Python::attach(|py| {
1584 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1585 let sequence = tuple.into_sequence();
1586 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1587 assert_eq!(sequence.len().unwrap(), 3);
1588 })
1589 }
1590
1591 #[test]
1592 fn test_bound_tuple_get_item() {
1593 Python::attach(|py| {
1594 let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1595
1596 assert_eq!(tuple.len(), 4);
1597 assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
1598 assert_eq!(
1599 tuple
1600 .get_borrowed_item(1)
1601 .unwrap()
1602 .extract::<i32>()
1603 .unwrap(),
1604 2
1605 );
1606
1607 assert_eq!(
1608 unsafe { tuple.get_item_unchecked(2) }
1609 .extract::<i32>()
1610 .unwrap(),
1611 3
1612 );
1613 assert_eq!(
1614 unsafe { tuple.get_borrowed_item_unchecked(3) }
1615 .extract::<i32>()
1616 .unwrap(),
1617 4
1618 );
1619 })
1620 }
1621
1622 fn test_iterators(
1624 tuple: &Bound<'_, PyTuple>,
1625 f: impl Fn(&mut dyn DoubleEndedIterator<Item = Bound<'_, PyAny>>),
1626 ) {
1627 let mut iter = tuple.iter();
1628 f(&mut iter);
1629
1630 let mut borrowed_iter = tuple.iter_borrowed().map(|item| item.to_owned());
1631 f(&mut borrowed_iter);
1632 }
1633
1634 #[test]
1635 fn test_tuple_iter_nth() {
1636 Python::attach(|py| {
1637 let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1638
1639 test_iterators(&tuple, |iter| {
1640 assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 2);
1641 assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 4);
1642 assert!(iter.nth(1).is_none());
1643 });
1644
1645 let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1646 test_iterators(&tuple, |iter| {
1647 iter.next();
1648 assert!(iter.nth(1).is_none());
1649 });
1650
1651 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1652 test_iterators(&tuple, |iter| {
1653 assert!(iter.nth(10).is_none());
1654 });
1655
1656 let tuple = PyTuple::new(py, vec![6, 7, 8, 9, 10]).unwrap();
1657 test_iterators(&tuple, |iter| {
1658 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 6);
1659 assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 9);
1660 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 10);
1661 });
1662
1663 test_iterators(&tuple, |iter| {
1664 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 9);
1665 assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 8);
1666 assert!(iter.next().is_none());
1667 });
1668
1669 test_iterators(&tuple, |iter| {
1671 assert!(iter.nth(100).is_none());
1672 assert!(iter.next().is_none());
1673 assert!(iter.next_back().is_none());
1674 });
1675
1676 test_iterators(&tuple, |iter| {
1680 assert!(iter.next().is_some());
1681 assert!(iter.nth(usize::MAX).is_none());
1682 });
1683 });
1684 }
1685
1686 #[test]
1687 fn test_tuple_iter_nth_back() {
1688 Python::attach(|py| {
1689 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1690 test_iterators(&tuple, |iter| {
1691 assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 5);
1692 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1693 assert!(iter.nth_back(2).is_none());
1694 });
1695
1696 let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1697 test_iterators(&tuple, |iter| {
1698 assert!(iter.nth_back(0).is_none());
1699 assert!(iter.nth_back(1).is_none());
1700 });
1701
1702 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1703 test_iterators(&tuple, |iter| {
1704 assert!(iter.nth_back(5).is_none());
1705 });
1706
1707 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1708 test_iterators(&tuple, |iter| {
1709 iter.next_back(); assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1711 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
1712 assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 1);
1713 });
1714
1715 test_iterators(&tuple, |iter| {
1716 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 4);
1717 assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 1);
1718 });
1719
1720 test_iterators(&tuple, |iter| {
1721 iter.next_back();
1722 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1723 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
1724 });
1725
1726 test_iterators(&tuple, |iter| {
1727 iter.nth(1);
1728 assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 3);
1729 assert!(iter.nth_back(0).is_none());
1730 });
1731
1732 test_iterators(&tuple, |iter| {
1734 assert!(iter.nth_back(100).is_none());
1735 assert!(iter.next_back().is_none());
1736 assert!(iter.next().is_none());
1737 });
1738
1739 test_iterators(&tuple, |iter| {
1741 iter.nth(1);
1742 assert!(iter.nth_back(usize::MAX).is_none());
1743 });
1744 });
1745 }
1746
1747 #[cfg(feature = "nightly")]
1748 #[test]
1749 fn test_tuple_iter_advance_by() {
1750 Python::attach(|py| {
1751 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1752 test_iterators(&tuple, |iter| {
1753 assert_eq!(iter.advance_by(2), Ok(()));
1754 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
1755 assert_eq!(iter.advance_by(0), Ok(()));
1756 assert_eq!(iter.advance_by(100), Err(NonZero::new(98).unwrap()));
1757 assert!(iter.next().is_none());
1758 });
1759
1760 test_iterators(&tuple, |iter| {
1761 assert_eq!(iter.advance_by(6), Err(NonZero::new(1).unwrap()));
1762 });
1763
1764 test_iterators(&tuple, |iter| {
1765 assert_eq!(iter.advance_by(5), Ok(()));
1766 });
1767
1768 test_iterators(&tuple, |iter| {
1769 assert_eq!(iter.advance_by(0), Ok(()));
1770 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 1);
1771 });
1772
1773 test_iterators(&tuple, |iter| {
1777 assert_eq!(
1778 iter.advance_by(usize::MAX),
1779 Err(NonZero::new(usize::MAX - 5).unwrap())
1780 );
1781 assert_eq!(
1782 iter.advance_by(usize::MAX),
1783 Err(NonZero::new(usize::MAX).unwrap())
1784 );
1785 });
1786 })
1787 }
1788
1789 #[cfg(feature = "nightly")]
1790 #[test]
1791 fn test_tuple_iter_advance_back_by() {
1792 Python::attach(|py| {
1793 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1794 test_iterators(&tuple, |iter| {
1795 assert_eq!(iter.advance_back_by(2), Ok(()));
1796 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 3);
1797 assert_eq!(iter.advance_back_by(0), Ok(()));
1798 assert_eq!(iter.advance_back_by(100), Err(NonZero::new(98).unwrap()));
1799 assert!(iter.next_back().is_none());
1800 });
1801
1802 test_iterators(&tuple, |iter| {
1803 assert_eq!(iter.advance_back_by(6), Err(NonZero::new(1).unwrap()));
1804 });
1805
1806 test_iterators(&tuple, |iter| {
1807 assert_eq!(iter.advance_back_by(5), Ok(()));
1808 });
1809
1810 test_iterators(&tuple, |iter| {
1811 assert_eq!(iter.advance_back_by(0), Ok(()));
1812 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 5);
1813 });
1814
1815 test_iterators(&tuple, |iter| {
1819 assert_eq!(
1820 iter.advance_back_by(usize::MAX),
1821 Err(NonZero::new(usize::MAX - 5).unwrap())
1822 );
1823 assert_eq!(
1824 iter.advance_back_by(usize::MAX),
1825 Err(NonZero::new(usize::MAX).unwrap())
1826 );
1827 });
1828 })
1829 }
1830
1831 #[test]
1832 fn test_tuple_iter_last() {
1833 Python::attach(|py| {
1834 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1835 test_iterators(&tuple, |iter| {
1836 let last = iter.last();
1837 assert_eq!(last.unwrap().extract::<i32>().unwrap(), 3);
1838 });
1839 })
1840 }
1841
1842 #[test]
1843 fn test_tuple_iter_count() {
1844 Python::attach(|py| {
1845 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1846 test_iterators(&tuple, |iter| {
1847 assert_eq!(iter.count(), 3);
1848 });
1849 })
1850 }
1851}