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