Skip to main content

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