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