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