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