Skip to main content

pyo3/types/
tuple.rs

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