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