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