1use std::iter::FusedIterator;
2
3use crate::conversion::private;
4use crate::ffi::{self, Py_ssize_t};
5use crate::ffi_ptr_ext::FfiPtrExt;
6#[cfg(feature = "experimental-inspect")]
7use crate::inspect::types::TypeInfo;
8use crate::instance::Borrowed;
9use crate::internal_tricks::get_ssize_index;
10use crate::types::{
11 any::PyAnyMethods, sequence::PySequenceMethods, PyDict, PyList, PySequence, PyString,
12};
13#[cfg(feature = "gil-refs")]
14use crate::PyNativeType;
15use crate::{
16 exceptions, Bound, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python,
17 ToPyObject,
18};
19
20#[inline]
21#[track_caller]
22fn new_from_iter<'py>(
23 py: Python<'py>,
24 elements: &mut dyn ExactSizeIterator<Item = PyObject>,
25) -> Bound<'py, PyTuple> {
26 unsafe {
27 let len: Py_ssize_t = elements
29 .len()
30 .try_into()
31 .expect("out of range integral type conversion attempted on `elements.len()`");
32
33 let ptr = ffi::PyTuple_New(len);
34
35 let tup = ptr.assume_owned(py).downcast_into_unchecked();
38
39 let mut counter: Py_ssize_t = 0;
40
41 for obj in elements.take(len as usize) {
42 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
43 ffi::PyTuple_SET_ITEM(ptr, counter, obj.into_ptr());
44 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
45 ffi::PyTuple_SetItem(ptr, counter, obj.into_ptr());
46 counter += 1;
47 }
48
49 assert!(elements.next().is_none(), "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation.");
50 assert_eq!(len, counter, "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation.");
51
52 tup
53 }
54}
55
56#[repr(transparent)]
64pub struct PyTuple(PyAny);
65
66pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), #checkfunction=ffi::PyTuple_Check);
67
68impl PyTuple {
69 #[track_caller]
96 pub fn new_bound<T, U>(
97 py: Python<'_>,
98 elements: impl IntoIterator<Item = T, IntoIter = U>,
99 ) -> Bound<'_, PyTuple>
100 where
101 T: ToPyObject,
102 U: ExactSizeIterator<Item = T>,
103 {
104 let mut elements = elements.into_iter().map(|e| e.to_object(py));
105 new_from_iter(py, &mut elements)
106 }
107
108 pub fn empty_bound(py: Python<'_>) -> Bound<'_, PyTuple> {
110 unsafe {
111 ffi::PyTuple_New(0)
112 .assume_owned(py)
113 .downcast_into_unchecked()
114 }
115 }
116}
117
118#[cfg(feature = "gil-refs")]
119impl PyTuple {
120 #[track_caller]
122 #[deprecated(
123 since = "0.21.0",
124 note = "`PyTuple::new` will be replaced by `PyTuple::new_bound` in a future PyO3 version"
125 )]
126 pub fn new<T, U>(
127 py: Python<'_>,
128 elements: impl IntoIterator<Item = T, IntoIter = U>,
129 ) -> &PyTuple
130 where
131 T: ToPyObject,
132 U: ExactSizeIterator<Item = T>,
133 {
134 Self::new_bound(py, elements).into_gil_ref()
135 }
136
137 #[deprecated(
139 since = "0.21.0",
140 note = "`PyTuple::empty` will be replaced by `PyTuple::empty_bound` in a future PyO3 version"
141 )]
142 pub fn empty(py: Python<'_>) -> &PyTuple {
143 Self::empty_bound(py).into_gil_ref()
144 }
145
146 pub fn len(&self) -> usize {
148 self.as_borrowed().len()
149 }
150
151 pub fn is_empty(&self) -> bool {
153 self.as_borrowed().is_empty()
154 }
155
156 pub fn as_sequence(&self) -> &PySequence {
158 unsafe { self.downcast_unchecked() }
159 }
160
161 pub fn get_slice(&self, low: usize, high: usize) -> &PyTuple {
166 self.as_borrowed().get_slice(low, high).into_gil_ref()
167 }
168
169 pub fn get_item(&self, index: usize) -> PyResult<&PyAny> {
185 self.as_borrowed()
186 .get_borrowed_item(index)
187 .map(Borrowed::into_gil_ref)
188 }
189
190 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
196 pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny {
197 self.as_borrowed()
198 .get_borrowed_item_unchecked(index)
199 .into_gil_ref()
200 }
201
202 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
204 pub fn as_slice(&self) -> &[&PyAny] {
205 unsafe {
208 let ptr = self.as_ptr() as *mut ffi::PyTupleObject;
209 let slice = std::slice::from_raw_parts((*ptr).ob_item.as_ptr(), self.len());
210 &*(slice as *const [*mut ffi::PyObject] as *const [&PyAny])
211 }
212 }
213
214 #[inline]
218 pub fn contains<V>(&self, value: V) -> PyResult<bool>
219 where
220 V: ToPyObject,
221 {
222 self.as_borrowed().contains(value)
223 }
224
225 #[inline]
229 pub fn index<V>(&self, value: V) -> PyResult<usize>
230 where
231 V: ToPyObject,
232 {
233 self.as_borrowed().index(value)
234 }
235
236 pub fn iter(&self) -> PyTupleIterator<'_> {
238 PyTupleIterator(BorrowedTupleIterator::new(self.as_borrowed()))
239 }
240
241 pub fn to_list(&self) -> &PyList {
245 self.as_borrowed().to_list().into_gil_ref()
246 }
247}
248
249#[cfg(feature = "gil-refs")]
250index_impls!(PyTuple, "tuple", PyTuple::len, PyTuple::get_slice);
251
252#[doc(alias = "PyTuple")]
258pub trait PyTupleMethods<'py>: crate::sealed::Sealed {
259 fn len(&self) -> usize;
261
262 fn is_empty(&self) -> bool;
264
265 fn as_sequence(&self) -> &Bound<'py, PySequence>;
267
268 fn into_sequence(self) -> Bound<'py, PySequence>;
270
271 fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple>;
276
277 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
293
294 fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>>;
297
298 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
304 unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
305
306 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
313 unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny>;
314
315 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
317 fn as_slice(&self) -> &[Bound<'py, PyAny>];
318
319 fn contains<V>(&self, value: V) -> PyResult<bool>
323 where
324 V: ToPyObject;
325
326 fn index<V>(&self, value: V) -> PyResult<usize>
330 where
331 V: ToPyObject;
332
333 fn iter(&self) -> BoundTupleIterator<'py>;
335
336 fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py>;
339
340 fn to_list(&self) -> Bound<'py, PyList>;
344}
345
346impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
347 fn len(&self) -> usize {
348 unsafe {
349 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
350 let size = ffi::PyTuple_GET_SIZE(self.as_ptr());
351 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
352 let size = ffi::PyTuple_Size(self.as_ptr());
353 size as usize
355 }
356 }
357
358 fn is_empty(&self) -> bool {
359 self.len() == 0
360 }
361
362 fn as_sequence(&self) -> &Bound<'py, PySequence> {
363 unsafe { self.downcast_unchecked() }
364 }
365
366 fn into_sequence(self) -> Bound<'py, PySequence> {
367 unsafe { self.into_any().downcast_into_unchecked() }
368 }
369
370 fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple> {
371 unsafe {
372 ffi::PyTuple_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
373 .assume_owned(self.py())
374 .downcast_into_unchecked()
375 }
376 }
377
378 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
379 self.get_borrowed_item(index).map(Borrowed::to_owned)
380 }
381
382 fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
383 self.as_borrowed().get_borrowed_item(index)
384 }
385
386 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
387 unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
388 self.get_borrowed_item_unchecked(index).to_owned()
389 }
390
391 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
392 unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny> {
393 self.as_borrowed().get_borrowed_item_unchecked(index)
394 }
395
396 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
397 fn as_slice(&self) -> &[Bound<'py, PyAny>] {
398 let items = unsafe { &(*self.as_ptr().cast::<ffi::PyTupleObject>()).ob_item };
400 unsafe { std::slice::from_raw_parts(items.as_ptr().cast(), self.len()) }
402 }
403
404 #[inline]
405 fn contains<V>(&self, value: V) -> PyResult<bool>
406 where
407 V: ToPyObject,
408 {
409 self.as_sequence().contains(value)
410 }
411
412 #[inline]
413 fn index<V>(&self, value: V) -> PyResult<usize>
414 where
415 V: ToPyObject,
416 {
417 self.as_sequence().index(value)
418 }
419
420 fn iter(&self) -> BoundTupleIterator<'py> {
421 BoundTupleIterator::new(self.clone())
422 }
423
424 fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
425 self.as_borrowed().iter_borrowed()
426 }
427
428 fn to_list(&self) -> Bound<'py, PyList> {
429 self.as_sequence()
430 .to_list()
431 .expect("failed to convert tuple to list")
432 }
433}
434
435impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
436 fn get_borrowed_item(self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
437 unsafe {
438 ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
439 .assume_borrowed_or_err(self.py())
440 }
441 }
442
443 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
444 unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
445 ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t).assume_borrowed(self.py())
446 }
447
448 pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
449 BorrowedTupleIterator::new(self)
450 }
451}
452
453#[cfg(feature = "gil-refs")]
455pub struct PyTupleIterator<'a>(BorrowedTupleIterator<'a, 'a>);
456
457#[cfg(feature = "gil-refs")]
458impl<'a> Iterator for PyTupleIterator<'a> {
459 type Item = &'a PyAny;
460
461 #[inline]
462 fn next(&mut self) -> Option<Self::Item> {
463 self.0.next().map(Borrowed::into_gil_ref)
464 }
465
466 #[inline]
467 fn size_hint(&self) -> (usize, Option<usize>) {
468 self.0.size_hint()
469 }
470}
471
472#[cfg(feature = "gil-refs")]
473impl<'a> DoubleEndedIterator for PyTupleIterator<'a> {
474 #[inline]
475 fn next_back(&mut self) -> Option<Self::Item> {
476 self.0.next_back().map(Borrowed::into_gil_ref)
477 }
478}
479
480#[cfg(feature = "gil-refs")]
481impl<'a> ExactSizeIterator for PyTupleIterator<'a> {
482 fn len(&self) -> usize {
483 self.0.len()
484 }
485}
486
487#[cfg(feature = "gil-refs")]
488impl FusedIterator for PyTupleIterator<'_> {}
489
490#[cfg(feature = "gil-refs")]
491impl<'a> IntoIterator for &'a PyTuple {
492 type Item = &'a PyAny;
493 type IntoIter = PyTupleIterator<'a>;
494
495 fn into_iter(self) -> Self::IntoIter {
496 PyTupleIterator(BorrowedTupleIterator::new(self.as_borrowed()))
497 }
498}
499
500pub struct BoundTupleIterator<'py> {
502 tuple: Bound<'py, PyTuple>,
503 index: usize,
504 length: usize,
505}
506
507impl<'py> BoundTupleIterator<'py> {
508 fn new(tuple: Bound<'py, PyTuple>) -> Self {
509 let length = tuple.len();
510 BoundTupleIterator {
511 tuple,
512 index: 0,
513 length,
514 }
515 }
516}
517
518impl<'py> Iterator for BoundTupleIterator<'py> {
519 type Item = Bound<'py, PyAny>;
520
521 #[inline]
522 fn next(&mut self) -> Option<Self::Item> {
523 if self.index < self.length {
524 let item = unsafe {
525 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.index).to_owned()
526 };
527 self.index += 1;
528 Some(item)
529 } else {
530 None
531 }
532 }
533
534 #[inline]
535 fn size_hint(&self) -> (usize, Option<usize>) {
536 let len = self.len();
537 (len, Some(len))
538 }
539}
540
541impl<'py> DoubleEndedIterator for BoundTupleIterator<'py> {
542 #[inline]
543 fn next_back(&mut self) -> Option<Self::Item> {
544 if self.index < self.length {
545 let item = unsafe {
546 BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.length - 1)
547 .to_owned()
548 };
549 self.length -= 1;
550 Some(item)
551 } else {
552 None
553 }
554 }
555}
556
557impl<'py> ExactSizeIterator for BoundTupleIterator<'py> {
558 fn len(&self) -> usize {
559 self.length.saturating_sub(self.index)
560 }
561}
562
563impl FusedIterator for BoundTupleIterator<'_> {}
564
565impl<'py> IntoIterator for Bound<'py, PyTuple> {
566 type Item = Bound<'py, PyAny>;
567 type IntoIter = BoundTupleIterator<'py>;
568
569 fn into_iter(self) -> Self::IntoIter {
570 BoundTupleIterator::new(self)
571 }
572}
573
574impl<'py> IntoIterator for &Bound<'py, PyTuple> {
575 type Item = Bound<'py, PyAny>;
576 type IntoIter = BoundTupleIterator<'py>;
577
578 fn into_iter(self) -> Self::IntoIter {
579 self.iter()
580 }
581}
582
583pub struct BorrowedTupleIterator<'a, 'py> {
585 tuple: Borrowed<'a, 'py, PyTuple>,
586 index: usize,
587 length: usize,
588}
589
590impl<'a, 'py> BorrowedTupleIterator<'a, 'py> {
591 fn new(tuple: Borrowed<'a, 'py, PyTuple>) -> Self {
592 let length = tuple.len();
593 BorrowedTupleIterator {
594 tuple,
595 index: 0,
596 length,
597 }
598 }
599
600 unsafe fn get_item(
601 tuple: Borrowed<'a, 'py, PyTuple>,
602 index: usize,
603 ) -> Borrowed<'a, 'py, PyAny> {
604 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
605 let item = tuple.get_borrowed_item(index).expect("tuple.get failed");
606 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
607 let item = tuple.get_borrowed_item_unchecked(index);
608 item
609 }
610}
611
612impl<'a, 'py> Iterator for BorrowedTupleIterator<'a, 'py> {
613 type Item = Borrowed<'a, 'py, PyAny>;
614
615 #[inline]
616 fn next(&mut self) -> Option<Self::Item> {
617 if self.index < self.length {
618 let item = unsafe { Self::get_item(self.tuple, self.index) };
619 self.index += 1;
620 Some(item)
621 } else {
622 None
623 }
624 }
625
626 #[inline]
627 fn size_hint(&self) -> (usize, Option<usize>) {
628 let len = self.len();
629 (len, Some(len))
630 }
631}
632
633impl<'a, 'py> DoubleEndedIterator for BorrowedTupleIterator<'a, 'py> {
634 #[inline]
635 fn next_back(&mut self) -> Option<Self::Item> {
636 if self.index < self.length {
637 let item = unsafe { Self::get_item(self.tuple, self.length - 1) };
638 self.length -= 1;
639 Some(item)
640 } else {
641 None
642 }
643 }
644}
645
646impl<'a, 'py> ExactSizeIterator for BorrowedTupleIterator<'a, 'py> {
647 fn len(&self) -> usize {
648 self.length.saturating_sub(self.index)
649 }
650}
651
652impl FusedIterator for BorrowedTupleIterator<'_, '_> {}
653
654impl IntoPy<Py<PyTuple>> for Bound<'_, PyTuple> {
655 fn into_py(self, _: Python<'_>) -> Py<PyTuple> {
656 self.unbind()
657 }
658}
659
660impl IntoPy<Py<PyTuple>> for &'_ Bound<'_, PyTuple> {
661 fn into_py(self, _: Python<'_>) -> Py<PyTuple> {
662 self.clone().unbind()
663 }
664}
665
666#[cold]
667fn wrong_tuple_length(t: &Bound<'_, PyTuple>, expected_length: usize) -> PyErr {
668 let msg = format!(
669 "expected tuple of length {}, but got tuple of length {}",
670 expected_length,
671 t.len()
672 );
673 exceptions::PyValueError::new_err(msg)
674}
675
676macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
677 impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
678 fn to_object(&self, py: Python<'_>) -> PyObject {
679 array_into_tuple(py, [$(self.$n.to_object(py)),+]).into()
680 }
681 }
682 impl <$($T: IntoPy<PyObject>),+> IntoPy<PyObject> for ($($T,)+) {
683 fn into_py(self, py: Python<'_>) -> PyObject {
684 array_into_tuple(py, [$(self.$n.into_py(py)),+]).into()
685 }
686
687 #[cfg(feature = "experimental-inspect")]
688 fn type_output() -> TypeInfo {
689 TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
690 }
691 }
692
693 impl <$($T: IntoPy<PyObject>),+> IntoPy<Py<PyTuple>> for ($($T,)+) {
694 fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
695 array_into_tuple(py, [$(self.$n.into_py(py)),+])
696 }
697
698 #[cfg(feature = "experimental-inspect")]
699 fn type_output() -> TypeInfo {
700 TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
701 }
702
703 #[inline]
704 fn __py_call_vectorcall1<'py>(
705 self,
706 py: Python<'py>,
707 function: Borrowed<'_, 'py, PyAny>,
708 _: private::Token,
709 ) -> PyResult<Bound<'py, PyAny>> {
710 cfg_if::cfg_if! {
711 if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
712 let args_bound = [$(self.$n.into_py(py).into_bound(py),)*];
714 if $length == 1 {
715 unsafe {
716 ffi::PyObject_CallOneArg(function.as_ptr(), args_bound[0].as_ptr()).assume_owned_or_err(py)
717 }
718 } else {
719 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
721 unsafe {
722 ffi::PyObject_Vectorcall(
723 function.as_ptr(),
724 args.as_mut_ptr().add(1),
725 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
726 std::ptr::null_mut(),
727 )
728 .assume_owned_or_err(py)
729 }
730 }
731 } else {
732 function.call1(<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py))
733 }
734 }
735 }
736
737 #[inline]
738 fn __py_call_vectorcall<'py>(
739 self,
740 py: Python<'py>,
741 function: Borrowed<'_, 'py, PyAny>,
742 kwargs: Option<Borrowed<'_, '_, PyDict>>,
743 _: private::Token,
744 ) -> PyResult<Bound<'py, PyAny>> {
745 cfg_if::cfg_if! {
746 if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
747 let args_bound = [$(self.$n.into_py(py).into_bound(py),)*];
749 let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
751 unsafe {
752 ffi::PyObject_VectorcallDict(
753 function.as_ptr(),
754 args.as_mut_ptr().add(1),
755 $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
756 kwargs.map_or_else(std::ptr::null_mut, |kwargs| kwargs.as_ptr()),
757 )
758 .assume_owned_or_err(py)
759 }
760 } else {
761 function.call(<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py), kwargs.as_deref())
762 }
763 }
764 }
765
766 #[inline]
767 fn __py_call_method_vectorcall1<'py>(
768 self,
769 py: Python<'py>,
770 object: Borrowed<'_, 'py, PyAny>,
771 method_name: Borrowed<'_, 'py, PyString>,
772 _: private::Token,
773 ) -> PyResult<Bound<'py, PyAny>> {
774 cfg_if::cfg_if! {
775 if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
776 let args_bound = [$(self.$n.into_py(py).into_bound(py),)*];
778 if $length == 1 {
779 unsafe {
780 ffi::PyObject_CallMethodOneArg(
781 object.as_ptr(),
782 method_name.as_ptr(),
783 args_bound[0].as_ptr(),
784 )
785 .assume_owned_or_err(py)
786 }
787 } else {
788 let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
789 unsafe {
790 ffi::PyObject_VectorcallMethod(
791 method_name.as_ptr(),
792 args.as_mut_ptr(),
793 1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
795 std::ptr::null_mut(),
796 )
797 .assume_owned_or_err(py)
798 }
799 }
800 } else {
801 object.call_method1(method_name.to_owned(), <Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py))
802 }
803 }
804 }
805 }
806
807 impl<'py, $($T: FromPyObject<'py>),+> FromPyObject<'py> for ($($T,)+) {
808 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self>
809 {
810 let t = obj.downcast::<PyTuple>()?;
811 if t.len() == $length {
812 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
813 return Ok(($(t.get_borrowed_item($n)?.extract::<$T>()?,)+));
814
815 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
816 unsafe {return Ok(($(t.get_borrowed_item_unchecked($n).extract::<$T>()?,)+));}
817 } else {
818 Err(wrong_tuple_length(t, $length))
819 }
820 }
821
822 #[cfg(feature = "experimental-inspect")]
823 fn type_input() -> TypeInfo {
824 TypeInfo::Tuple(Some(vec![$( $T::type_input() ),+]))
825 }
826 }
827});
828
829fn array_into_tuple<const N: usize>(py: Python<'_>, array: [PyObject; N]) -> Py<PyTuple> {
830 unsafe {
831 let ptr = ffi::PyTuple_New(N.try_into().expect("0 < N <= 12"));
832 let tup = Py::from_owned_ptr(py, ptr);
833 for (index, obj) in array.into_iter().enumerate() {
834 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
835 ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
836 #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
837 ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
838 }
839 tup
840 }
841}
842
843tuple_conversion!(1, (ref0, 0, T0));
844tuple_conversion!(2, (ref0, 0, T0), (ref1, 1, T1));
845tuple_conversion!(3, (ref0, 0, T0), (ref1, 1, T1), (ref2, 2, T2));
846tuple_conversion!(
847 4,
848 (ref0, 0, T0),
849 (ref1, 1, T1),
850 (ref2, 2, T2),
851 (ref3, 3, T3)
852);
853tuple_conversion!(
854 5,
855 (ref0, 0, T0),
856 (ref1, 1, T1),
857 (ref2, 2, T2),
858 (ref3, 3, T3),
859 (ref4, 4, T4)
860);
861tuple_conversion!(
862 6,
863 (ref0, 0, T0),
864 (ref1, 1, T1),
865 (ref2, 2, T2),
866 (ref3, 3, T3),
867 (ref4, 4, T4),
868 (ref5, 5, T5)
869);
870tuple_conversion!(
871 7,
872 (ref0, 0, T0),
873 (ref1, 1, T1),
874 (ref2, 2, T2),
875 (ref3, 3, T3),
876 (ref4, 4, T4),
877 (ref5, 5, T5),
878 (ref6, 6, T6)
879);
880tuple_conversion!(
881 8,
882 (ref0, 0, T0),
883 (ref1, 1, T1),
884 (ref2, 2, T2),
885 (ref3, 3, T3),
886 (ref4, 4, T4),
887 (ref5, 5, T5),
888 (ref6, 6, T6),
889 (ref7, 7, T7)
890);
891tuple_conversion!(
892 9,
893 (ref0, 0, T0),
894 (ref1, 1, T1),
895 (ref2, 2, T2),
896 (ref3, 3, T3),
897 (ref4, 4, T4),
898 (ref5, 5, T5),
899 (ref6, 6, T6),
900 (ref7, 7, T7),
901 (ref8, 8, T8)
902);
903tuple_conversion!(
904 10,
905 (ref0, 0, T0),
906 (ref1, 1, T1),
907 (ref2, 2, T2),
908 (ref3, 3, T3),
909 (ref4, 4, T4),
910 (ref5, 5, T5),
911 (ref6, 6, T6),
912 (ref7, 7, T7),
913 (ref8, 8, T8),
914 (ref9, 9, T9)
915);
916tuple_conversion!(
917 11,
918 (ref0, 0, T0),
919 (ref1, 1, T1),
920 (ref2, 2, T2),
921 (ref3, 3, T3),
922 (ref4, 4, T4),
923 (ref5, 5, T5),
924 (ref6, 6, T6),
925 (ref7, 7, T7),
926 (ref8, 8, T8),
927 (ref9, 9, T9),
928 (ref10, 10, T10)
929);
930
931tuple_conversion!(
932 12,
933 (ref0, 0, T0),
934 (ref1, 1, T1),
935 (ref2, 2, T2),
936 (ref3, 3, T3),
937 (ref4, 4, T4),
938 (ref5, 5, T5),
939 (ref6, 6, T6),
940 (ref7, 7, T7),
941 (ref8, 8, T8),
942 (ref9, 9, T9),
943 (ref10, 10, T10),
944 (ref11, 11, T11)
945);
946
947#[cfg(test)]
948mod tests {
949 use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyList, PyTuple};
950 use crate::{Python, ToPyObject};
951 use std::collections::HashSet;
952
953 #[test]
954 fn test_new() {
955 Python::with_gil(|py| {
956 let ob = PyTuple::new_bound(py, [1, 2, 3]);
957 assert_eq!(3, ob.len());
958 let ob = ob.as_any();
959 assert_eq!((1, 2, 3), ob.extract().unwrap());
960
961 let mut map = HashSet::new();
962 map.insert(1);
963 map.insert(2);
964 PyTuple::new_bound(py, map);
965 });
966 }
967
968 #[test]
969 fn test_len() {
970 Python::with_gil(|py| {
971 let ob = (1, 2, 3).to_object(py);
972 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
973 assert_eq!(3, tuple.len());
974 assert!(!tuple.is_empty());
975 let ob = tuple.as_any();
976 assert_eq!((1, 2, 3), ob.extract().unwrap());
977 });
978 }
979
980 #[test]
981 fn test_empty() {
982 Python::with_gil(|py| {
983 let tuple = PyTuple::empty_bound(py);
984 assert!(tuple.is_empty());
985 assert_eq!(0, tuple.len());
986 });
987 }
988
989 #[test]
990 fn test_slice() {
991 Python::with_gil(|py| {
992 let tup = PyTuple::new_bound(py, [2, 3, 5, 7]);
993 let slice = tup.get_slice(1, 3);
994 assert_eq!(2, slice.len());
995 let slice = tup.get_slice(1, 7);
996 assert_eq!(3, slice.len());
997 });
998 }
999
1000 #[test]
1001 fn test_iter() {
1002 Python::with_gil(|py| {
1003 let ob = (1, 2, 3).to_object(py);
1004 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1005 assert_eq!(3, tuple.len());
1006 let mut iter = tuple.iter();
1007
1008 assert_eq!(iter.size_hint(), (3, Some(3)));
1009
1010 assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1011 assert_eq!(iter.size_hint(), (2, Some(2)));
1012
1013 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1014 assert_eq!(iter.size_hint(), (1, Some(1)));
1015
1016 assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1017 assert_eq!(iter.size_hint(), (0, Some(0)));
1018
1019 assert!(iter.next().is_none());
1020 assert!(iter.next().is_none());
1021 });
1022 }
1023
1024 #[test]
1025 fn test_iter_rev() {
1026 Python::with_gil(|py| {
1027 let ob = (1, 2, 3).to_object(py);
1028 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1029 assert_eq!(3, tuple.len());
1030 let mut iter = tuple.iter().rev();
1031
1032 assert_eq!(iter.size_hint(), (3, Some(3)));
1033
1034 assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1035 assert_eq!(iter.size_hint(), (2, Some(2)));
1036
1037 assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1038 assert_eq!(iter.size_hint(), (1, Some(1)));
1039
1040 assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
1041 assert_eq!(iter.size_hint(), (0, Some(0)));
1042
1043 assert!(iter.next().is_none());
1044 assert!(iter.next().is_none());
1045 });
1046 }
1047
1048 #[test]
1049 fn test_bound_iter() {
1050 Python::with_gil(|py| {
1051 let tuple = PyTuple::new_bound(py, [1, 2, 3]);
1052 assert_eq!(3, tuple.len());
1053 let mut iter = tuple.iter();
1054
1055 assert_eq!(iter.size_hint(), (3, Some(3)));
1056
1057 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1058 assert_eq!(iter.size_hint(), (2, Some(2)));
1059
1060 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1061 assert_eq!(iter.size_hint(), (1, Some(1)));
1062
1063 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1064 assert_eq!(iter.size_hint(), (0, Some(0)));
1065
1066 assert!(iter.next().is_none());
1067 assert!(iter.next().is_none());
1068 });
1069 }
1070
1071 #[test]
1072 fn test_bound_iter_rev() {
1073 Python::with_gil(|py| {
1074 let tuple = PyTuple::new_bound(py, [1, 2, 3]);
1075 assert_eq!(3, tuple.len());
1076 let mut iter = tuple.iter().rev();
1077
1078 assert_eq!(iter.size_hint(), (3, Some(3)));
1079
1080 assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
1081 assert_eq!(iter.size_hint(), (2, Some(2)));
1082
1083 assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
1084 assert_eq!(iter.size_hint(), (1, Some(1)));
1085
1086 assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
1087 assert_eq!(iter.size_hint(), (0, Some(0)));
1088
1089 assert!(iter.next().is_none());
1090 assert!(iter.next().is_none());
1091 });
1092 }
1093
1094 #[test]
1095 fn test_into_iter() {
1096 Python::with_gil(|py| {
1097 let ob = (1, 2, 3).to_object(py);
1098 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1099 assert_eq!(3, tuple.len());
1100
1101 for (i, item) in tuple.iter().enumerate() {
1102 assert_eq!(i + 1, item.extract::<'_, usize>().unwrap());
1103 }
1104 });
1105 }
1106
1107 #[test]
1108 fn test_into_iter_bound() {
1109 use crate::Bound;
1110
1111 Python::with_gil(|py| {
1112 let ob = (1, 2, 3).to_object(py);
1113 let tuple: &Bound<'_, PyTuple> = ob.downcast_bound(py).unwrap();
1114 assert_eq!(3, tuple.len());
1115
1116 let mut items = vec![];
1117 for item in tuple {
1118 items.push(item.extract::<usize>().unwrap());
1119 }
1120 assert_eq!(items, vec![1, 2, 3]);
1121 });
1122 }
1123
1124 #[test]
1125 #[cfg(not(any(Py_LIMITED_API, GraalPy)))]
1126 fn test_as_slice() {
1127 Python::with_gil(|py| {
1128 let ob = (1, 2, 3).to_object(py);
1129 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1130
1131 let slice = tuple.as_slice();
1132 assert_eq!(3, slice.len());
1133 assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap());
1134 assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap());
1135 assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap());
1136 });
1137 }
1138
1139 #[test]
1140 fn test_tuple_lengths_up_to_12() {
1141 Python::with_gil(|py| {
1142 let t0 = (0,).to_object(py);
1143 let t1 = (0, 1).to_object(py);
1144 let t2 = (0, 1, 2).to_object(py);
1145 let t3 = (0, 1, 2, 3).to_object(py);
1146 let t4 = (0, 1, 2, 3, 4).to_object(py);
1147 let t5 = (0, 1, 2, 3, 4, 5).to_object(py);
1148 let t6 = (0, 1, 2, 3, 4, 5, 6).to_object(py);
1149 let t7 = (0, 1, 2, 3, 4, 5, 6, 7).to_object(py);
1150 let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).to_object(py);
1151 let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).to_object(py);
1152 let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).to_object(py);
1153 let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).to_object(py);
1154
1155 assert_eq!(t0.extract::<(i32,)>(py).unwrap(), (0,));
1156 assert_eq!(t1.extract::<(i32, i32)>(py).unwrap(), (0, 1,));
1157 assert_eq!(t2.extract::<(i32, i32, i32)>(py).unwrap(), (0, 1, 2,));
1158 assert_eq!(
1159 t3.extract::<(i32, i32, i32, i32,)>(py).unwrap(),
1160 (0, 1, 2, 3,)
1161 );
1162 assert_eq!(
1163 t4.extract::<(i32, i32, i32, i32, i32,)>(py).unwrap(),
1164 (0, 1, 2, 3, 4,)
1165 );
1166 assert_eq!(
1167 t5.extract::<(i32, i32, i32, i32, i32, i32,)>(py).unwrap(),
1168 (0, 1, 2, 3, 4, 5,)
1169 );
1170 assert_eq!(
1171 t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>(py)
1172 .unwrap(),
1173 (0, 1, 2, 3, 4, 5, 6,)
1174 );
1175 assert_eq!(
1176 t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>(py)
1177 .unwrap(),
1178 (0, 1, 2, 3, 4, 5, 6, 7,)
1179 );
1180 assert_eq!(
1181 t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py)
1182 .unwrap(),
1183 (0, 1, 2, 3, 4, 5, 6, 7, 8,)
1184 );
1185 assert_eq!(
1186 t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py)
1187 .unwrap(),
1188 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9,)
1189 );
1190 assert_eq!(
1191 t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py)
1192 .unwrap(),
1193 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,)
1194 );
1195 assert_eq!(
1196 t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py)
1197 .unwrap(),
1198 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,)
1199 );
1200 })
1201 }
1202
1203 #[test]
1204 fn test_tuple_get_item_invalid_index() {
1205 Python::with_gil(|py| {
1206 let ob = (1, 2, 3).to_object(py);
1207 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1208 let obj = tuple.get_item(5);
1209 assert!(obj.is_err());
1210 assert_eq!(
1211 obj.unwrap_err().to_string(),
1212 "IndexError: tuple index out of range"
1213 );
1214 });
1215 }
1216
1217 #[test]
1218 fn test_tuple_get_item_sanity() {
1219 Python::with_gil(|py| {
1220 let ob = (1, 2, 3).to_object(py);
1221 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1222 let obj = tuple.get_item(0);
1223 assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
1224 });
1225 }
1226
1227 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1228 #[test]
1229 fn test_tuple_get_item_unchecked_sanity() {
1230 Python::with_gil(|py| {
1231 let ob = (1, 2, 3).to_object(py);
1232 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1233 let obj = unsafe { tuple.get_item_unchecked(0) };
1234 assert_eq!(obj.extract::<i32>().unwrap(), 1);
1235 });
1236 }
1237
1238 #[test]
1239 #[cfg(feature = "gil-refs")]
1240 #[allow(deprecated)]
1241 fn test_tuple_index_trait() {
1242 Python::with_gil(|py| {
1243 let ob = (1, 2, 3).to_object(py);
1244 let tuple: &PyTuple = ob.downcast(py).unwrap();
1245 assert_eq!(1, tuple[0].extract::<i32>().unwrap());
1246 assert_eq!(2, tuple[1].extract::<i32>().unwrap());
1247 assert_eq!(3, tuple[2].extract::<i32>().unwrap());
1248 });
1249 }
1250
1251 #[test]
1252 #[should_panic]
1253 #[cfg(feature = "gil-refs")]
1254 #[allow(deprecated)]
1255 fn test_tuple_index_trait_panic() {
1256 Python::with_gil(|py| {
1257 let ob = (1, 2, 3).to_object(py);
1258 let tuple: &PyTuple = ob.downcast(py).unwrap();
1259 let _ = &tuple[7];
1260 });
1261 }
1262
1263 #[test]
1264 #[cfg(feature = "gil-refs")]
1265 #[allow(deprecated)]
1266 fn test_tuple_index_trait_ranges() {
1267 Python::with_gil(|py| {
1268 let ob = (1, 2, 3).to_object(py);
1269 let tuple: &PyTuple = ob.downcast(py).unwrap();
1270 assert_eq!(vec![2, 3], tuple[1..3].extract::<Vec<i32>>().unwrap());
1271 assert_eq!(
1272 Vec::<i32>::new(),
1273 tuple[3..3].extract::<Vec<i32>>().unwrap()
1274 );
1275 assert_eq!(vec![2, 3], tuple[1..].extract::<Vec<i32>>().unwrap());
1276 assert_eq!(Vec::<i32>::new(), tuple[3..].extract::<Vec<i32>>().unwrap());
1277 assert_eq!(vec![1, 2, 3], tuple[..].extract::<Vec<i32>>().unwrap());
1278 assert_eq!(vec![2, 3], tuple[1..=2].extract::<Vec<i32>>().unwrap());
1279 assert_eq!(vec![1, 2], tuple[..2].extract::<Vec<i32>>().unwrap());
1280 assert_eq!(vec![1, 2], tuple[..=1].extract::<Vec<i32>>().unwrap());
1281 })
1282 }
1283
1284 #[test]
1285 #[should_panic = "range start index 5 out of range for tuple of length 3"]
1286 #[cfg(feature = "gil-refs")]
1287 #[allow(deprecated)]
1288 fn test_tuple_index_trait_range_panic_start() {
1289 Python::with_gil(|py| {
1290 let ob = (1, 2, 3).to_object(py);
1291 let tuple: &PyTuple = ob.downcast(py).unwrap();
1292 tuple[5..10].extract::<Vec<i32>>().unwrap();
1293 })
1294 }
1295
1296 #[test]
1297 #[should_panic = "range end index 10 out of range for tuple of length 3"]
1298 #[cfg(feature = "gil-refs")]
1299 #[allow(deprecated)]
1300 fn test_tuple_index_trait_range_panic_end() {
1301 Python::with_gil(|py| {
1302 let ob = (1, 2, 3).to_object(py);
1303 let tuple: &PyTuple = ob.downcast(py).unwrap();
1304 tuple[1..10].extract::<Vec<i32>>().unwrap();
1305 })
1306 }
1307
1308 #[test]
1309 #[should_panic = "slice index starts at 2 but ends at 1"]
1310 #[cfg(feature = "gil-refs")]
1311 #[allow(deprecated)]
1312 fn test_tuple_index_trait_range_panic_wrong_order() {
1313 Python::with_gil(|py| {
1314 let ob = (1, 2, 3).to_object(py);
1315 let tuple: &PyTuple = ob.downcast(py).unwrap();
1316 #[allow(clippy::reversed_empty_ranges)]
1317 tuple[2..1].extract::<Vec<i32>>().unwrap();
1318 })
1319 }
1320
1321 #[test]
1322 #[should_panic = "range start index 8 out of range for tuple of length 3"]
1323 #[cfg(feature = "gil-refs")]
1324 #[allow(deprecated)]
1325 fn test_tuple_index_trait_range_from_panic() {
1326 Python::with_gil(|py| {
1327 let ob = (1, 2, 3).to_object(py);
1328 let tuple: &PyTuple = ob.downcast(py).unwrap();
1329 tuple[8..].extract::<Vec<i32>>().unwrap();
1330 })
1331 }
1332
1333 #[test]
1334 fn test_tuple_contains() {
1335 Python::with_gil(|py| {
1336 let ob = (1, 1, 2, 3, 5, 8).to_object(py);
1337 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1338 assert_eq!(6, tuple.len());
1339
1340 let bad_needle = 7i32.to_object(py);
1341 assert!(!tuple.contains(&bad_needle).unwrap());
1342
1343 let good_needle = 8i32.to_object(py);
1344 assert!(tuple.contains(&good_needle).unwrap());
1345
1346 let type_coerced_needle = 8f32.to_object(py);
1347 assert!(tuple.contains(&type_coerced_needle).unwrap());
1348 });
1349 }
1350
1351 #[test]
1352 fn test_tuple_index() {
1353 Python::with_gil(|py| {
1354 let ob = (1, 1, 2, 3, 5, 8).to_object(py);
1355 let tuple = ob.downcast_bound::<PyTuple>(py).unwrap();
1356 assert_eq!(0, tuple.index(1i32).unwrap());
1357 assert_eq!(2, tuple.index(2i32).unwrap());
1358 assert_eq!(3, tuple.index(3i32).unwrap());
1359 assert_eq!(4, tuple.index(5i32).unwrap());
1360 assert_eq!(5, tuple.index(8i32).unwrap());
1361 assert!(tuple.index(42i32).is_err());
1362 });
1363 }
1364
1365 use std::ops::Range;
1366
1367 struct FaultyIter(Range<usize>, usize);
1370
1371 impl Iterator for FaultyIter {
1372 type Item = usize;
1373
1374 fn next(&mut self) -> Option<Self::Item> {
1375 self.0.next()
1376 }
1377 }
1378
1379 impl ExactSizeIterator for FaultyIter {
1380 fn len(&self) -> usize {
1381 self.1
1382 }
1383 }
1384
1385 #[test]
1386 #[should_panic(
1387 expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1388 )]
1389 fn too_long_iterator() {
1390 Python::with_gil(|py| {
1391 let iter = FaultyIter(0..usize::MAX, 73);
1392 let _tuple = PyTuple::new_bound(py, iter);
1393 })
1394 }
1395
1396 #[test]
1397 #[should_panic(
1398 expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1399 )]
1400 fn too_short_iterator() {
1401 Python::with_gil(|py| {
1402 let iter = FaultyIter(0..35, 73);
1403 let _tuple = PyTuple::new_bound(py, iter);
1404 })
1405 }
1406
1407 #[test]
1408 #[should_panic(
1409 expected = "out of range integral type conversion attempted on `elements.len()`"
1410 )]
1411 fn overflowing_size() {
1412 Python::with_gil(|py| {
1413 let iter = FaultyIter(0..0, usize::MAX);
1414
1415 let _tuple = PyTuple::new_bound(py, iter);
1416 })
1417 }
1418
1419 #[cfg(feature = "macros")]
1420 #[test]
1421 fn bad_clone_mem_leaks() {
1422 use crate::{IntoPy, Py, PyAny};
1423 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1424
1425 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1426
1427 #[crate::pyclass]
1428 #[pyo3(crate = "crate")]
1429 struct Bad(usize);
1430
1431 impl Clone for Bad {
1432 fn clone(&self) -> Self {
1433 assert_ne!(self.0, 42);
1435 NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1436
1437 Bad(self.0)
1438 }
1439 }
1440
1441 impl Drop for Bad {
1442 fn drop(&mut self) {
1443 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1444 }
1445 }
1446
1447 impl ToPyObject for Bad {
1448 fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
1449 self.to_owned().into_py(py)
1450 }
1451 }
1452
1453 struct FaultyIter(Range<usize>, usize);
1454
1455 impl Iterator for FaultyIter {
1456 type Item = Bad;
1457
1458 fn next(&mut self) -> Option<Self::Item> {
1459 self.0.next().map(|i| {
1460 NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1461 Bad(i)
1462 })
1463 }
1464 }
1465
1466 impl ExactSizeIterator for FaultyIter {
1467 fn len(&self) -> usize {
1468 self.1
1469 }
1470 }
1471
1472 Python::with_gil(|py| {
1473 std::panic::catch_unwind(|| {
1474 let iter = FaultyIter(0..50, 50);
1475 let _tuple = PyTuple::new_bound(py, iter);
1476 })
1477 .unwrap_err();
1478 });
1479
1480 assert_eq!(
1481 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1482 0,
1483 "Some destructors did not run"
1484 );
1485 }
1486
1487 #[cfg(feature = "macros")]
1488 #[test]
1489 fn bad_clone_mem_leaks_2() {
1490 use crate::{IntoPy, Py, PyAny};
1491 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1492
1493 static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1494
1495 #[crate::pyclass]
1496 #[pyo3(crate = "crate")]
1497 struct Bad(usize);
1498
1499 impl Clone for Bad {
1500 fn clone(&self) -> Self {
1501 assert_ne!(self.0, 3);
1503 NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1504
1505 Bad(self.0)
1506 }
1507 }
1508
1509 impl Drop for Bad {
1510 fn drop(&mut self) {
1511 NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1512 }
1513 }
1514
1515 impl ToPyObject for Bad {
1516 fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
1517 self.to_owned().into_py(py)
1518 }
1519 }
1520
1521 let s = (Bad(1), Bad(2), Bad(3), Bad(4));
1522 NEEDS_DESTRUCTING_COUNT.store(4, SeqCst);
1523 Python::with_gil(|py| {
1524 std::panic::catch_unwind(|| {
1525 let _tuple: Py<PyAny> = s.to_object(py);
1526 })
1527 .unwrap_err();
1528 });
1529 drop(s);
1530
1531 assert_eq!(
1532 NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1533 0,
1534 "Some destructors did not run"
1535 );
1536 }
1537
1538 #[test]
1539 fn test_tuple_to_list() {
1540 Python::with_gil(|py| {
1541 let tuple = PyTuple::new_bound(py, vec![1, 2, 3]);
1542 let list = tuple.to_list();
1543 let list_expected = PyList::new_bound(py, vec![1, 2, 3]);
1544 assert!(list.eq(list_expected).unwrap());
1545 })
1546 }
1547
1548 #[test]
1549 fn test_tuple_as_sequence() {
1550 Python::with_gil(|py| {
1551 let tuple = PyTuple::new_bound(py, vec![1, 2, 3]);
1552 let sequence = tuple.as_sequence();
1553 assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
1554 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1555
1556 assert_eq!(tuple.len(), 3);
1557 assert_eq!(sequence.len().unwrap(), 3);
1558 })
1559 }
1560
1561 #[test]
1562 fn test_tuple_into_sequence() {
1563 Python::with_gil(|py| {
1564 let tuple = PyTuple::new_bound(py, vec![1, 2, 3]);
1565 let sequence = tuple.into_sequence();
1566 assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1567 assert_eq!(sequence.len().unwrap(), 3);
1568 })
1569 }
1570
1571 #[test]
1572 fn test_bound_tuple_get_item() {
1573 Python::with_gil(|py| {
1574 let tuple = PyTuple::new_bound(py, vec![1, 2, 3, 4]);
1575
1576 assert_eq!(tuple.len(), 4);
1577 assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
1578 assert_eq!(
1579 tuple
1580 .get_borrowed_item(1)
1581 .unwrap()
1582 .extract::<i32>()
1583 .unwrap(),
1584 2
1585 );
1586 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
1587 {
1588 assert_eq!(
1589 unsafe { tuple.get_item_unchecked(2) }
1590 .extract::<i32>()
1591 .unwrap(),
1592 3
1593 );
1594 assert_eq!(
1595 unsafe { tuple.get_borrowed_item_unchecked(3) }
1596 .extract::<i32>()
1597 .unwrap(),
1598 4
1599 );
1600 }
1601 })
1602 }
1603}