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