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