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