pyo3/types/
tuple.rs

1use crate::ffi::{self, Py_ssize_t};
2use crate::ffi_ptr_ext::FfiPtrExt;
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::instance::Borrowed;
6use crate::internal_tricks::get_ssize_index;
7use crate::types::{any::PyAnyMethods, sequence::PySequenceMethods, PyList, PySequence};
8use crate::{
9    exceptions, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, PyResult, Python,
10};
11use std::iter::FusedIterator;
12#[cfg(feature = "nightly")]
13use std::num::NonZero;
14
15#[inline]
16#[track_caller]
17fn try_new_from_iter<'py>(
18    py: Python<'py>,
19    mut elements: impl ExactSizeIterator<Item = PyResult<Bound<'py, PyAny>>>,
20) -> PyResult<Bound<'py, PyTuple>> {
21    unsafe {
22        // PyTuple_New checks for overflow but has a bad error message, so we check ourselves
23        let len: Py_ssize_t = elements
24            .len()
25            .try_into()
26            .expect("out of range integral type conversion attempted on `elements.len()`");
27
28        let ptr = ffi::PyTuple_New(len);
29
30        // - Panics if the ptr is null
31        // - Cleans up the tuple if `convert` or the asserts panic
32        let tup = ptr.assume_owned(py).cast_into_unchecked();
33
34        let mut counter: Py_ssize_t = 0;
35
36        for obj in (&mut elements).take(len as usize) {
37            #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
38            ffi::PyTuple_SET_ITEM(ptr, counter, obj?.into_ptr());
39            #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
40            ffi::PyTuple_SetItem(ptr, counter, obj?.into_ptr());
41            counter += 1;
42        }
43
44        assert!(elements.next().is_none(), "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation.");
45        assert_eq!(len, counter, "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation.");
46
47        Ok(tup)
48    }
49}
50
51/// Represents a Python `tuple` object.
52///
53/// Values of this type are accessed via PyO3's smart pointers, e.g. as
54/// [`Py<PyTuple>`][crate::Py] or [`Bound<'py, PyTuple>`][Bound].
55///
56/// For APIs available on `tuple` objects, see the [`PyTupleMethods`] trait which is implemented for
57/// [`Bound<'py, PyTuple>`][Bound].
58#[repr(transparent)]
59pub struct PyTuple(PyAny);
60
61pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), #checkfunction=ffi::PyTuple_Check);
62
63impl PyTuple {
64    /// Constructs a new tuple with the given elements.
65    ///
66    /// If you want to create a [`PyTuple`] with elements of different or unknown types, create a Rust
67    /// tuple with the given elements and convert it at once using [`into_pyobject()`][crate::IntoPyObject].
68    /// (`IntoPyObject` is implemented for tuples of up to 12 elements.)
69    ///
70    /// To create a [`PyTuple`] from an iterable that doesn't implement [`ExactSizeIterator`],
71    /// collect the elements into a `Vec` first.
72    ///
73    /// # Examples
74    ///
75    /// ```rust
76    /// use pyo3::prelude::*;
77    /// use pyo3::types::PyTuple;
78    ///
79    /// # fn main() -> PyResult<()> {
80    /// Python::attach(|py| {
81    ///     let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
82    ///     let tuple = PyTuple::new(py, elements)?;
83    ///     assert_eq!(format!("{:?}", tuple), "(0, 1, 2, 3, 4, 5)");
84    ///
85    ///     // alternative using `into_pyobject()`
86    ///     let tuple = (0, "hello", true).into_pyobject(py)?;
87    ///     assert_eq!(format!("{:?}", tuple), "(0, 'hello', True)");
88    /// # Ok(())
89    /// })
90    /// # }
91    /// ```
92    ///
93    /// # Panics
94    ///
95    /// This function will panic if `element`'s [`ExactSizeIterator`] implementation is incorrect.
96    /// All standard library structures implement this trait correctly, if they do, so calling this
97    /// function using [`Vec`]`<T>` or `&[T]` will always succeed.
98    #[track_caller]
99    pub fn new<'py, T, U>(
100        py: Python<'py>,
101        elements: impl IntoIterator<Item = T, IntoIter = U>,
102    ) -> PyResult<Bound<'py, PyTuple>>
103    where
104        T: IntoPyObject<'py>,
105        U: ExactSizeIterator<Item = T>,
106    {
107        let elements = elements.into_iter().map(|e| e.into_bound_py_any(py));
108        try_new_from_iter(py, elements)
109    }
110
111    /// Constructs an empty tuple (on the Python side, a singleton object).
112    pub fn empty(py: Python<'_>) -> Bound<'_, PyTuple> {
113        unsafe { ffi::PyTuple_New(0).assume_owned(py).cast_into_unchecked() }
114    }
115}
116
117/// Implementation of functionality for [`PyTuple`].
118///
119/// These methods are defined for the `Bound<'py, PyTuple>` smart pointer, so to use method call
120/// syntax these methods are separated into a trait, because stable Rust does not yet support
121/// `arbitrary_self_types`.
122#[doc(alias = "PyTuple")]
123pub trait PyTupleMethods<'py>: crate::sealed::Sealed {
124    /// Gets the length of the tuple.
125    fn len(&self) -> usize;
126
127    /// Checks if the tuple is empty.
128    fn is_empty(&self) -> bool;
129
130    /// Returns `self` cast as a `PySequence`.
131    fn as_sequence(&self) -> &Bound<'py, PySequence>;
132
133    /// Returns `self` cast as a `PySequence`.
134    fn into_sequence(self) -> Bound<'py, PySequence>;
135
136    /// Takes the slice `self[low:high]` and returns it as a new tuple.
137    ///
138    /// Indices must be nonnegative, and out-of-range indices are clipped to
139    /// `self.len()`.
140    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple>;
141
142    /// Gets the tuple item at the specified index.
143    /// # Example
144    /// ```
145    /// use pyo3::prelude::*;
146    ///
147    /// # fn main() -> PyResult<()> {
148    /// Python::attach(|py| -> PyResult<()> {
149    ///     let tuple = (1, 2, 3).into_pyobject(py)?;
150    ///     let obj = tuple.get_item(0);
151    ///     assert_eq!(obj?.extract::<i32>()?, 1);
152    ///     Ok(())
153    /// })
154    /// # }
155    /// ```
156    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
157
158    /// Like [`get_item`][PyTupleMethods::get_item], but returns a borrowed object, which is a slight performance optimization
159    /// by avoiding a reference count change.
160    fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>>;
161
162    /// Gets the tuple item at the specified index. Undefined behavior on bad index. Use with caution.
163    ///
164    /// # Safety
165    ///
166    /// Caller must verify that the index is within the bounds of the tuple.
167    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
168    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
169
170    /// Like [`get_item_unchecked`][PyTupleMethods::get_item_unchecked], but returns a borrowed object,
171    /// which is a slight performance optimization by avoiding a reference count change.
172    ///
173    /// # Safety
174    ///
175    /// Caller must verify that the index is within the bounds of the tuple.
176    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
177    unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny>;
178
179    /// Returns `self` as a slice of objects.
180    #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
181    fn as_slice(&self) -> &[Bound<'py, PyAny>];
182
183    /// Determines if self contains `value`.
184    ///
185    /// This is equivalent to the Python expression `value in self`.
186    fn contains<V>(&self, value: V) -> PyResult<bool>
187    where
188        V: IntoPyObject<'py>;
189
190    /// Returns the first index `i` for which `self[i] == value`.
191    ///
192    /// This is equivalent to the Python expression `self.index(value)`.
193    fn index<V>(&self, value: V) -> PyResult<usize>
194    where
195        V: IntoPyObject<'py>;
196
197    /// Returns an iterator over the tuple items.
198    fn iter(&self) -> BoundTupleIterator<'py>;
199
200    /// Like [`iter`][PyTupleMethods::iter], but produces an iterator which returns borrowed objects,
201    /// which is a slight performance optimization by avoiding a reference count change.
202    fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py>;
203
204    /// Return a new list containing the contents of this tuple; equivalent to the Python expression `list(tuple)`.
205    ///
206    /// This method is equivalent to `self.as_sequence().to_list()` and faster than `PyList::new(py, self)`.
207    fn to_list(&self) -> Bound<'py, PyList>;
208}
209
210impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
211    fn len(&self) -> usize {
212        unsafe {
213            #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
214            let size = ffi::PyTuple_GET_SIZE(self.as_ptr());
215            #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
216            let size = ffi::PyTuple_Size(self.as_ptr());
217            // non-negative Py_ssize_t should always fit into Rust uint
218            size as usize
219        }
220    }
221
222    fn is_empty(&self) -> bool {
223        self.len() == 0
224    }
225
226    fn as_sequence(&self) -> &Bound<'py, PySequence> {
227        unsafe { self.cast_unchecked() }
228    }
229
230    fn into_sequence(self) -> Bound<'py, PySequence> {
231        unsafe { self.cast_into_unchecked() }
232    }
233
234    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple> {
235        unsafe {
236            ffi::PyTuple_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
237                .assume_owned(self.py())
238                .cast_into_unchecked()
239        }
240    }
241
242    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
243        self.get_borrowed_item(index).map(Borrowed::to_owned)
244    }
245
246    fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
247        self.as_borrowed().get_borrowed_item(index)
248    }
249
250    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
251    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
252        unsafe { self.get_borrowed_item_unchecked(index).to_owned() }
253    }
254
255    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
256    unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny> {
257        unsafe { self.as_borrowed().get_borrowed_item_unchecked(index) }
258    }
259
260    #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
261    fn as_slice(&self) -> &[Bound<'py, PyAny>] {
262        // SAFETY: self is known to be a tuple object, and tuples are immutable
263        let items = unsafe { &(*self.as_ptr().cast::<ffi::PyTupleObject>()).ob_item };
264        // SAFETY: Bound<'py, PyAny> has the same memory layout as *mut ffi::PyObject
265        unsafe { std::slice::from_raw_parts(items.as_ptr().cast(), self.len()) }
266    }
267
268    #[inline]
269    fn contains<V>(&self, value: V) -> PyResult<bool>
270    where
271        V: IntoPyObject<'py>,
272    {
273        self.as_sequence().contains(value)
274    }
275
276    #[inline]
277    fn index<V>(&self, value: V) -> PyResult<usize>
278    where
279        V: IntoPyObject<'py>,
280    {
281        self.as_sequence().index(value)
282    }
283
284    fn iter(&self) -> BoundTupleIterator<'py> {
285        BoundTupleIterator::new(self.clone())
286    }
287
288    fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
289        self.as_borrowed().iter_borrowed()
290    }
291
292    fn to_list(&self) -> Bound<'py, PyList> {
293        self.as_sequence()
294            .to_list()
295            .expect("failed to convert tuple to list")
296    }
297}
298
299impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
300    fn get_borrowed_item(self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
301        unsafe {
302            ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
303                .assume_borrowed_or_err(self.py())
304        }
305    }
306
307    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
308    unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
309        unsafe {
310            ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t).assume_borrowed(self.py())
311        }
312    }
313
314    pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
315        BorrowedTupleIterator::new(self)
316    }
317}
318
319/// Used by `PyTuple::into_iter()`.
320pub struct BoundTupleIterator<'py> {
321    tuple: Bound<'py, PyTuple>,
322    index: usize,
323    length: usize,
324}
325
326impl<'py> BoundTupleIterator<'py> {
327    fn new(tuple: Bound<'py, PyTuple>) -> Self {
328        let length = tuple.len();
329        BoundTupleIterator {
330            tuple,
331            index: 0,
332            length,
333        }
334    }
335}
336
337impl<'py> Iterator for BoundTupleIterator<'py> {
338    type Item = Bound<'py, PyAny>;
339
340    #[inline]
341    fn next(&mut self) -> Option<Self::Item> {
342        if self.index < self.length {
343            let item = unsafe {
344                BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.index).to_owned()
345            };
346            self.index += 1;
347            Some(item)
348        } else {
349            None
350        }
351    }
352
353    #[inline]
354    fn size_hint(&self) -> (usize, Option<usize>) {
355        let len = self.len();
356        (len, Some(len))
357    }
358
359    #[inline]
360    fn count(self) -> usize
361    where
362        Self: Sized,
363    {
364        self.len()
365    }
366
367    #[inline]
368    fn last(mut self) -> Option<Self::Item>
369    where
370        Self: Sized,
371    {
372        self.next_back()
373    }
374
375    #[inline]
376    #[cfg(not(feature = "nightly"))]
377    fn nth(&mut self, n: usize) -> Option<Self::Item> {
378        let length = self.length.min(self.tuple.len());
379        let target_index = self.index + n;
380        if target_index < length {
381            let item = unsafe {
382                BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), target_index).to_owned()
383            };
384            self.index = target_index + 1;
385            Some(item)
386        } else {
387            None
388        }
389    }
390
391    #[inline]
392    #[cfg(feature = "nightly")]
393    fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
394        let max_len = self.length.min(self.tuple.len());
395        let currently_at = self.index;
396        if currently_at >= max_len {
397            if n == 0 {
398                return Ok(());
399            } else {
400                return Err(unsafe { NonZero::new_unchecked(n) });
401            }
402        }
403
404        let items_left = max_len - currently_at;
405        if n <= items_left {
406            self.index += n;
407            Ok(())
408        } else {
409            self.index = max_len;
410            let remainder = n - items_left;
411            Err(unsafe { NonZero::new_unchecked(remainder) })
412        }
413    }
414}
415
416impl DoubleEndedIterator for BoundTupleIterator<'_> {
417    #[inline]
418    fn next_back(&mut self) -> Option<Self::Item> {
419        if self.index < self.length {
420            let item = unsafe {
421                BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.length - 1)
422                    .to_owned()
423            };
424            self.length -= 1;
425            Some(item)
426        } else {
427            None
428        }
429    }
430
431    #[inline]
432    #[cfg(not(feature = "nightly"))]
433    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
434        let length_size = self.length.min(self.tuple.len());
435        if self.index + n < length_size {
436            let target_index = length_size - n - 1;
437            let item = unsafe {
438                BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), target_index).to_owned()
439            };
440            self.length = target_index;
441            Some(item)
442        } else {
443            None
444        }
445    }
446
447    #[inline]
448    #[cfg(feature = "nightly")]
449    fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
450        let max_len = self.length.min(self.tuple.len());
451        let currently_at = self.index;
452        if currently_at >= max_len {
453            if n == 0 {
454                return Ok(());
455            } else {
456                return Err(unsafe { NonZero::new_unchecked(n) });
457            }
458        }
459
460        let items_left = max_len - currently_at;
461        if n <= items_left {
462            self.length = max_len - n;
463            Ok(())
464        } else {
465            self.length = currently_at;
466            let remainder = n - items_left;
467            Err(unsafe { NonZero::new_unchecked(remainder) })
468        }
469    }
470}
471
472impl ExactSizeIterator for BoundTupleIterator<'_> {
473    fn len(&self) -> usize {
474        self.length.saturating_sub(self.index)
475    }
476}
477
478impl FusedIterator for BoundTupleIterator<'_> {}
479
480impl<'py> IntoIterator for Bound<'py, PyTuple> {
481    type Item = Bound<'py, PyAny>;
482    type IntoIter = BoundTupleIterator<'py>;
483
484    fn into_iter(self) -> Self::IntoIter {
485        BoundTupleIterator::new(self)
486    }
487}
488
489impl<'py> IntoIterator for &Bound<'py, PyTuple> {
490    type Item = Bound<'py, PyAny>;
491    type IntoIter = BoundTupleIterator<'py>;
492
493    fn into_iter(self) -> Self::IntoIter {
494        self.iter()
495    }
496}
497
498/// Used by `PyTuple::iter_borrowed()`.
499pub struct BorrowedTupleIterator<'a, 'py> {
500    tuple: Borrowed<'a, 'py, PyTuple>,
501    index: usize,
502    length: usize,
503}
504
505impl<'a, 'py> BorrowedTupleIterator<'a, 'py> {
506    fn new(tuple: Borrowed<'a, 'py, PyTuple>) -> Self {
507        let length = tuple.len();
508        BorrowedTupleIterator {
509            tuple,
510            index: 0,
511            length,
512        }
513    }
514
515    unsafe fn get_item(
516        tuple: Borrowed<'a, 'py, PyTuple>,
517        index: usize,
518    ) -> Borrowed<'a, 'py, PyAny> {
519        #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
520        let item = tuple.get_borrowed_item(index).expect("tuple.get failed");
521        #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
522        let item = unsafe { tuple.get_borrowed_item_unchecked(index) };
523        item
524    }
525}
526
527impl<'a, 'py> Iterator for BorrowedTupleIterator<'a, 'py> {
528    type Item = Borrowed<'a, 'py, PyAny>;
529
530    #[inline]
531    fn next(&mut self) -> Option<Self::Item> {
532        if self.index < self.length {
533            let item = unsafe { Self::get_item(self.tuple, self.index) };
534            self.index += 1;
535            Some(item)
536        } else {
537            None
538        }
539    }
540
541    #[inline]
542    fn size_hint(&self) -> (usize, Option<usize>) {
543        let len = self.len();
544        (len, Some(len))
545    }
546
547    #[inline]
548    fn count(self) -> usize
549    where
550        Self: Sized,
551    {
552        self.len()
553    }
554
555    #[inline]
556    fn last(mut self) -> Option<Self::Item>
557    where
558        Self: Sized,
559    {
560        self.next_back()
561    }
562}
563
564impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> {
565    #[inline]
566    fn next_back(&mut self) -> Option<Self::Item> {
567        if self.index < self.length {
568            let item = unsafe { Self::get_item(self.tuple, self.length - 1) };
569            self.length -= 1;
570            Some(item)
571        } else {
572            None
573        }
574    }
575}
576
577impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> {
578    fn len(&self) -> usize {
579        self.length.saturating_sub(self.index)
580    }
581}
582
583impl FusedIterator for BorrowedTupleIterator<'_, '_> {}
584
585#[cold]
586fn wrong_tuple_length(t: &Bound<'_, PyTuple>, expected_length: usize) -> PyErr {
587    let msg = format!(
588        "expected tuple of length {}, but got tuple of length {}",
589        expected_length,
590        t.len()
591    );
592    exceptions::PyValueError::new_err(msg)
593}
594
595macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
596    impl <'py, $($T),+> IntoPyObject<'py> for ($($T,)+)
597    where
598        $($T: IntoPyObject<'py>,)+
599    {
600        type Target = PyTuple;
601        type Output = Bound<'py, Self::Target>;
602        type Error = PyErr;
603
604        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
605            Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
606        }
607
608        #[cfg(feature = "experimental-inspect")]
609        fn type_output() -> TypeInfo {
610            TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
611        }
612    }
613
614    impl <'a, 'py, $($T),+> IntoPyObject<'py> for &'a ($($T,)+)
615    where
616        $(&'a $T: IntoPyObject<'py>,)+
617        $($T: 'a,)+ // MSRV
618    {
619        type Target = PyTuple;
620        type Output = Bound<'py, Self::Target>;
621        type Error = PyErr;
622
623        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
624            Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
625        }
626
627        #[cfg(feature = "experimental-inspect")]
628        fn type_output() -> TypeInfo {
629            TypeInfo::Tuple(Some(vec![$( <&$T>::type_output() ),+]))
630        }
631    }
632
633    impl<'py, $($T),+> crate::call::private::Sealed for ($($T,)+) where $($T: IntoPyObject<'py>,)+ {}
634    impl<'py, $($T),+> crate::call::PyCallArgs<'py> for ($($T,)+)
635    where
636        $($T: IntoPyObject<'py>,)+
637    {
638        #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
639        fn call(
640            self,
641            function: Borrowed<'_, 'py, PyAny>,
642            kwargs: Borrowed<'_, '_, crate::types::PyDict>,
643            _: crate::call::private::Token,
644        ) -> PyResult<Bound<'py, PyAny>> {
645            let py = function.py();
646            // We need this to drop the arguments correctly.
647            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
648            // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
649            let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
650            unsafe {
651                ffi::PyObject_VectorcallDict(
652                    function.as_ptr(),
653                    args.as_mut_ptr().add(1),
654                    $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
655                    kwargs.as_ptr(),
656                )
657                .assume_owned_or_err(py)
658            }
659        }
660
661        #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
662        fn call_positional(
663            self,
664            function: Borrowed<'_, 'py, PyAny>,
665            _: crate::call::private::Token,
666        ) -> PyResult<Bound<'py, PyAny>> {
667            let py = function.py();
668            // We need this to drop the arguments correctly.
669            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
670
671            #[cfg(not(Py_LIMITED_API))]
672            if $length == 1 {
673                return unsafe {
674                    ffi::PyObject_CallOneArg(
675                       function.as_ptr(),
676                       args_bound[0].as_ptr()
677                    )
678                    .assume_owned_or_err(py)
679                };
680            }
681
682            // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
683            let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
684            unsafe {
685                ffi::PyObject_Vectorcall(
686                    function.as_ptr(),
687                    args.as_mut_ptr().add(1),
688                    $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
689                    std::ptr::null_mut(),
690                )
691                .assume_owned_or_err(py)
692            }
693        }
694
695        #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
696        fn call_method_positional(
697            self,
698            object: Borrowed<'_, 'py, PyAny>,
699            method_name: Borrowed<'_, 'py, crate::types::PyString>,
700            _: crate::call::private::Token,
701        ) -> PyResult<Bound<'py, PyAny>> {
702            let py = object.py();
703            // We need this to drop the arguments correctly.
704            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
705
706            #[cfg(not(Py_LIMITED_API))]
707            if $length == 1 {
708                return unsafe {
709                    ffi::PyObject_CallMethodOneArg(
710                            object.as_ptr(),
711                            method_name.as_ptr(),
712                            args_bound[0].as_ptr(),
713                    )
714                    .assume_owned_or_err(py)
715                };
716            }
717
718            let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
719            unsafe {
720                ffi::PyObject_VectorcallMethod(
721                    method_name.as_ptr(),
722                    args.as_mut_ptr(),
723                    // +1 for the receiver.
724                    1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
725                    std::ptr::null_mut(),
726                )
727                .assume_owned_or_err(py)
728            }
729
730        }
731
732        #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
733        fn call(
734            self,
735            function: Borrowed<'_, 'py, PyAny>,
736            kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
737            token: crate::call::private::Token,
738        ) -> PyResult<Bound<'py, PyAny>> {
739            self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
740        }
741
742        #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
743        fn call_positional(
744            self,
745            function: Borrowed<'_, 'py, PyAny>,
746            token: crate::call::private::Token,
747        ) -> PyResult<Bound<'py, PyAny>> {
748            self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
749        }
750
751        #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
752        fn call_method_positional(
753            self,
754            object: Borrowed<'_, 'py, PyAny>,
755            method_name: Borrowed<'_, 'py, crate::types::PyString>,
756            token: crate::call::private::Token,
757        ) -> PyResult<Bound<'py, PyAny>> {
758            self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
759        }
760    }
761
762    impl<'a, 'py, $($T),+> crate::call::private::Sealed for &'a ($($T,)+) where $(&'a $T: IntoPyObject<'py>,)+ $($T: 'a,)+ /*MSRV */ {}
763    impl<'a, 'py, $($T),+> crate::call::PyCallArgs<'py> for &'a ($($T,)+)
764    where
765        $(&'a $T: IntoPyObject<'py>,)+
766        $($T: 'a,)+ // MSRV
767    {
768        #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
769        fn call(
770            self,
771            function: Borrowed<'_, 'py, PyAny>,
772            kwargs: Borrowed<'_, '_, crate::types::PyDict>,
773            _: crate::call::private::Token,
774        ) -> PyResult<Bound<'py, PyAny>> {
775            let py = function.py();
776            // We need this to drop the arguments correctly.
777            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
778            // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
779            let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
780            unsafe {
781                ffi::PyObject_VectorcallDict(
782                    function.as_ptr(),
783                    args.as_mut_ptr().add(1),
784                    $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
785                    kwargs.as_ptr(),
786                )
787                .assume_owned_or_err(py)
788            }
789        }
790
791        #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
792        fn call_positional(
793            self,
794            function: Borrowed<'_, 'py, PyAny>,
795            _: crate::call::private::Token,
796        ) -> PyResult<Bound<'py, PyAny>> {
797            let py = function.py();
798            // We need this to drop the arguments correctly.
799            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
800
801            #[cfg(not(Py_LIMITED_API))]
802            if $length == 1 {
803                return unsafe {
804                    ffi::PyObject_CallOneArg(
805                       function.as_ptr(),
806                       args_bound[0].as_ptr()
807                    )
808                    .assume_owned_or_err(py)
809                };
810            }
811
812            // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
813            let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
814            unsafe {
815                ffi::PyObject_Vectorcall(
816                    function.as_ptr(),
817                    args.as_mut_ptr().add(1),
818                    $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
819                    std::ptr::null_mut(),
820                )
821                .assume_owned_or_err(py)
822            }
823        }
824
825        #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
826        fn call_method_positional(
827            self,
828            object: Borrowed<'_, 'py, PyAny>,
829            method_name: Borrowed<'_, 'py, crate::types::PyString>,
830            _: crate::call::private::Token,
831        ) -> PyResult<Bound<'py, PyAny>> {
832            let py = object.py();
833            // We need this to drop the arguments correctly.
834            let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
835
836            #[cfg(not(Py_LIMITED_API))]
837            if $length == 1 {
838                return unsafe {
839                    ffi::PyObject_CallMethodOneArg(
840                            object.as_ptr(),
841                            method_name.as_ptr(),
842                            args_bound[0].as_ptr(),
843                    )
844                    .assume_owned_or_err(py)
845                };
846            }
847
848            let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
849            unsafe {
850                ffi::PyObject_VectorcallMethod(
851                    method_name.as_ptr(),
852                    args.as_mut_ptr(),
853                    // +1 for the receiver.
854                    1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
855                    std::ptr::null_mut(),
856                )
857                .assume_owned_or_err(py)
858            }
859        }
860
861        #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
862        fn call(
863            self,
864            function: Borrowed<'_, 'py, PyAny>,
865            kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
866            token: crate::call::private::Token,
867        ) -> PyResult<Bound<'py, PyAny>> {
868            self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
869        }
870
871        #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
872        fn call_positional(
873            self,
874            function: Borrowed<'_, 'py, PyAny>,
875            token: crate::call::private::Token,
876        ) -> PyResult<Bound<'py, PyAny>> {
877            self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
878        }
879
880        #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
881        fn call_method_positional(
882            self,
883            object: Borrowed<'_, 'py, PyAny>,
884            method_name: Borrowed<'_, 'py, crate::types::PyString>,
885            token: crate::call::private::Token,
886        ) -> PyResult<Bound<'py, PyAny>> {
887            self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
888        }
889    }
890
891    impl<'py, $($T: FromPyObject<'py>),+> FromPyObject<'py> for ($($T,)+) {
892        fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self>
893        {
894            let t = obj.cast::<PyTuple>()?;
895            if t.len() == $length {
896                #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
897                return Ok(($(t.get_borrowed_item($n)?.extract::<$T>()?,)+));
898
899                #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
900                unsafe {return Ok(($(t.get_borrowed_item_unchecked($n).extract::<$T>()?,)+));}
901            } else {
902                Err(wrong_tuple_length(t, $length))
903            }
904        }
905
906        #[cfg(feature = "experimental-inspect")]
907        fn type_input() -> TypeInfo {
908            TypeInfo::Tuple(Some(vec![$( $T::type_input() ),+]))
909        }
910    }
911});
912
913fn array_into_tuple<'py, const N: usize>(
914    py: Python<'py>,
915    array: [Bound<'py, PyAny>; N],
916) -> Bound<'py, PyTuple> {
917    unsafe {
918        let ptr = ffi::PyTuple_New(N.try_into().expect("0 < N <= 12"));
919        let tup = ptr.assume_owned(py).cast_into_unchecked();
920        for (index, obj) in array.into_iter().enumerate() {
921            #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
922            ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
923            #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
924            ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
925        }
926        tup
927    }
928}
929
930tuple_conversion!(1, (ref0, 0, T0));
931tuple_conversion!(2, (ref0, 0, T0), (ref1, 1, T1));
932tuple_conversion!(3, (ref0, 0, T0), (ref1, 1, T1), (ref2, 2, T2));
933tuple_conversion!(
934    4,
935    (ref0, 0, T0),
936    (ref1, 1, T1),
937    (ref2, 2, T2),
938    (ref3, 3, T3)
939);
940tuple_conversion!(
941    5,
942    (ref0, 0, T0),
943    (ref1, 1, T1),
944    (ref2, 2, T2),
945    (ref3, 3, T3),
946    (ref4, 4, T4)
947);
948tuple_conversion!(
949    6,
950    (ref0, 0, T0),
951    (ref1, 1, T1),
952    (ref2, 2, T2),
953    (ref3, 3, T3),
954    (ref4, 4, T4),
955    (ref5, 5, T5)
956);
957tuple_conversion!(
958    7,
959    (ref0, 0, T0),
960    (ref1, 1, T1),
961    (ref2, 2, T2),
962    (ref3, 3, T3),
963    (ref4, 4, T4),
964    (ref5, 5, T5),
965    (ref6, 6, T6)
966);
967tuple_conversion!(
968    8,
969    (ref0, 0, T0),
970    (ref1, 1, T1),
971    (ref2, 2, T2),
972    (ref3, 3, T3),
973    (ref4, 4, T4),
974    (ref5, 5, T5),
975    (ref6, 6, T6),
976    (ref7, 7, T7)
977);
978tuple_conversion!(
979    9,
980    (ref0, 0, T0),
981    (ref1, 1, T1),
982    (ref2, 2, T2),
983    (ref3, 3, T3),
984    (ref4, 4, T4),
985    (ref5, 5, T5),
986    (ref6, 6, T6),
987    (ref7, 7, T7),
988    (ref8, 8, T8)
989);
990tuple_conversion!(
991    10,
992    (ref0, 0, T0),
993    (ref1, 1, T1),
994    (ref2, 2, T2),
995    (ref3, 3, T3),
996    (ref4, 4, T4),
997    (ref5, 5, T5),
998    (ref6, 6, T6),
999    (ref7, 7, T7),
1000    (ref8, 8, T8),
1001    (ref9, 9, T9)
1002);
1003tuple_conversion!(
1004    11,
1005    (ref0, 0, T0),
1006    (ref1, 1, T1),
1007    (ref2, 2, T2),
1008    (ref3, 3, T3),
1009    (ref4, 4, T4),
1010    (ref5, 5, T5),
1011    (ref6, 6, T6),
1012    (ref7, 7, T7),
1013    (ref8, 8, T8),
1014    (ref9, 9, T9),
1015    (ref10, 10, T10)
1016);
1017
1018tuple_conversion!(
1019    12,
1020    (ref0, 0, T0),
1021    (ref1, 1, T1),
1022    (ref2, 2, T2),
1023    (ref3, 3, T3),
1024    (ref4, 4, T4),
1025    (ref5, 5, T5),
1026    (ref6, 6, T6),
1027    (ref7, 7, T7),
1028    (ref8, 8, T8),
1029    (ref9, 9, T9),
1030    (ref10, 10, T10),
1031    (ref11, 11, T11)
1032);
1033
1034#[cfg(test)]
1035mod tests {
1036    use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyList, PyTuple};
1037    use crate::{IntoPyObject, Python};
1038    use std::collections::HashSet;
1039    #[cfg(feature = "nightly")]
1040    use std::num::NonZero;
1041    use std::ops::Range;
1042    #[test]
1043    fn test_new() {
1044        Python::attach(|py| {
1045            let ob = PyTuple::new(py, [1, 2, 3]).unwrap();
1046            assert_eq!(3, ob.len());
1047            let ob = ob.as_any();
1048            assert_eq!((1, 2, 3), ob.extract().unwrap());
1049
1050            let mut map = HashSet::new();
1051            map.insert(1);
1052            map.insert(2);
1053            PyTuple::new(py, map).unwrap();
1054        });
1055    }
1056
1057    #[test]
1058    fn test_len() {
1059        Python::attach(|py| {
1060            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1061            let tuple = ob.cast::<PyTuple>().unwrap();
1062            assert_eq!(3, tuple.len());
1063            assert!(!tuple.is_empty());
1064            let ob = tuple.as_any();
1065            assert_eq!((1, 2, 3), ob.extract().unwrap());
1066        });
1067    }
1068
1069    #[test]
1070    fn test_empty() {
1071        Python::attach(|py| {
1072            let tuple = PyTuple::empty(py);
1073            assert!(tuple.is_empty());
1074            assert_eq!(0, tuple.len());
1075        });
1076    }
1077
1078    #[test]
1079    fn test_slice() {
1080        Python::attach(|py| {
1081            let tup = PyTuple::new(py, [2, 3, 5, 7]).unwrap();
1082            let slice = tup.get_slice(1, 3);
1083            assert_eq!(2, slice.len());
1084            let slice = tup.get_slice(1, 7);
1085            assert_eq!(3, slice.len());
1086        });
1087    }
1088
1089    #[test]
1090    fn test_iter() {
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            let mut iter = tuple.iter();
1096
1097            assert_eq!(iter.size_hint(), (3, Some(3)));
1098
1099            assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1100            assert_eq!(iter.size_hint(), (2, Some(2)));
1101
1102            assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1103            assert_eq!(iter.size_hint(), (1, Some(1)));
1104
1105            assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1106            assert_eq!(iter.size_hint(), (0, Some(0)));
1107
1108            assert!(iter.next().is_none());
1109            assert!(iter.next().is_none());
1110        });
1111    }
1112
1113    #[test]
1114    fn test_iter_rev() {
1115        Python::attach(|py| {
1116            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1117            let tuple = ob.cast::<PyTuple>().unwrap();
1118            assert_eq!(3, tuple.len());
1119            let mut iter = tuple.iter().rev();
1120
1121            assert_eq!(iter.size_hint(), (3, Some(3)));
1122
1123            assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1124            assert_eq!(iter.size_hint(), (2, Some(2)));
1125
1126            assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1127            assert_eq!(iter.size_hint(), (1, Some(1)));
1128
1129            assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1130            assert_eq!(iter.size_hint(), (0, Some(0)));
1131
1132            assert!(iter.next().is_none());
1133            assert!(iter.next().is_none());
1134        });
1135    }
1136
1137    #[test]
1138    fn test_bound_iter() {
1139        Python::attach(|py| {
1140            let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1141            assert_eq!(3, tuple.len());
1142            let mut iter = tuple.iter();
1143
1144            assert_eq!(iter.size_hint(), (3, Some(3)));
1145
1146            assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1147            assert_eq!(iter.size_hint(), (2, Some(2)));
1148
1149            assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1150            assert_eq!(iter.size_hint(), (1, Some(1)));
1151
1152            assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1153            assert_eq!(iter.size_hint(), (0, Some(0)));
1154
1155            assert!(iter.next().is_none());
1156            assert!(iter.next().is_none());
1157        });
1158    }
1159
1160    #[test]
1161    fn test_bound_iter_rev() {
1162        Python::attach(|py| {
1163            let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1164            assert_eq!(3, tuple.len());
1165            let mut iter = tuple.iter().rev();
1166
1167            assert_eq!(iter.size_hint(), (3, Some(3)));
1168
1169            assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1170            assert_eq!(iter.size_hint(), (2, Some(2)));
1171
1172            assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1173            assert_eq!(iter.size_hint(), (1, Some(1)));
1174
1175            assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1176            assert_eq!(iter.size_hint(), (0, Some(0)));
1177
1178            assert!(iter.next().is_none());
1179            assert!(iter.next().is_none());
1180        });
1181    }
1182
1183    #[test]
1184    fn test_into_iter() {
1185        Python::attach(|py| {
1186            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1187            let tuple = ob.cast::<PyTuple>().unwrap();
1188            assert_eq!(3, tuple.len());
1189
1190            for (i, item) in tuple.iter().enumerate() {
1191                assert_eq!(i + 1, item.extract::<'_, usize>().unwrap());
1192            }
1193        });
1194    }
1195
1196    #[test]
1197    fn test_into_iter_bound() {
1198        Python::attach(|py| {
1199            let tuple = (1, 2, 3).into_pyobject(py).unwrap();
1200            assert_eq!(3, tuple.len());
1201
1202            let mut items = vec![];
1203            for item in tuple {
1204                items.push(item.extract::<usize>().unwrap());
1205            }
1206            assert_eq!(items, vec![1, 2, 3]);
1207        });
1208    }
1209
1210    #[test]
1211    #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
1212    fn test_as_slice() {
1213        Python::attach(|py| {
1214            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1215            let tuple = ob.cast::<PyTuple>().unwrap();
1216
1217            let slice = tuple.as_slice();
1218            assert_eq!(3, slice.len());
1219            assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap());
1220            assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap());
1221            assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap());
1222        });
1223    }
1224
1225    #[test]
1226    fn test_tuple_lengths_up_to_12() {
1227        Python::attach(|py| {
1228            let t0 = (0,).into_pyobject(py).unwrap();
1229            let t1 = (0, 1).into_pyobject(py).unwrap();
1230            let t2 = (0, 1, 2).into_pyobject(py).unwrap();
1231            let t3 = (0, 1, 2, 3).into_pyobject(py).unwrap();
1232            let t4 = (0, 1, 2, 3, 4).into_pyobject(py).unwrap();
1233            let t5 = (0, 1, 2, 3, 4, 5).into_pyobject(py).unwrap();
1234            let t6 = (0, 1, 2, 3, 4, 5, 6).into_pyobject(py).unwrap();
1235            let t7 = (0, 1, 2, 3, 4, 5, 6, 7).into_pyobject(py).unwrap();
1236            let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).into_pyobject(py).unwrap();
1237            let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).into_pyobject(py).unwrap();
1238            let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
1239                .into_pyobject(py)
1240                .unwrap();
1241            let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
1242                .into_pyobject(py)
1243                .unwrap();
1244
1245            assert_eq!(t0.extract::<(i32,)>().unwrap(), (0,));
1246            assert_eq!(t1.extract::<(i32, i32)>().unwrap(), (0, 1,));
1247            assert_eq!(t2.extract::<(i32, i32, i32)>().unwrap(), (0, 1, 2,));
1248            assert_eq!(
1249                t3.extract::<(i32, i32, i32, i32,)>().unwrap(),
1250                (0, 1, 2, 3,)
1251            );
1252            assert_eq!(
1253                t4.extract::<(i32, i32, i32, i32, i32,)>().unwrap(),
1254                (0, 1, 2, 3, 4,)
1255            );
1256            assert_eq!(
1257                t5.extract::<(i32, i32, i32, i32, i32, i32,)>().unwrap(),
1258                (0, 1, 2, 3, 4, 5,)
1259            );
1260            assert_eq!(
1261                t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>()
1262                    .unwrap(),
1263                (0, 1, 2, 3, 4, 5, 6,)
1264            );
1265            assert_eq!(
1266                t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>()
1267                    .unwrap(),
1268                (0, 1, 2, 3, 4, 5, 6, 7,)
1269            );
1270            assert_eq!(
1271                t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1272                    .unwrap(),
1273                (0, 1, 2, 3, 4, 5, 6, 7, 8,)
1274            );
1275            assert_eq!(
1276                t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1277                    .unwrap(),
1278                (0, 1, 2, 3, 4, 5, 6, 7, 8, 9,)
1279            );
1280            assert_eq!(
1281                t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1282                    .unwrap(),
1283                (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,)
1284            );
1285            assert_eq!(
1286                t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1287                    .unwrap(),
1288                (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,)
1289            );
1290        })
1291    }
1292
1293    #[test]
1294    fn test_tuple_get_item_invalid_index() {
1295        Python::attach(|py| {
1296            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1297            let tuple = ob.cast::<PyTuple>().unwrap();
1298            let obj = tuple.get_item(5);
1299            assert!(obj.is_err());
1300            assert_eq!(
1301                obj.unwrap_err().to_string(),
1302                "IndexError: tuple index out of range"
1303            );
1304        });
1305    }
1306
1307    #[test]
1308    fn test_tuple_get_item_sanity() {
1309        Python::attach(|py| {
1310            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1311            let tuple = ob.cast::<PyTuple>().unwrap();
1312            let obj = tuple.get_item(0);
1313            assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
1314        });
1315    }
1316
1317    #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1318    #[test]
1319    fn test_tuple_get_item_unchecked_sanity() {
1320        Python::attach(|py| {
1321            let ob = (1, 2, 3).into_pyobject(py).unwrap();
1322            let tuple = ob.cast::<PyTuple>().unwrap();
1323            let obj = unsafe { tuple.get_item_unchecked(0) };
1324            assert_eq!(obj.extract::<i32>().unwrap(), 1);
1325        });
1326    }
1327
1328    #[test]
1329    fn test_tuple_contains() {
1330        Python::attach(|py| {
1331            let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1332            let tuple = ob.cast::<PyTuple>().unwrap();
1333            assert_eq!(6, tuple.len());
1334
1335            let bad_needle = 7i32.into_pyobject(py).unwrap();
1336            assert!(!tuple.contains(&bad_needle).unwrap());
1337
1338            let good_needle = 8i32.into_pyobject(py).unwrap();
1339            assert!(tuple.contains(&good_needle).unwrap());
1340
1341            let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
1342            assert!(tuple.contains(&type_coerced_needle).unwrap());
1343        });
1344    }
1345
1346    #[test]
1347    fn test_tuple_index() {
1348        Python::attach(|py| {
1349            let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1350            let tuple = ob.cast::<PyTuple>().unwrap();
1351            assert_eq!(0, tuple.index(1i32).unwrap());
1352            assert_eq!(2, tuple.index(2i32).unwrap());
1353            assert_eq!(3, tuple.index(3i32).unwrap());
1354            assert_eq!(4, tuple.index(5i32).unwrap());
1355            assert_eq!(5, tuple.index(8i32).unwrap());
1356            assert!(tuple.index(42i32).is_err());
1357        });
1358    }
1359
1360    // An iterator that lies about its `ExactSizeIterator` implementation.
1361    // See https://github.com/PyO3/pyo3/issues/2118
1362    struct FaultyIter(Range<usize>, usize);
1363
1364    impl Iterator for FaultyIter {
1365        type Item = usize;
1366
1367        fn next(&mut self) -> Option<Self::Item> {
1368            self.0.next()
1369        }
1370    }
1371
1372    impl ExactSizeIterator for FaultyIter {
1373        fn len(&self) -> usize {
1374            self.1
1375        }
1376    }
1377
1378    #[test]
1379    #[should_panic(
1380        expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1381    )]
1382    fn too_long_iterator() {
1383        Python::attach(|py| {
1384            let iter = FaultyIter(0..usize::MAX, 73);
1385            let _tuple = PyTuple::new(py, iter);
1386        })
1387    }
1388
1389    #[test]
1390    #[should_panic(
1391        expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1392    )]
1393    fn too_short_iterator() {
1394        Python::attach(|py| {
1395            let iter = FaultyIter(0..35, 73);
1396            let _tuple = PyTuple::new(py, iter);
1397        })
1398    }
1399
1400    #[test]
1401    #[should_panic(
1402        expected = "out of range integral type conversion attempted on `elements.len()`"
1403    )]
1404    fn overflowing_size() {
1405        Python::attach(|py| {
1406            let iter = FaultyIter(0..0, usize::MAX);
1407
1408            let _tuple = PyTuple::new(py, iter);
1409        })
1410    }
1411
1412    #[test]
1413    fn bad_intopyobject_doesnt_cause_leaks() {
1414        use crate::types::PyInt;
1415        use std::convert::Infallible;
1416        use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1417
1418        static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1419
1420        struct Bad(usize);
1421
1422        impl Drop for Bad {
1423            fn drop(&mut self) {
1424                NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1425            }
1426        }
1427
1428        impl<'py> IntoPyObject<'py> for Bad {
1429            type Target = PyInt;
1430            type Output = crate::Bound<'py, Self::Target>;
1431            type Error = Infallible;
1432
1433            fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1434                // This panic should not lead to a memory leak
1435                assert_ne!(self.0, 42);
1436                self.0.into_pyobject(py)
1437            }
1438        }
1439
1440        struct FaultyIter(Range<usize>, usize);
1441
1442        impl Iterator for FaultyIter {
1443            type Item = Bad;
1444
1445            fn next(&mut self) -> Option<Self::Item> {
1446                self.0.next().map(|i| {
1447                    NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1448                    Bad(i)
1449                })
1450            }
1451        }
1452
1453        impl ExactSizeIterator for FaultyIter {
1454            fn len(&self) -> usize {
1455                self.1
1456            }
1457        }
1458
1459        Python::attach(|py| {
1460            std::panic::catch_unwind(|| {
1461                let iter = FaultyIter(0..50, 50);
1462                let _tuple = PyTuple::new(py, iter);
1463            })
1464            .unwrap_err();
1465        });
1466
1467        assert_eq!(
1468            NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1469            0,
1470            "Some destructors did not run"
1471        );
1472    }
1473
1474    #[test]
1475    fn bad_intopyobject_doesnt_cause_leaks_2() {
1476        use crate::types::PyInt;
1477        use std::convert::Infallible;
1478        use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1479
1480        static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1481
1482        struct Bad(usize);
1483
1484        impl Drop for Bad {
1485            fn drop(&mut self) {
1486                NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1487            }
1488        }
1489
1490        impl<'py> IntoPyObject<'py> for &Bad {
1491            type Target = PyInt;
1492            type Output = crate::Bound<'py, Self::Target>;
1493            type Error = Infallible;
1494
1495            fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1496                // This panic should not lead to a memory leak
1497                assert_ne!(self.0, 3);
1498                self.0.into_pyobject(py)
1499            }
1500        }
1501
1502        let s = (Bad(1), Bad(2), Bad(3), Bad(4));
1503        NEEDS_DESTRUCTING_COUNT.store(4, SeqCst);
1504        Python::attach(|py| {
1505            std::panic::catch_unwind(|| {
1506                let _tuple = (&s).into_pyobject(py).unwrap();
1507            })
1508            .unwrap_err();
1509        });
1510        drop(s);
1511
1512        assert_eq!(
1513            NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1514            0,
1515            "Some destructors did not run"
1516        );
1517    }
1518
1519    #[test]
1520    fn test_tuple_to_list() {
1521        Python::attach(|py| {
1522            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1523            let list = tuple.to_list();
1524            let list_expected = PyList::new(py, vec![1, 2, 3]).unwrap();
1525            assert!(list.eq(list_expected).unwrap());
1526        })
1527    }
1528
1529    #[test]
1530    fn test_tuple_as_sequence() {
1531        Python::attach(|py| {
1532            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1533            let sequence = tuple.as_sequence();
1534            assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
1535            assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1536
1537            assert_eq!(tuple.len(), 3);
1538            assert_eq!(sequence.len().unwrap(), 3);
1539        })
1540    }
1541
1542    #[test]
1543    fn test_tuple_into_sequence() {
1544        Python::attach(|py| {
1545            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1546            let sequence = tuple.into_sequence();
1547            assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1548            assert_eq!(sequence.len().unwrap(), 3);
1549        })
1550    }
1551
1552    #[test]
1553    fn test_bound_tuple_get_item() {
1554        Python::attach(|py| {
1555            let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1556
1557            assert_eq!(tuple.len(), 4);
1558            assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
1559            assert_eq!(
1560                tuple
1561                    .get_borrowed_item(1)
1562                    .unwrap()
1563                    .extract::<i32>()
1564                    .unwrap(),
1565                2
1566            );
1567            #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1568            {
1569                assert_eq!(
1570                    unsafe { tuple.get_item_unchecked(2) }
1571                        .extract::<i32>()
1572                        .unwrap(),
1573                    3
1574                );
1575                assert_eq!(
1576                    unsafe { tuple.get_borrowed_item_unchecked(3) }
1577                        .extract::<i32>()
1578                        .unwrap(),
1579                    4
1580                );
1581            }
1582        })
1583    }
1584
1585    #[test]
1586    fn test_bound_tuple_nth() {
1587        Python::attach(|py| {
1588            let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1589            let mut iter = tuple.iter();
1590            assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 2);
1591            assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 4);
1592            assert!(iter.nth(1).is_none());
1593
1594            let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1595            let mut iter = tuple.iter();
1596            iter.next();
1597            assert!(iter.nth(1).is_none());
1598
1599            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1600            let mut iter = tuple.iter();
1601            assert!(iter.nth(10).is_none());
1602
1603            let tuple = PyTuple::new(py, vec![6, 7, 8, 9, 10]).unwrap();
1604            let mut iter = tuple.iter();
1605            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 6);
1606            assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 9);
1607            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 10);
1608
1609            let mut iter = tuple.iter();
1610            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 9);
1611            assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 8);
1612            assert!(iter.next().is_none());
1613        });
1614    }
1615
1616    #[test]
1617    fn test_bound_tuple_nth_back() {
1618        Python::attach(|py| {
1619            let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1620            let mut iter = tuple.iter();
1621            assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 5);
1622            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1623            assert!(iter.nth_back(2).is_none());
1624
1625            let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1626            let mut iter = tuple.iter();
1627            assert!(iter.nth_back(0).is_none());
1628            assert!(iter.nth_back(1).is_none());
1629
1630            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1631            let mut iter = tuple.iter();
1632            assert!(iter.nth_back(5).is_none());
1633
1634            let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1635            let mut iter = tuple.iter();
1636            iter.next_back(); // Consume the last element
1637            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1638            assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
1639            assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 1);
1640
1641            let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1642            let mut iter = tuple.iter();
1643            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 4);
1644            assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 1);
1645
1646            let mut iter2 = tuple.iter();
1647            iter2.next_back();
1648            assert_eq!(iter2.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1649            assert_eq!(iter2.next_back().unwrap().extract::<i32>().unwrap(), 2);
1650
1651            let mut iter3 = tuple.iter();
1652            iter3.nth(1);
1653            assert_eq!(iter3.nth_back(2).unwrap().extract::<i32>().unwrap(), 3);
1654            assert!(iter3.nth_back(0).is_none());
1655        });
1656    }
1657
1658    #[cfg(feature = "nightly")]
1659    #[test]
1660    fn test_bound_tuple_advance_by() {
1661        Python::attach(|py| {
1662            let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1663            let mut iter = tuple.iter();
1664
1665            assert_eq!(iter.advance_by(2), Ok(()));
1666            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
1667            assert_eq!(iter.advance_by(0), Ok(()));
1668            assert_eq!(iter.advance_by(100), Err(NonZero::new(98).unwrap()));
1669            assert!(iter.next().is_none());
1670
1671            let mut iter2 = tuple.iter();
1672            assert_eq!(iter2.advance_by(6), Err(NonZero::new(1).unwrap()));
1673
1674            let mut iter3 = tuple.iter();
1675            assert_eq!(iter3.advance_by(5), Ok(()));
1676
1677            let mut iter4 = tuple.iter();
1678            assert_eq!(iter4.advance_by(0), Ok(()));
1679            assert_eq!(iter4.next().unwrap().extract::<i32>().unwrap(), 1);
1680        })
1681    }
1682
1683    #[cfg(feature = "nightly")]
1684    #[test]
1685    fn test_bound_tuple_advance_back_by() {
1686        Python::attach(|py| {
1687            let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1688            let mut iter = tuple.iter();
1689
1690            assert_eq!(iter.advance_back_by(2), Ok(()));
1691            assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 3);
1692            assert_eq!(iter.advance_back_by(0), Ok(()));
1693            assert_eq!(iter.advance_back_by(100), Err(NonZero::new(98).unwrap()));
1694            assert!(iter.next_back().is_none());
1695
1696            let mut iter2 = tuple.iter();
1697            assert_eq!(iter2.advance_back_by(6), Err(NonZero::new(1).unwrap()));
1698
1699            let mut iter3 = tuple.iter();
1700            assert_eq!(iter3.advance_back_by(5), Ok(()));
1701
1702            let mut iter4 = tuple.iter();
1703            assert_eq!(iter4.advance_back_by(0), Ok(()));
1704            assert_eq!(iter4.next_back().unwrap().extract::<i32>().unwrap(), 5);
1705        })
1706    }
1707
1708    #[test]
1709    fn test_iter_last() {
1710        Python::attach(|py| {
1711            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1712            let last = tuple.iter().last();
1713            assert_eq!(last.unwrap().extract::<i32>().unwrap(), 3);
1714        })
1715    }
1716
1717    #[test]
1718    fn test_iter_count() {
1719        Python::attach(|py| {
1720            let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1721            assert_eq!(tuple.iter().count(), 3);
1722        })
1723    }
1724}