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