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