pyo3/types/
tuple.rs

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