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