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 unsafe { 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 unsafe { 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*mut PyObject::PyTuple_GetItem(self.as_ptr(), arg2: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 unsafe {
333 ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t).assume_borrowed(self.py())
334 }
335 }
336
337 pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
338 BorrowedTupleIterator::new(self)
339 }
340}
341
342/// Used by `PyTuple::into_iter()`.
343pub struct BoundTupleIterator<'py> {
344 tuple: Bound<'py, PyTuple>,
345 index: usize,
346 length: usize,
347}
348
349impl<'py> BoundTupleIterator<'py> {
350 fn new(tuple: Bound<'py, PyTuple>) -> Self {
351 let length: usize = tuple.len();
352 BoundTupleIterator {
353 tuple,
354 index: 0,
355 length,
356 }
357 }
358}
359
360impl<'py> Iterator for BoundTupleIterator<'py> {
361 type Item = Bound<'py, PyAny>;
362
363 #[inline]
364 fn next(&mut self) -> Option<Self::Item> {
365 if self.index < self.length {
366 let item = unsafe {
367 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.index).to_owned()
368 };
369 self.index += 1;
370 Some(item)
371 } else {
372 None
373 }
374 }
375
376 #[inline]
377 fn size_hint(&self) -> (usize, Option<usize>) {
378 let len = self.len();
379 (len, Some(len))
380 }
381
382 #[inline]
383 fn count(self) -> usize
384 where
385 Self: Sized,
386 {
387 self.len()
388 }
389
390 #[inline]
391 fn last(mut self) -> Option<Self::Item>
392 where
393 Self: Sized,
394 {
395 self.next_back()
396 }
397
398 #[inline]
399 #[cfg(not(feature = "nightly"))]
400 fn nth(&mut self, n: usize) -> Option<Self::Item> {
401 let length = self.length.min(self.tuple.len());
402 let target_index = self.index + n;
403 if target_index < length {
404 let item = unsafe {
405 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), target_index).to_owned()
406 };
407 self.index = target_index + 1;
408 Some(item)
409 } else {
410 None
411 }
412 }
413
414 #[inline]
415 #[cfg(feature = "nightly")]
416 fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
417 let max_len = self.length.min(self.tuple.len());
418 let currently_at = self.index;
419 if currently_at >= max_len {
420 if n == 0 {
421 return Ok(());
422 } else {
423 return Err(unsafe { NonZero::new_unchecked(n) });
424 }
425 }
426
427 let items_left = max_len - currently_at;
428 if n <= items_left {
429 self.index += n;
430 Ok(())
431 } else {
432 self.index = max_len;
433 let remainder = n - items_left;
434 Err(unsafe { NonZero::new_unchecked(remainder) })
435 }
436 }
437}
438
439impl DoubleEndedIterator for BoundTupleIterator<'_> {
440 #[inline]
441 fn next_back(&mut self) -> Option<Self::Item> {
442 if self.index < self.length {
443 let item = unsafe {
444 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.length - 1)
445 .to_owned()
446 };
447 self.length -= 1;
448 Some(item)
449 } else {
450 None
451 }
452 }
453
454 #[inline]
455 #[cfg(not(feature = "nightly"))]
456 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
457 let length_size = self.length.min(self.tuple.len());
458 if self.index + n < length_size {
459 let target_index = length_size - n - 1;
460 let item = unsafe {
461 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), target_index).to_owned()
462 };
463 self.length = target_index;
464 Some(item)
465 } else {
466 None
467 }
468 }
469
470 #[inline]
471 #[cfg(feature = "nightly")]
472 fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
473 let max_len = self.length.min(self.tuple.len());
474 let currently_at = self.index;
475 if currently_at >= max_len {
476 if n == 0 {
477 return Ok(());
478 } else {
479 return Err(unsafe { NonZero::new_unchecked(n) });
480 }
481 }
482
483 let items_left = max_len - currently_at;
484 if n <= items_left {
485 self.length = max_len - n;
486 Ok(())
487 } else {
488 self.length = currently_at;
489 let remainder = n - items_left;
490 Err(unsafe { NonZero::new_unchecked(remainder) })
491 }
492 }
493}
494
495impl ExactSizeIterator for BoundTupleIterator<'_> {
496 fn len(&self) -> usize {
497 self.length.saturating_sub(self.index)
498 }
499}
500
501impl FusedIterator for BoundTupleIterator<'_> {}
502
503impl<'py> IntoIterator for Bound<'py, PyTuple> {
504 type Item = Bound<'py, PyAny>;
505 type IntoIter = BoundTupleIterator<'py>;
506
507 fn into_iter(self) -> Self::IntoIter {
508 BoundTupleIterator::new(self)
509 }
510}
511
512impl<'py> IntoIterator for &Bound<'py, PyTuple> {
513 type Item = Bound<'py, PyAny>;
514 type IntoIter = BoundTupleIterator<'py>;
515
516 fn into_iter(self) -> Self::IntoIter {
517 self.iter()
518 }
519}
520
521/// Used by `PyTuple::iter_borrowed()`.
522pub struct BorrowedTupleIterator<'a, 'py> {
523 tuple: Borrowed<'a, 'py, PyTuple>,
524 index: usize,
525 length: usize,
526}
527
528impl<'a, 'py> BorrowedTupleIterator<'a, 'py> {
529 fn new(tuple: Borrowed<'a, 'py, PyTuple>) -> Self {
530 let length: usize = tuple.len();
531 BorrowedTupleIterator {
532 tuple,
533 index: 0,
534 length,
535 }
536 }
537
538 unsafe fn get_item(
539 tuple: Borrowed<'a, 'py, PyTuple>,
540 index: usize,
541 ) -> Borrowed<'a, 'py, PyAny> {
542 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
543 let item: Borrowed<'a, 'py, PyAny> = tuple.get_borrowed_item(index).expect(msg:"tuple.get failed");
544 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
545 let item = unsafe { tuple.get_borrowed_item_unchecked(index) };
546 item
547 }
548}
549
550impl<'a, 'py> Iterator for BorrowedTupleIterator<'a, 'py> {
551 type Item = Borrowed<'a, 'py, PyAny>;
552
553 #[inline]
554 fn next(&mut self) -> Option<Self::Item> {
555 if self.index < self.length {
556 let item = unsafe { Self::get_item(self.tuple, self.index) };
557 self.index += 1;
558 Some(item)
559 } else {
560 None
561 }
562 }
563
564 #[inline]
565 fn size_hint(&self) -> (usize, Option<usize>) {
566 let len = self.len();
567 (len, Some(len))
568 }
569
570 #[inline]
571 fn count(self) -> usize
572 where
573 Self: Sized,
574 {
575 self.len()
576 }
577
578 #[inline]
579 fn last(mut self) -> Option<Self::Item>
580 where
581 Self: Sized,
582 {
583 self.next_back()
584 }
585}
586
587impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> {
588 #[inline]
589 fn next_back(&mut self) -> Option<Self::Item> {
590 if self.index < self.length {
591 let item: Borrowed<'_, '_, PyAny> = unsafe { Self::get_item(self.tuple, self.length - 1) };
592 self.length -= 1;
593 Some(item)
594 } else {
595 None
596 }
597 }
598}
599
600impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> {
601 fn len(&self) -> usize {
602 self.length.saturating_sub(self.index)
603 }
604}
605
606impl FusedIterator for BorrowedTupleIterator<'_, '_> {}
607
608#[allow(deprecated)]
609impl IntoPy<Py<PyTuple>> for Bound<'_, PyTuple> {
610 fn into_py(self, _: Python<'_>) -> Py<PyTuple> {
611 self.unbind()
612 }
613}
614
615#[allow(deprecated)]
616impl IntoPy<Py<PyTuple>> for &'_ Bound<'_, PyTuple> {
617 fn into_py(self, _: Python<'_>) -> Py<PyTuple> {
618 self.clone().unbind()
619 }
620}
621
622#[cold]
623fn wrong_tuple_length(t: &Bound<'_, PyTuple>, expected_length: usize) -> PyErr {
624 let msg: String = format!(
625 "expected tuple of length {}, but got tuple of length {}",
626 expected_length,
627 t.len()
628 );
629 exceptions::PyValueError::new_err(args:msg)
630}
631
632macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
633 #[allow(deprecated)]
634 impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
635 fn to_object(&self, py: Python<'_>) -> PyObject {
636 array_into_tuple(py, [$(self.$n.to_object(py).into_bound(py)),+]).into()
637 }
638 }
639
640 #[allow(deprecated)]
641 impl <$($T: IntoPy<PyObject>),+> IntoPy<PyObject> for ($($T,)+) {
642 fn into_py(self, py: Python<'_>) -> PyObject {
643 array_into_tuple(py, [$(self.$n.into_py(py).into_bound(py)),+]).into()
644 }
645 }
646
647 impl <'py, $($T),+> IntoPyObject<'py> for ($($T,)+)
648 where
649 $($T: IntoPyObject<'py>,)+
650 {
651 type Target = PyTuple;
652 type Output = Bound<'py, Self::Target>;
653 type Error = PyErr;
654
655 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
656 Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
657 }
658
659 #[cfg(feature = "experimental-inspect")]
660 fn type_output() -> TypeInfo {
661 TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
662 }
663 }
664
665 impl <'a, 'py, $($T),+> IntoPyObject<'py> for &'a ($($T,)+)
666 where
667 $(&'a $T: IntoPyObject<'py>,)+
668 $($T: 'a,)+ // MSRV
669 {
670 type Target = PyTuple;
671 type Output = Bound<'py, Self::Target>;
672 type Error = PyErr;
673
674 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
675 Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
676 }
677
678 #[cfg(feature = "experimental-inspect")]
679 fn type_output() -> TypeInfo {
680 TypeInfo::Tuple(Some(vec![$( <&$T>::type_output() ),+]))
681 }
682 }
683
684 impl<'py, $($T),+> crate::call::private::Sealed for ($($T,)+) where $($T: IntoPyObject<'py>,)+ {}
685 impl<'py, $($T),+> crate::call::PyCallArgs<'py> for ($($T,)+)
686 where
687 $($T: IntoPyObject<'py>,)+
688 {
689 #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
690 fn call(
691 self,
692 function: Borrowed<'_, 'py, PyAny>,
693 kwargs: Borrowed<'_, '_, crate::types::PyDict>,
694 _: crate::call::private::Token,
695 ) -> PyResult<Bound<'py, PyAny>> {
696 let py = function.py();
697 // We need this to drop the arguments correctly.
698 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
699 // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
700 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
701 unsafe {
702 ffi::PyObject_VectorcallDict(
703 function.as_ptr(),
704 args.as_mut_ptr().add(1),
705 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
706 kwargs.as_ptr(),
707 )
708 .assume_owned_or_err(py)
709 }
710 }
711
712 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
713 fn call_positional(
714 self,
715 function: Borrowed<'_, 'py, PyAny>,
716 _: crate::call::private::Token,
717 ) -> PyResult<Bound<'py, PyAny>> {
718 let py = function.py();
719 // We need this to drop the arguments correctly.
720 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
721
722 #[cfg(not(Py_LIMITED_API))]
723 if $length == 1 {
724 return unsafe {
725 ffi::PyObject_CallOneArg(
726 function.as_ptr(),
727 args_bound[0].as_ptr()
728 )
729 .assume_owned_or_err(py)
730 };
731 }
732
733 // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
734 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
735 unsafe {
736 ffi::PyObject_Vectorcall(
737 function.as_ptr(),
738 args.as_mut_ptr().add(1),
739 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
740 std::ptr::null_mut(),
741 )
742 .assume_owned_or_err(py)
743 }
744 }
745
746 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
747 fn call_method_positional(
748 self,
749 object: Borrowed<'_, 'py, PyAny>,
750 method_name: Borrowed<'_, 'py, crate::types::PyString>,
751 _: crate::call::private::Token,
752 ) -> PyResult<Bound<'py, PyAny>> {
753 let py = object.py();
754 // We need this to drop the arguments correctly.
755 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
756
757 #[cfg(not(Py_LIMITED_API))]
758 if $length == 1 {
759 return unsafe {
760 ffi::PyObject_CallMethodOneArg(
761 object.as_ptr(),
762 method_name.as_ptr(),
763 args_bound[0].as_ptr(),
764 )
765 .assume_owned_or_err(py)
766 };
767 }
768
769 let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
770 unsafe {
771 ffi::PyObject_VectorcallMethod(
772 method_name.as_ptr(),
773 args.as_mut_ptr(),
774 // +1 for the receiver.
775 1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
776 std::ptr::null_mut(),
777 )
778 .assume_owned_or_err(py)
779 }
780
781 }
782
783 #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
784 fn call(
785 self,
786 function: Borrowed<'_, 'py, PyAny>,
787 kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
788 token: crate::call::private::Token,
789 ) -> PyResult<Bound<'py, PyAny>> {
790 self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
791 }
792
793 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
794 fn call_positional(
795 self,
796 function: Borrowed<'_, 'py, PyAny>,
797 token: crate::call::private::Token,
798 ) -> PyResult<Bound<'py, PyAny>> {
799 self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
800 }
801
802 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
803 fn call_method_positional(
804 self,
805 object: Borrowed<'_, 'py, PyAny>,
806 method_name: Borrowed<'_, 'py, crate::types::PyString>,
807 token: crate::call::private::Token,
808 ) -> PyResult<Bound<'py, PyAny>> {
809 self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
810 }
811 }
812
813 impl<'a, 'py, $($T),+> crate::call::private::Sealed for &'a ($($T,)+) where $(&'a $T: IntoPyObject<'py>,)+ $($T: 'a,)+ /*MSRV */ {}
814 impl<'a, 'py, $($T),+> crate::call::PyCallArgs<'py> for &'a ($($T,)+)
815 where
816 $(&'a $T: IntoPyObject<'py>,)+
817 $($T: 'a,)+ // MSRV
818 {
819 #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
820 fn call(
821 self,
822 function: Borrowed<'_, 'py, PyAny>,
823 kwargs: Borrowed<'_, '_, crate::types::PyDict>,
824 _: crate::call::private::Token,
825 ) -> PyResult<Bound<'py, PyAny>> {
826 let py = function.py();
827 // We need this to drop the arguments correctly.
828 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
829 // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
830 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
831 unsafe {
832 ffi::PyObject_VectorcallDict(
833 function.as_ptr(),
834 args.as_mut_ptr().add(1),
835 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
836 kwargs.as_ptr(),
837 )
838 .assume_owned_or_err(py)
839 }
840 }
841
842 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
843 fn call_positional(
844 self,
845 function: Borrowed<'_, 'py, PyAny>,
846 _: crate::call::private::Token,
847 ) -> PyResult<Bound<'py, PyAny>> {
848 let py = function.py();
849 // We need this to drop the arguments correctly.
850 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
851
852 #[cfg(not(Py_LIMITED_API))]
853 if $length == 1 {
854 return unsafe {
855 ffi::PyObject_CallOneArg(
856 function.as_ptr(),
857 args_bound[0].as_ptr()
858 )
859 .assume_owned_or_err(py)
860 };
861 }
862
863 // Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
864 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
865 unsafe {
866 ffi::PyObject_Vectorcall(
867 function.as_ptr(),
868 args.as_mut_ptr().add(1),
869 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
870 std::ptr::null_mut(),
871 )
872 .assume_owned_or_err(py)
873 }
874 }
875
876 #[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
877 fn call_method_positional(
878 self,
879 object: Borrowed<'_, 'py, PyAny>,
880 method_name: Borrowed<'_, 'py, crate::types::PyString>,
881 _: crate::call::private::Token,
882 ) -> PyResult<Bound<'py, PyAny>> {
883 let py = object.py();
884 // We need this to drop the arguments correctly.
885 let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
886
887 #[cfg(not(Py_LIMITED_API))]
888 if $length == 1 {
889 return unsafe {
890 ffi::PyObject_CallMethodOneArg(
891 object.as_ptr(),
892 method_name.as_ptr(),
893 args_bound[0].as_ptr(),
894 )
895 .assume_owned_or_err(py)
896 };
897 }
898
899 let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
900 unsafe {
901 ffi::PyObject_VectorcallMethod(
902 method_name.as_ptr(),
903 args.as_mut_ptr(),
904 // +1 for the receiver.
905 1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
906 std::ptr::null_mut(),
907 )
908 .assume_owned_or_err(py)
909 }
910 }
911
912 #[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
913 fn call(
914 self,
915 function: Borrowed<'_, 'py, PyAny>,
916 kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
917 token: crate::call::private::Token,
918 ) -> PyResult<Bound<'py, PyAny>> {
919 self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
920 }
921
922 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
923 fn call_positional(
924 self,
925 function: Borrowed<'_, 'py, PyAny>,
926 token: crate::call::private::Token,
927 ) -> PyResult<Bound<'py, PyAny>> {
928 self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
929 }
930
931 #[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
932 fn call_method_positional(
933 self,
934 object: Borrowed<'_, 'py, PyAny>,
935 method_name: Borrowed<'_, 'py, crate::types::PyString>,
936 token: crate::call::private::Token,
937 ) -> PyResult<Bound<'py, PyAny>> {
938 self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
939 }
940 }
941
942 #[allow(deprecated)]
943 impl <$($T: IntoPy<PyObject>),+> IntoPy<Py<PyTuple>> for ($($T,)+) {
944 fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
945 array_into_tuple(py, [$(self.$n.into_py(py).into_bound(py)),+]).unbind()
946 }
947 }
948
949 impl<'py, $($T: FromPyObject<'py>),+> FromPyObject<'py> for ($($T,)+) {
950 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self>
951 {
952 let t = obj.downcast::<PyTuple>()?;
953 if t.len() == $length {
954 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
955 return Ok(($(t.get_borrowed_item($n)?.extract::<$T>()?,)+));
956
957 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
958 unsafe {return Ok(($(t.get_borrowed_item_unchecked($n).extract::<$T>()?,)+));}
959 } else {
960 Err(wrong_tuple_length(t, $length))
961 }
962 }
963
964 #[cfg(feature = "experimental-inspect")]
965 fn type_input() -> TypeInfo {
966 TypeInfo::Tuple(Some(vec![$( $T::type_input() ),+]))
967 }
968 }
969});
970
971fn array_into_tuple<'py, const N: usize>(
972 py: Python<'py>,
973 array: [Bound<'py, PyAny>; N],
974) -> Bound<'py, PyTuple> {
975 unsafe {
976 let ptr: *mut PyObject = ffi::PyTuple_New(N.try_into().expect(msg:"0 < N <= 12"));
977 let tup: Bound<'_, PyTuple> = ptr.assume_owned(py).downcast_into_unchecked();
978 for (index: usize, obj: Bound<'py, PyAny>) in array.into_iter().enumerate() {
979 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
980 ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
981 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
982 ffi::PyTuple_SetItem(arg1:ptr, arg2:index as ffi::Py_ssize_t, arg3:obj.into_ptr());
983 }
984 tup
985 }
986}
987
988tuple_conversion!(1, (ref0, 0, T0));
989tuple_conversion!(2, (ref0, 0, T0), (ref1, 1, T1));
990tuple_conversion!(3, (ref0, 0, T0), (ref1, 1, T1), (ref2, 2, T2));
991tuple_conversion!(
992 4,
993 (ref0, 0, T0),
994 (ref1, 1, T1),
995 (ref2, 2, T2),
996 (ref3, 3, T3)
997);
998tuple_conversion!(
999 5,
1000 (ref0, 0, T0),
1001 (ref1, 1, T1),
1002 (ref2, 2, T2),
1003 (ref3, 3, T3),
1004 (ref4, 4, T4)
1005);
1006tuple_conversion!(
1007 6,
1008 (ref0, 0, T0),
1009 (ref1, 1, T1),
1010 (ref2, 2, T2),
1011 (ref3, 3, T3),
1012 (ref4, 4, T4),
1013 (ref5, 5, T5)
1014);
1015tuple_conversion!(
1016 7,
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);
1025tuple_conversion!(
1026 8,
1027 (ref0, 0, T0),
1028 (ref1, 1, T1),
1029 (ref2, 2, T2),
1030 (ref3, 3, T3),
1031 (ref4, 4, T4),
1032 (ref5, 5, T5),
1033 (ref6, 6, T6),
1034 (ref7, 7, T7)
1035);
1036tuple_conversion!(
1037 9,
1038 (ref0, 0, T0),
1039 (ref1, 1, T1),
1040 (ref2, 2, T2),
1041 (ref3, 3, T3),
1042 (ref4, 4, T4),
1043 (ref5, 5, T5),
1044 (ref6, 6, T6),
1045 (ref7, 7, T7),
1046 (ref8, 8, T8)
1047);
1048tuple_conversion!(
1049 10,
1050 (ref0, 0, T0),
1051 (ref1, 1, T1),
1052 (ref2, 2, T2),
1053 (ref3, 3, T3),
1054 (ref4, 4, T4),
1055 (ref5, 5, T5),
1056 (ref6, 6, T6),
1057 (ref7, 7, T7),
1058 (ref8, 8, T8),
1059 (ref9, 9, T9)
1060);
1061tuple_conversion!(
1062 11,
1063 (ref0, 0, T0),
1064 (ref1, 1, T1),
1065 (ref2, 2, T2),
1066 (ref3, 3, T3),
1067 (ref4, 4, T4),
1068 (ref5, 5, T5),
1069 (ref6, 6, T6),
1070 (ref7, 7, T7),
1071 (ref8, 8, T8),
1072 (ref9, 9, T9),
1073 (ref10, 10, T10)
1074);
1075
1076tuple_conversion!(
1077 12,
1078 (ref0, 0, T0),
1079 (ref1, 1, T1),
1080 (ref2, 2, T2),
1081 (ref3, 3, T3),
1082 (ref4, 4, T4),
1083 (ref5, 5, T5),
1084 (ref6, 6, T6),
1085 (ref7, 7, T7),
1086 (ref8, 8, T8),
1087 (ref9, 9, T9),
1088 (ref10, 10, T10),
1089 (ref11, 11, T11)
1090);
1091
1092#[cfg(test)]
1093mod tests {
1094 use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyList, PyTuple};
1095 use crate::{IntoPyObject, Python};
1096 use std::collections::HashSet;
1097 #[cfg(feature = "nightly")]
1098 use std::num::NonZero;
1099 use std::ops::Range;
1100 #[test]
1101 fn test_new() {
1102 Python::with_gil(|py| {
1103 let ob = PyTuple::new(py, [1, 2, 3]).unwrap();
1104 assert_eq!(3, ob.len());
1105 let ob = ob.as_any();
1106 assert_eq!((1, 2, 3), ob.extract().unwrap());
1107
1108 let mut map = HashSet::new();
1109 map.insert(1);
1110 map.insert(2);
1111 PyTuple::new(py, map).unwrap();
1112 });
1113 }
1114
1115 #[test]
1116 fn test_len() {
1117 Python::with_gil(|py| {
1118 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1119 let tuple = ob.downcast::<PyTuple>().unwrap();
1120 assert_eq!(3, tuple.len());
1121 assert!(!tuple.is_empty());
1122 let ob = tuple.as_any();
1123 assert_eq!((1, 2, 3), ob.extract().unwrap());
1124 });
1125 }
1126
1127 #[test]
1128 fn test_empty() {
1129 Python::with_gil(|py| {
1130 let tuple = PyTuple::empty(py);
1131 assert!(tuple.is_empty());
1132 assert_eq!(0, tuple.len());
1133 });
1134 }
1135
1136 #[test]
1137 fn test_slice() {
1138 Python::with_gil(|py| {
1139 let tup = PyTuple::new(py, [2, 3, 5, 7]).unwrap();
1140 let slice = tup.get_slice(1, 3);
1141 assert_eq!(2, slice.len());
1142 let slice = tup.get_slice(1, 7);
1143 assert_eq!(3, slice.len());
1144 });
1145 }
1146
1147 #[test]
1148 fn test_iter() {
1149 Python::with_gil(|py| {
1150 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1151 let tuple = ob.downcast::<PyTuple>().unwrap();
1152 assert_eq!(3, tuple.len());
1153 let mut iter = tuple.iter();
1154
1155 assert_eq!(iter.size_hint(), (3, Some(3)));
1156
1157 assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1158 assert_eq!(iter.size_hint(), (2, Some(2)));
1159
1160 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1161 assert_eq!(iter.size_hint(), (1, Some(1)));
1162
1163 assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1164 assert_eq!(iter.size_hint(), (0, Some(0)));
1165
1166 assert!(iter.next().is_none());
1167 assert!(iter.next().is_none());
1168 });
1169 }
1170
1171 #[test]
1172 fn test_iter_rev() {
1173 Python::with_gil(|py| {
1174 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1175 let tuple = ob.downcast::<PyTuple>().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_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1182 assert_eq!(iter.size_hint(), (2, Some(2)));
1183
1184 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1185 assert_eq!(iter.size_hint(), (1, Some(1)));
1186
1187 assert_eq!(1_i32, 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_bound_iter() {
1197 Python::with_gil(|py| {
1198 let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1199 assert_eq!(3, tuple.len());
1200 let mut iter = tuple.iter();
1201
1202 assert_eq!(iter.size_hint(), (3, Some(3)));
1203
1204 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1205 assert_eq!(iter.size_hint(), (2, Some(2)));
1206
1207 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1208 assert_eq!(iter.size_hint(), (1, Some(1)));
1209
1210 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1211 assert_eq!(iter.size_hint(), (0, Some(0)));
1212
1213 assert!(iter.next().is_none());
1214 assert!(iter.next().is_none());
1215 });
1216 }
1217
1218 #[test]
1219 fn test_bound_iter_rev() {
1220 Python::with_gil(|py| {
1221 let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
1222 assert_eq!(3, tuple.len());
1223 let mut iter = tuple.iter().rev();
1224
1225 assert_eq!(iter.size_hint(), (3, Some(3)));
1226
1227 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1228 assert_eq!(iter.size_hint(), (2, Some(2)));
1229
1230 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1231 assert_eq!(iter.size_hint(), (1, Some(1)));
1232
1233 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1234 assert_eq!(iter.size_hint(), (0, Some(0)));
1235
1236 assert!(iter.next().is_none());
1237 assert!(iter.next().is_none());
1238 });
1239 }
1240
1241 #[test]
1242 fn test_into_iter() {
1243 Python::with_gil(|py| {
1244 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1245 let tuple = ob.downcast::<PyTuple>().unwrap();
1246 assert_eq!(3, tuple.len());
1247
1248 for (i, item) in tuple.iter().enumerate() {
1249 assert_eq!(i + 1, item.extract::<'_, usize>().unwrap());
1250 }
1251 });
1252 }
1253
1254 #[test]
1255 fn test_into_iter_bound() {
1256 Python::with_gil(|py| {
1257 let tuple = (1, 2, 3).into_pyobject(py).unwrap();
1258 assert_eq!(3, tuple.len());
1259
1260 let mut items = vec![];
1261 for item in tuple {
1262 items.push(item.extract::<usize>().unwrap());
1263 }
1264 assert_eq!(items, vec![1, 2, 3]);
1265 });
1266 }
1267
1268 #[test]
1269 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
1270 fn test_as_slice() {
1271 Python::with_gil(|py| {
1272 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1273 let tuple = ob.downcast::<PyTuple>().unwrap();
1274
1275 let slice = tuple.as_slice();
1276 assert_eq!(3, slice.len());
1277 assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap());
1278 assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap());
1279 assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap());
1280 });
1281 }
1282
1283 #[test]
1284 fn test_tuple_lengths_up_to_12() {
1285 Python::with_gil(|py| {
1286 let t0 = (0,).into_pyobject(py).unwrap();
1287 let t1 = (0, 1).into_pyobject(py).unwrap();
1288 let t2 = (0, 1, 2).into_pyobject(py).unwrap();
1289 let t3 = (0, 1, 2, 3).into_pyobject(py).unwrap();
1290 let t4 = (0, 1, 2, 3, 4).into_pyobject(py).unwrap();
1291 let t5 = (0, 1, 2, 3, 4, 5).into_pyobject(py).unwrap();
1292 let t6 = (0, 1, 2, 3, 4, 5, 6).into_pyobject(py).unwrap();
1293 let t7 = (0, 1, 2, 3, 4, 5, 6, 7).into_pyobject(py).unwrap();
1294 let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).into_pyobject(py).unwrap();
1295 let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).into_pyobject(py).unwrap();
1296 let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
1297 .into_pyobject(py)
1298 .unwrap();
1299 let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
1300 .into_pyobject(py)
1301 .unwrap();
1302
1303 assert_eq!(t0.extract::<(i32,)>().unwrap(), (0,));
1304 assert_eq!(t1.extract::<(i32, i32)>().unwrap(), (0, 1,));
1305 assert_eq!(t2.extract::<(i32, i32, i32)>().unwrap(), (0, 1, 2,));
1306 assert_eq!(
1307 t3.extract::<(i32, i32, i32, i32,)>().unwrap(),
1308 (0, 1, 2, 3,)
1309 );
1310 assert_eq!(
1311 t4.extract::<(i32, i32, i32, i32, i32,)>().unwrap(),
1312 (0, 1, 2, 3, 4,)
1313 );
1314 assert_eq!(
1315 t5.extract::<(i32, i32, i32, i32, i32, i32,)>().unwrap(),
1316 (0, 1, 2, 3, 4, 5,)
1317 );
1318 assert_eq!(
1319 t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>()
1320 .unwrap(),
1321 (0, 1, 2, 3, 4, 5, 6,)
1322 );
1323 assert_eq!(
1324 t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>()
1325 .unwrap(),
1326 (0, 1, 2, 3, 4, 5, 6, 7,)
1327 );
1328 assert_eq!(
1329 t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1330 .unwrap(),
1331 (0, 1, 2, 3, 4, 5, 6, 7, 8,)
1332 );
1333 assert_eq!(
1334 t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1335 .unwrap(),
1336 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9,)
1337 );
1338 assert_eq!(
1339 t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1340 .unwrap(),
1341 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,)
1342 );
1343 assert_eq!(
1344 t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
1345 .unwrap(),
1346 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,)
1347 );
1348 })
1349 }
1350
1351 #[test]
1352 fn test_tuple_get_item_invalid_index() {
1353 Python::with_gil(|py| {
1354 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1355 let tuple = ob.downcast::<PyTuple>().unwrap();
1356 let obj = tuple.get_item(5);
1357 assert!(obj.is_err());
1358 assert_eq!(
1359 obj.unwrap_err().to_string(),
1360 "IndexError: tuple index out of range"
1361 );
1362 });
1363 }
1364
1365 #[test]
1366 fn test_tuple_get_item_sanity() {
1367 Python::with_gil(|py| {
1368 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1369 let tuple = ob.downcast::<PyTuple>().unwrap();
1370 let obj = tuple.get_item(0);
1371 assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
1372 });
1373 }
1374
1375 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1376 #[test]
1377 fn test_tuple_get_item_unchecked_sanity() {
1378 Python::with_gil(|py| {
1379 let ob = (1, 2, 3).into_pyobject(py).unwrap();
1380 let tuple = ob.downcast::<PyTuple>().unwrap();
1381 let obj = unsafe { tuple.get_item_unchecked(0) };
1382 assert_eq!(obj.extract::<i32>().unwrap(), 1);
1383 });
1384 }
1385
1386 #[test]
1387 fn test_tuple_contains() {
1388 Python::with_gil(|py| {
1389 let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1390 let tuple = ob.downcast::<PyTuple>().unwrap();
1391 assert_eq!(6, tuple.len());
1392
1393 let bad_needle = 7i32.into_pyobject(py).unwrap();
1394 assert!(!tuple.contains(&bad_needle).unwrap());
1395
1396 let good_needle = 8i32.into_pyobject(py).unwrap();
1397 assert!(tuple.contains(&good_needle).unwrap());
1398
1399 let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
1400 assert!(tuple.contains(&type_coerced_needle).unwrap());
1401 });
1402 }
1403
1404 #[test]
1405 fn test_tuple_index() {
1406 Python::with_gil(|py| {
1407 let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
1408 let tuple = ob.downcast::<PyTuple>().unwrap();
1409 assert_eq!(0, tuple.index(1i32).unwrap());
1410 assert_eq!(2, tuple.index(2i32).unwrap());
1411 assert_eq!(3, tuple.index(3i32).unwrap());
1412 assert_eq!(4, tuple.index(5i32).unwrap());
1413 assert_eq!(5, tuple.index(8i32).unwrap());
1414 assert!(tuple.index(42i32).is_err());
1415 });
1416 }
1417
1418 // An iterator that lies about its `ExactSizeIterator` implementation.
1419 // See https://github.com/PyO3/pyo3/issues/2118
1420 struct FaultyIter(Range<usize>, usize);
1421
1422 impl Iterator for FaultyIter {
1423 type Item = usize;
1424
1425 fn next(&mut self) -> Option<Self::Item> {
1426 self.0.next()
1427 }
1428 }
1429
1430 impl ExactSizeIterator for FaultyIter {
1431 fn len(&self) -> usize {
1432 self.1
1433 }
1434 }
1435
1436 #[test]
1437 #[should_panic(
1438 expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1439 )]
1440 fn too_long_iterator() {
1441 Python::with_gil(|py| {
1442 let iter = FaultyIter(0..usize::MAX, 73);
1443 let _tuple = PyTuple::new(py, iter);
1444 })
1445 }
1446
1447 #[test]
1448 #[should_panic(
1449 expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1450 )]
1451 fn too_short_iterator() {
1452 Python::with_gil(|py| {
1453 let iter = FaultyIter(0..35, 73);
1454 let _tuple = PyTuple::new(py, iter);
1455 })
1456 }
1457
1458 #[test]
1459 #[should_panic(
1460 expected = "out of range integral type conversion attempted on `elements.len()`"
1461 )]
1462 fn overflowing_size() {
1463 Python::with_gil(|py| {
1464 let iter = FaultyIter(0..0, usize::MAX);
1465
1466 let _tuple = PyTuple::new(py, iter);
1467 })
1468 }
1469
1470 #[test]
1471 fn bad_intopyobject_doesnt_cause_leaks() {
1472 use crate::types::PyInt;
1473 use std::convert::Infallible;
1474 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1475
1476 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1477
1478 struct Bad(usize);
1479
1480 impl Drop for Bad {
1481 fn drop(&mut self) {
1482 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1483 }
1484 }
1485
1486 impl<'py> IntoPyObject<'py> for Bad {
1487 type Target = PyInt;
1488 type Output = crate::Bound<'py, Self::Target>;
1489 type Error = Infallible;
1490
1491 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1492 // This panic should not lead to a memory leak
1493 assert_ne!(self.0, 42);
1494 self.0.into_pyobject(py)
1495 }
1496 }
1497
1498 struct FaultyIter(Range<usize>, usize);
1499
1500 impl Iterator for FaultyIter {
1501 type Item = Bad;
1502
1503 fn next(&mut self) -> Option<Self::Item> {
1504 self.0.next().map(|i| {
1505 NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1506 Bad(i)
1507 })
1508 }
1509 }
1510
1511 impl ExactSizeIterator for FaultyIter {
1512 fn len(&self) -> usize {
1513 self.1
1514 }
1515 }
1516
1517 Python::with_gil(|py| {
1518 std::panic::catch_unwind(|| {
1519 let iter = FaultyIter(0..50, 50);
1520 let _tuple = PyTuple::new(py, iter);
1521 })
1522 .unwrap_err();
1523 });
1524
1525 assert_eq!(
1526 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1527 0,
1528 "Some destructors did not run"
1529 );
1530 }
1531
1532 #[test]
1533 fn bad_intopyobject_doesnt_cause_leaks_2() {
1534 use crate::types::PyInt;
1535 use std::convert::Infallible;
1536 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1537
1538 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1539
1540 struct Bad(usize);
1541
1542 impl Drop for Bad {
1543 fn drop(&mut self) {
1544 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1545 }
1546 }
1547
1548 impl<'py> IntoPyObject<'py> for &Bad {
1549 type Target = PyInt;
1550 type Output = crate::Bound<'py, Self::Target>;
1551 type Error = Infallible;
1552
1553 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1554 // This panic should not lead to a memory leak
1555 assert_ne!(self.0, 3);
1556 self.0.into_pyobject(py)
1557 }
1558 }
1559
1560 let s = (Bad(1), Bad(2), Bad(3), Bad(4));
1561 NEEDS_DESTRUCTING_COUNT.store(4, SeqCst);
1562 Python::with_gil(|py| {
1563 std::panic::catch_unwind(|| {
1564 let _tuple = (&s).into_pyobject(py).unwrap();
1565 })
1566 .unwrap_err();
1567 });
1568 drop(s);
1569
1570 assert_eq!(
1571 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1572 0,
1573 "Some destructors did not run"
1574 );
1575 }
1576
1577 #[test]
1578 fn test_tuple_to_list() {
1579 Python::with_gil(|py| {
1580 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1581 let list = tuple.to_list();
1582 let list_expected = PyList::new(py, vec![1, 2, 3]).unwrap();
1583 assert!(list.eq(list_expected).unwrap());
1584 })
1585 }
1586
1587 #[test]
1588 fn test_tuple_as_sequence() {
1589 Python::with_gil(|py| {
1590 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1591 let sequence = tuple.as_sequence();
1592 assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
1593 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1594
1595 assert_eq!(tuple.len(), 3);
1596 assert_eq!(sequence.len().unwrap(), 3);
1597 })
1598 }
1599
1600 #[test]
1601 fn test_tuple_into_sequence() {
1602 Python::with_gil(|py| {
1603 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1604 let sequence = tuple.into_sequence();
1605 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1606 assert_eq!(sequence.len().unwrap(), 3);
1607 })
1608 }
1609
1610 #[test]
1611 fn test_bound_tuple_get_item() {
1612 Python::with_gil(|py| {
1613 let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1614
1615 assert_eq!(tuple.len(), 4);
1616 assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
1617 assert_eq!(
1618 tuple
1619 .get_borrowed_item(1)
1620 .unwrap()
1621 .extract::<i32>()
1622 .unwrap(),
1623 2
1624 );
1625 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1626 {
1627 assert_eq!(
1628 unsafe { tuple.get_item_unchecked(2) }
1629 .extract::<i32>()
1630 .unwrap(),
1631 3
1632 );
1633 assert_eq!(
1634 unsafe { tuple.get_borrowed_item_unchecked(3) }
1635 .extract::<i32>()
1636 .unwrap(),
1637 4
1638 );
1639 }
1640 })
1641 }
1642
1643 #[test]
1644 fn test_bound_tuple_nth() {
1645 Python::with_gil(|py| {
1646 let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
1647 let mut iter = tuple.iter();
1648 assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 2);
1649 assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 4);
1650 assert!(iter.nth(1).is_none());
1651
1652 let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1653 let mut iter = tuple.iter();
1654 iter.next();
1655 assert!(iter.nth(1).is_none());
1656
1657 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1658 let mut iter = tuple.iter();
1659 assert!(iter.nth(10).is_none());
1660
1661 let tuple = PyTuple::new(py, vec![6, 7, 8, 9, 10]).unwrap();
1662 let mut iter = tuple.iter();
1663 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 6);
1664 assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 9);
1665 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 10);
1666
1667 let mut iter = tuple.iter();
1668 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 9);
1669 assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 8);
1670 assert!(iter.next().is_none());
1671 });
1672 }
1673
1674 #[test]
1675 fn test_bound_tuple_nth_back() {
1676 Python::with_gil(|py| {
1677 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1678 let mut iter = tuple.iter();
1679 assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 5);
1680 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1681 assert!(iter.nth_back(2).is_none());
1682
1683 let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
1684 let mut iter = tuple.iter();
1685 assert!(iter.nth_back(0).is_none());
1686 assert!(iter.nth_back(1).is_none());
1687
1688 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1689 let mut iter = tuple.iter();
1690 assert!(iter.nth_back(5).is_none());
1691
1692 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1693 let mut iter = tuple.iter();
1694 iter.next_back(); // Consume the last element
1695 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1696 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
1697 assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 1);
1698
1699 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1700 let mut iter = tuple.iter();
1701 assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 4);
1702 assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 1);
1703
1704 let mut iter2 = tuple.iter();
1705 iter2.next_back();
1706 assert_eq!(iter2.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1707 assert_eq!(iter2.next_back().unwrap().extract::<i32>().unwrap(), 2);
1708
1709 let mut iter3 = tuple.iter();
1710 iter3.nth(1);
1711 assert_eq!(iter3.nth_back(2).unwrap().extract::<i32>().unwrap(), 3);
1712 assert!(iter3.nth_back(0).is_none());
1713 });
1714 }
1715
1716 #[cfg(feature = "nightly")]
1717 #[test]
1718 fn test_bound_tuple_advance_by() {
1719 Python::with_gil(|py| {
1720 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1721 let mut iter = tuple.iter();
1722
1723 assert_eq!(iter.advance_by(2), Ok(()));
1724 assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
1725 assert_eq!(iter.advance_by(0), Ok(()));
1726 assert_eq!(iter.advance_by(100), Err(NonZero::new(98).unwrap()));
1727 assert!(iter.next().is_none());
1728
1729 let mut iter2 = tuple.iter();
1730 assert_eq!(iter2.advance_by(6), Err(NonZero::new(1).unwrap()));
1731
1732 let mut iter3 = tuple.iter();
1733 assert_eq!(iter3.advance_by(5), Ok(()));
1734
1735 let mut iter4 = tuple.iter();
1736 assert_eq!(iter4.advance_by(0), Ok(()));
1737 assert_eq!(iter4.next().unwrap().extract::<i32>().unwrap(), 1);
1738 })
1739 }
1740
1741 #[cfg(feature = "nightly")]
1742 #[test]
1743 fn test_bound_tuple_advance_back_by() {
1744 Python::with_gil(|py| {
1745 let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
1746 let mut iter = tuple.iter();
1747
1748 assert_eq!(iter.advance_back_by(2), Ok(()));
1749 assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 3);
1750 assert_eq!(iter.advance_back_by(0), Ok(()));
1751 assert_eq!(iter.advance_back_by(100), Err(NonZero::new(98).unwrap()));
1752 assert!(iter.next_back().is_none());
1753
1754 let mut iter2 = tuple.iter();
1755 assert_eq!(iter2.advance_back_by(6), Err(NonZero::new(1).unwrap()));
1756
1757 let mut iter3 = tuple.iter();
1758 assert_eq!(iter3.advance_back_by(5), Ok(()));
1759
1760 let mut iter4 = tuple.iter();
1761 assert_eq!(iter4.advance_back_by(0), Ok(()));
1762 assert_eq!(iter4.next_back().unwrap().extract::<i32>().unwrap(), 5);
1763 })
1764 }
1765
1766 #[test]
1767 fn test_iter_last() {
1768 Python::with_gil(|py| {
1769 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1770 let last = tuple.iter().last();
1771 assert_eq!(last.unwrap().extract::<i32>().unwrap(), 3);
1772 })
1773 }
1774
1775 #[test]
1776 fn test_iter_count() {
1777 Python::with_gil(|py| {
1778 let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1779 assert_eq!(tuple.iter().count(), 3);
1780 })
1781 }
1782}
1783