use crate::ffi::{self, Py_ssize_t};
use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::{type_hint_subscript, PyStaticExpr};
use crate::instance::Borrowed;
use crate::internal_tricks::get_ssize_index;
#[cfg(feature = "experimental-inspect")]
use crate::type_object::PyTypeInfo;
use crate::types::{sequence::PySequenceMethods, PyList, PySequence};
#[cfg(all(
not(any(PyPy, GraalPy)),
any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
))]
use crate::BoundObject;
use crate::{
exceptions, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, PyResult, Python,
};
#[cfg(RustPython)]
use crate::{
py_result_ext::PyResultExt,
sync::PyOnceLock,
types::{PyType, PyTypeMethods},
Py,
};
use core::iter::FusedIterator;
#[cfg(feature = "nightly")]
use core::num::NonZero;
#[cfg(all(
not(any(PyPy, GraalPy)),
any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
))]
use libc::size_t;
#[inline]
#[track_caller]
#[cfg_attr(RustPython, allow(unused_mut))]
fn try_new_from_iter<'py>(
py: Python<'py>,
mut elements: impl ExactSizeIterator<Item = PyResult<Bound<'py, PyAny>>>,
) -> PyResult<Bound<'py, PyTuple>> {
#[cfg(not(RustPython))]
unsafe {
let len: Py_ssize_t = elements
.len()
.try_into()
.expect("out of range integral type conversion attempted on `elements.len()`");
let ptr = ffi::PyTuple_New(len);
let tup = ptr.assume_owned(py).cast_into_unchecked();
let mut counter: Py_ssize_t = 0;
for obj in (&mut elements).take(len as usize) {
#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
ffi::PyTuple_SET_ITEM(ptr, counter, obj?.into_ptr());
#[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
ffi::PyTuple_SetItem(ptr, counter, obj?.into_ptr());
counter += 1;
}
assert!(elements.next().is_none(), "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation.");
assert_eq!(len, counter, "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation.");
Ok(tup)
}
#[cfg(RustPython)]
unsafe {
let elements = elements.collect::<PyResult<Vec<_>>>()?;
ffi::PyTuple_FromArray(elements.as_ptr().cast(), elements.len() as _)
.assume_owned_or_err(py)
.cast_into_unchecked()
}
}
#[repr(transparent)]
pub struct PyTuple(PyAny);
#[cfg(not(RustPython))]
pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), "builtins", "tuple", #checkfunction=ffi::PyTuple_Check);
#[cfg(RustPython)]
pyobject_native_type_core!(
PyTuple,
|py| {
static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
TYPE.import(py, "builtins", "tuple").unwrap().as_type_ptr()
},
"builtins",
"tuple",
#checkfunction=ffi::PyTuple_Check
);
impl PyTuple {
#[track_caller]
pub fn new<'py, T, U>(
py: Python<'py>,
elements: impl IntoIterator<Item = T, IntoIter = U>,
) -> PyResult<Bound<'py, PyTuple>>
where
T: IntoPyObject<'py>,
U: ExactSizeIterator<Item = T>,
{
let elements = elements.into_iter().map(|e| e.into_bound_py_any(py));
try_new_from_iter(py, elements)
}
pub fn empty(py: Python<'_>) -> Bound<'_, PyTuple> {
unsafe { ffi::PyTuple_New(0).assume_owned(py).cast_into_unchecked() }
}
}
#[doc(alias = "PyTuple")]
pub trait PyTupleMethods<'py>: crate::sealed::Sealed {
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn as_sequence(&self) -> &Bound<'py, PySequence>;
fn into_sequence(self) -> Bound<'py, PySequence>;
fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple>;
fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>>;
unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny>;
#[cfg(not(any(Py_LIMITED_API, GraalPy)))]
fn as_slice(&self) -> &[Bound<'py, PyAny>];
fn contains<V>(&self, value: V) -> PyResult<bool>
where
V: IntoPyObject<'py>;
fn index<V>(&self, value: V) -> PyResult<usize>
where
V: IntoPyObject<'py>;
fn iter(&self) -> BoundTupleIterator<'py>;
fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py>;
fn to_list(&self) -> Bound<'py, PyList>;
}
impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
fn len(&self) -> usize {
unsafe {
#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
let size = ffi::PyTuple_GET_SIZE(self.as_ptr());
#[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
let size = ffi::PyTuple_Size(self.as_ptr());
size as usize
}
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn as_sequence(&self) -> &Bound<'py, PySequence> {
unsafe { self.cast_unchecked() }
}
fn into_sequence(self) -> Bound<'py, PySequence> {
unsafe { self.cast_into_unchecked() }
}
fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple> {
unsafe {
ffi::PyTuple_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
.assume_owned(self.py())
.cast_into_unchecked()
}
}
fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
self.get_borrowed_item(index).map(Borrowed::to_owned)
}
fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
self.as_borrowed().get_borrowed_item(index)
}
unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
unsafe { self.get_borrowed_item_unchecked(index).to_owned() }
}
unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny> {
unsafe { self.as_borrowed().get_borrowed_item_unchecked(index) }
}
#[cfg(not(any(Py_LIMITED_API, GraalPy)))]
fn as_slice(&self) -> &[Bound<'py, PyAny>] {
let items = unsafe { &(*self.as_ptr().cast::<ffi::PyTupleObject>()).ob_item };
unsafe { core::slice::from_raw_parts(items.as_ptr().cast(), self.len()) }
}
#[inline]
fn contains<V>(&self, value: V) -> PyResult<bool>
where
V: IntoPyObject<'py>,
{
self.as_sequence().contains(value)
}
#[inline]
fn index<V>(&self, value: V) -> PyResult<usize>
where
V: IntoPyObject<'py>,
{
self.as_sequence().index(value)
}
fn iter(&self) -> BoundTupleIterator<'py> {
BoundTupleIterator::new(self.clone())
}
fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
self.as_borrowed().iter_borrowed()
}
fn to_list(&self) -> Bound<'py, PyList> {
self.as_sequence()
.to_list()
.expect("failed to convert tuple to list")
}
}
impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
fn get_borrowed_item(self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
unsafe {
ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
.assume_borrowed_or_err(self.py())
}
}
unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
cfg_select! {
not(any(Py_LIMITED_API, PyPy, GraalPy)) => unsafe {
ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t)
.assume_borrowed_unchecked(self.py())
},
any(Py_LIMITED_API, PyPy, GraalPy) => unsafe {
ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
.assume_borrowed_unchecked(self.py())
}
}
}
pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
BorrowedTupleIterator::new(self)
}
}
pub struct BoundTupleIterator<'py> {
tuple: Bound<'py, PyTuple>,
index: usize,
length: usize,
}
impl<'py> BoundTupleIterator<'py> {
fn new(tuple: Bound<'py, PyTuple>) -> Self {
let length = tuple.len();
BoundTupleIterator {
tuple,
index: 0,
length,
}
}
}
impl<'py> Iterator for BoundTupleIterator<'py> {
type Item = Bound<'py, PyAny>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.length {
let item = unsafe { self.tuple.get_item_unchecked(self.index) };
self.index += 1;
Some(item)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.len()
}
#[inline]
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
#[inline]
#[cfg(not(feature = "nightly"))]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if let Some(target_index) = self.index.checked_add(n) {
if target_index < self.length {
let item = unsafe { self.tuple.get_item_unchecked(target_index) };
self.index = target_index + 1;
return Some(item);
}
}
self.index = self.length;
None
}
#[inline]
#[cfg(feature = "nightly")]
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
let items_left = self.length.saturating_sub(self.index);
if let Some(overflow) = NonZero::new(n.saturating_sub(items_left)) {
self.index = self.length;
return Err(overflow);
}
self.index += n;
Ok(())
}
}
impl DoubleEndedIterator for BoundTupleIterator<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.index < self.length {
let target_index = self.length - 1;
let item = unsafe { self.tuple.get_item_unchecked(target_index) };
self.length = target_index;
Some(item)
} else {
None
}
}
#[inline]
#[cfg(not(feature = "nightly"))]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
if let Some(index_after_item) = self.length.checked_sub(n) {
if self.index < index_after_item {
let target_index = index_after_item - 1;
let item = unsafe { self.tuple.get_item_unchecked(target_index) };
self.length = target_index;
return Some(item);
}
}
self.length = self.index;
None
}
#[inline]
#[cfg(feature = "nightly")]
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
let items_left = self.length.saturating_sub(self.index);
if let Some(overflow) = NonZero::new(n.saturating_sub(items_left)) {
self.length = self.index;
return Err(overflow);
}
self.length -= n;
Ok(())
}
}
impl ExactSizeIterator for BoundTupleIterator<'_> {
fn len(&self) -> usize {
self.length.saturating_sub(self.index)
}
}
impl FusedIterator for BoundTupleIterator<'_> {}
impl<'py> IntoIterator for Bound<'py, PyTuple> {
type Item = Bound<'py, PyAny>;
type IntoIter = BoundTupleIterator<'py>;
fn into_iter(self) -> Self::IntoIter {
BoundTupleIterator::new(self)
}
}
impl<'py> IntoIterator for &Bound<'py, PyTuple> {
type Item = Bound<'py, PyAny>;
type IntoIter = BoundTupleIterator<'py>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct BorrowedTupleIterator<'a, 'py> {
tuple: Borrowed<'a, 'py, PyTuple>,
index: usize,
length: usize,
}
impl<'a, 'py> BorrowedTupleIterator<'a, 'py> {
fn new(tuple: Borrowed<'a, 'py, PyTuple>) -> Self {
let length = tuple.len();
BorrowedTupleIterator {
tuple,
index: 0,
length,
}
}
}
impl<'a, 'py> Iterator for BorrowedTupleIterator<'a, 'py> {
type Item = Borrowed<'a, 'py, PyAny>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.length {
let item = unsafe { self.tuple.get_borrowed_item_unchecked(self.index) };
self.index += 1;
Some(item)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.len()
}
#[inline]
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
}
impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.index < self.length {
let target_index = self.length - 1;
let item = unsafe { self.tuple.get_borrowed_item_unchecked(target_index) };
self.length = target_index;
Some(item)
} else {
None
}
}
}
impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> {
fn len(&self) -> usize {
self.length.saturating_sub(self.index)
}
}
impl FusedIterator for BorrowedTupleIterator<'_, '_> {}
#[cold]
fn wrong_tuple_length(t: Borrowed<'_, '_, PyTuple>, expected_length: usize) -> PyErr {
let msg = format!(
"expected tuple of length {}, but got tuple of length {}",
expected_length,
t.len()
);
exceptions::PyValueError::new_err(msg)
}
macro_rules! tuple_conversion (($length:expr, $(($n:tt, $T:ident)),+) => {
impl <'py, $($T),+> IntoPyObject<'py> for ($($T,)+)
where
$($T: IntoPyObject<'py>,)+
{
type Target = PyTuple;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = type_hint_subscript!(
PyTuple::TYPE_HINT,
$($T::OUTPUT_TYPE),+
);
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
}
}
impl <'a, 'py, $($T),+> IntoPyObject<'py> for &'a ($($T,)+)
where
$(&'a $T: IntoPyObject<'py>,)+
{
type Target = PyTuple;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = type_hint_subscript!(
PyTuple::TYPE_HINT,
$(<&$T>::OUTPUT_TYPE ),+
);
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+]))
}
}
impl<'py, $($T),+> crate::call::private::Sealed for ($($T,)+) where $($T: IntoPyObject<'py>,)+ {}
impl<'py, $($T),+> crate::call::PyCallArgs<'py> for ($($T,)+)
where
$($T: IntoPyObject<'py>,)+
{
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
fn call(
self,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Borrowed<'_, '_, crate::types::PyDict>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = function.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_VectorcallDict(
function.as_ptr(),
args.as_mut_ptr().add(1),
const { with_vectorcall_arguments_offset($length) },
kwargs.as_ptr(),
)
.assume_owned_or_err(py)
}
}
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
fn call_positional(
self,
function: Borrowed<'_, 'py, PyAny>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = function.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
#[cfg(not(Py_LIMITED_API))]
if $length == 1 {
return unsafe {
ffi::PyObject_CallOneArg(
function.as_ptr(),
args_objects.0.as_ptr()
)
.assume_owned_or_err(py)
};
}
let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_Vectorcall(
function.as_ptr(),
args.as_mut_ptr().add(1),
const { with_vectorcall_arguments_offset($length) },
core::ptr::null_mut(),
)
.assume_owned_or_err(py)
}
}
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
fn call_method_positional(
self,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, crate::types::PyString>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = object.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
#[cfg(not(Py_LIMITED_API))]
if $length == 1 {
return unsafe {
ffi::PyObject_CallMethodOneArg(
object.as_ptr(),
method_name.as_ptr(),
args_objects.0.as_ptr()
)
.assume_owned_or_err(py)
};
}
let mut args = [object.as_ptr(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_VectorcallMethod(
method_name.as_ptr(),
args.as_mut_ptr(),
const { with_vectorcall_arguments_offset(1 + $length) },
core::ptr::null_mut(),
)
.assume_owned_or_err(py)
}
}
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
fn call(
self,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
}
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
fn call_positional(
self,
function: Borrowed<'_, 'py, PyAny>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
}
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
fn call_method_positional(
self,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, crate::types::PyString>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
}
}
impl<'a, 'py, $($T),+> crate::call::private::Sealed for &'a ($($T,)+) where $(&'a $T: IntoPyObject<'py>,)+ {}
impl<'a, 'py, $($T),+> crate::call::PyCallArgs<'py> for &'a ($($T,)+)
where
$(&'a $T: IntoPyObject<'py>,)+
{
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
fn call(
self,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Borrowed<'_, '_, crate::types::PyDict>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = function.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_VectorcallDict(
function.as_ptr(),
args.as_mut_ptr().add(1),
const { with_vectorcall_arguments_offset($length) },
kwargs.as_ptr(),
)
.assume_owned_or_err(py)
}
}
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
fn call_positional(
self,
function: Borrowed<'_, 'py, PyAny>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = function.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
#[cfg(not(Py_LIMITED_API))]
if $length == 1 {
return unsafe {
ffi::PyObject_CallOneArg(
function.as_ptr(),
args_objects.0.as_ptr()
)
.assume_owned_or_err(py)
};
}
let mut args = [core::ptr::null_mut(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_Vectorcall(
function.as_ptr(),
args.as_mut_ptr().add(1),
const { with_vectorcall_arguments_offset($length) },
core::ptr::null_mut(),
)
.assume_owned_or_err(py)
}
}
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
fn call_method_positional(
self,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, crate::types::PyString>,
_: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
let py = object.py();
let args_objects = ($(self.$n.into_pyobject_or_pyerr(py)?),*,);
#[cfg(not(Py_LIMITED_API))]
if $length == 1 {
return unsafe {
ffi::PyObject_CallMethodOneArg(
object.as_ptr(),
method_name.as_ptr(),
args_objects.0.as_ptr(),
)
.assume_owned_or_err(py)
};
}
let mut args = [object.as_ptr(), $(args_objects.$n.as_ptr()),*];
unsafe {
ffi::PyObject_VectorcallMethod(
method_name.as_ptr(),
args.as_mut_ptr(),
const { with_vectorcall_arguments_offset(1 + $length) },
core::ptr::null_mut(),
)
.assume_owned_or_err(py)
}
}
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
fn call(
self,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Borrowed<'_, 'py, crate::types::PyDict>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
}
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
fn call_positional(
self,
function: Borrowed<'_, 'py, PyAny>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
}
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
fn call_method_positional(
self,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, crate::types::PyString>,
token: crate::call::private::Token,
) -> PyResult<Bound<'py, PyAny>> {
self.into_pyobject_or_pyerr(object.py())?.call_method_positional(object, method_name, token)
}
}
impl<'a, 'py, $($T: FromPyObject<'a, 'py>),+> FromPyObject<'a, 'py> for ($($T,)+) {
type Error = PyErr;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = type_hint_subscript!(
PyTuple::TYPE_HINT,
$($T::INPUT_TYPE ),+
);
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error>
{
let t = obj.cast::<PyTuple>()?;
if t.len() == $length {
Ok(($(
unsafe { t.get_borrowed_item_unchecked($n) }
.extract::<$T>()
.map_err(Into::into)?,
)+))
} else {
Err(wrong_tuple_length(t, $length))
}
}
}
});
fn array_into_tuple<'py, const N: usize>(
py: Python<'py>,
array: [Bound<'py, PyAny>; N],
) -> Bound<'py, PyTuple> {
#[cfg(not(RustPython))]
unsafe {
let ptr = ffi::PyTuple_New(N.try_into().expect("0 < N <= 12"));
let tup = ptr.assume_owned(py).cast_into_unchecked();
for (index, obj) in array.into_iter().enumerate() {
#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
#[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]
ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr());
}
tup
}
#[cfg(RustPython)]
unsafe {
ffi::PyTuple_FromArray(array.as_ptr().cast(), N.try_into().expect("0 < N <= 12"))
.assume_owned(py)
.cast_into_unchecked()
}
}
#[cfg(all(
not(any(PyPy, GraalPy)),
any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)
))]
const fn with_vectorcall_arguments_offset(n: size_t) -> size_t {
n.checked_add(ffi::PY_VECTORCALL_ARGUMENTS_OFFSET)
.expect("overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET")
}
tuple_conversion!(1, (0, T0));
tuple_conversion!(2, (0, T0), (1, T1));
tuple_conversion!(3, (0, T0), (1, T1), (2, T2));
tuple_conversion!(4, (0, T0), (1, T1), (2, T2), (3, T3));
tuple_conversion!(5, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
tuple_conversion!(6, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
tuple_conversion!(
7,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6)
);
tuple_conversion!(
8,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7)
);
tuple_conversion!(
9,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8)
);
tuple_conversion!(
10,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9)
);
tuple_conversion!(
11,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10)
);
tuple_conversion!(
12,
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10),
(11, T11)
);
#[cfg(test)]
mod tests {
use crate::platform::HashSet;
use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyList, PyTuple};
use crate::{Bound, IntoPyObject, PyAny, Python};
#[cfg(feature = "nightly")]
use core::num::NonZero;
use core::ops::Range;
#[test]
fn test_new() {
Python::attach(|py| {
let ob = PyTuple::new(py, [1, 2, 3]).unwrap();
assert_eq!(3, ob.len());
let ob = ob.as_any();
assert_eq!((1, 2, 3), ob.extract().unwrap());
let mut map = HashSet::new();
map.insert(1);
map.insert(2);
PyTuple::new(py, map).unwrap();
});
}
#[test]
fn test_len() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(3, tuple.len());
assert!(!tuple.is_empty());
let ob = tuple.as_any();
assert_eq!((1, 2, 3), ob.extract().unwrap());
});
}
#[test]
fn test_empty() {
Python::attach(|py| {
let tuple = PyTuple::empty(py);
assert!(tuple.is_empty());
assert_eq!(0, tuple.len());
});
}
#[test]
fn test_slice() {
Python::attach(|py| {
let tup = PyTuple::new(py, [2, 3, 5, 7]).unwrap();
let slice = tup.get_slice(1, 3);
assert_eq!(2, slice.len());
let slice = tup.get_slice(1, 7);
assert_eq!(3, slice.len());
});
}
#[test]
fn test_iter() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(3, tuple.len());
let mut iter = tuple.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (2, Some(2)));
assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (1, Some(1)));
assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (0, Some(0)));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
});
}
#[test]
fn test_iter_rev() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(3, tuple.len());
let mut iter = tuple.iter().rev();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (2, Some(2)));
assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (1, Some(1)));
assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
assert_eq!(iter.size_hint(), (0, Some(0)));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
});
}
#[test]
fn test_bound_iter() {
Python::attach(|py| {
let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
assert_eq!(3, tuple.len());
let mut iter = tuple.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (2, Some(2)));
assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (1, Some(1)));
assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (0, Some(0)));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
});
}
#[test]
fn test_bound_iter_rev() {
Python::attach(|py| {
let tuple = PyTuple::new(py, [1, 2, 3]).unwrap();
assert_eq!(3, tuple.len());
let mut iter = tuple.iter().rev();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(3, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (2, Some(2)));
assert_eq!(2, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (1, Some(1)));
assert_eq!(1, iter.next().unwrap().extract::<i32>().unwrap());
assert_eq!(iter.size_hint(), (0, Some(0)));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
});
}
#[test]
fn test_into_iter() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(3, tuple.len());
for (i, item) in tuple.iter().enumerate() {
assert_eq!(i + 1, item.extract::<'_, usize>().unwrap());
}
});
}
#[test]
fn test_into_iter_bound() {
Python::attach(|py| {
let tuple = (1, 2, 3).into_pyobject(py).unwrap();
assert_eq!(3, tuple.len());
let mut items = vec![];
for item in tuple {
items.push(item.extract::<usize>().unwrap());
}
assert_eq!(items, vec![1, 2, 3]);
});
}
#[test]
#[cfg(not(any(Py_LIMITED_API, GraalPy)))]
fn test_as_slice() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
let slice = tuple.as_slice();
assert_eq!(3, slice.len());
assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap());
assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap());
assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap());
});
}
#[test]
fn test_tuple_lengths_up_to_12() {
Python::attach(|py| {
let t0 = (0,).into_pyobject(py).unwrap();
let t1 = (0, 1).into_pyobject(py).unwrap();
let t2 = (0, 1, 2).into_pyobject(py).unwrap();
let t3 = (0, 1, 2, 3).into_pyobject(py).unwrap();
let t4 = (0, 1, 2, 3, 4).into_pyobject(py).unwrap();
let t5 = (0, 1, 2, 3, 4, 5).into_pyobject(py).unwrap();
let t6 = (0, 1, 2, 3, 4, 5, 6).into_pyobject(py).unwrap();
let t7 = (0, 1, 2, 3, 4, 5, 6, 7).into_pyobject(py).unwrap();
let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).into_pyobject(py).unwrap();
let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).into_pyobject(py).unwrap();
let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.into_pyobject(py)
.unwrap();
let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
.into_pyobject(py)
.unwrap();
assert_eq!(t0.extract::<(i32,)>().unwrap(), (0,));
assert_eq!(t1.extract::<(i32, i32)>().unwrap(), (0, 1,));
assert_eq!(t2.extract::<(i32, i32, i32)>().unwrap(), (0, 1, 2,));
assert_eq!(
t3.extract::<(i32, i32, i32, i32,)>().unwrap(),
(0, 1, 2, 3,)
);
assert_eq!(
t4.extract::<(i32, i32, i32, i32, i32,)>().unwrap(),
(0, 1, 2, 3, 4,)
);
assert_eq!(
t5.extract::<(i32, i32, i32, i32, i32, i32,)>().unwrap(),
(0, 1, 2, 3, 4, 5,)
);
assert_eq!(
t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6,)
);
assert_eq!(
t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6, 7,)
);
assert_eq!(
t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6, 7, 8,)
);
assert_eq!(
t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,)
);
assert_eq!(
t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,)
);
assert_eq!(
t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>()
.unwrap(),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,)
);
})
}
#[test]
fn test_tuple_get_item_invalid_index() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
let obj = tuple.get_item(5);
assert!(obj.is_err());
assert_eq!(
obj.unwrap_err().to_string(),
"IndexError: tuple index out of range"
);
});
}
#[test]
fn test_tuple_get_item_sanity() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
let obj = tuple.get_item(0);
assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
});
}
#[test]
fn test_tuple_get_item_unchecked_sanity() {
Python::attach(|py| {
let ob = (1, 2, 3).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
let obj = unsafe { tuple.get_item_unchecked(0) };
assert_eq!(obj.extract::<i32>().unwrap(), 1);
});
}
#[test]
fn test_tuple_contains() {
Python::attach(|py| {
let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(6, tuple.len());
let bad_needle = 7i32.into_pyobject(py).unwrap();
assert!(!tuple.contains(&bad_needle).unwrap());
let good_needle = 8i32.into_pyobject(py).unwrap();
assert!(tuple.contains(&good_needle).unwrap());
let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
assert!(tuple.contains(&type_coerced_needle).unwrap());
});
}
#[test]
fn test_tuple_index() {
Python::attach(|py| {
let ob = (1, 1, 2, 3, 5, 8).into_pyobject(py).unwrap();
let tuple = ob.cast::<PyTuple>().unwrap();
assert_eq!(0, tuple.index(1i32).unwrap());
assert_eq!(2, tuple.index(2i32).unwrap());
assert_eq!(3, tuple.index(3i32).unwrap());
assert_eq!(4, tuple.index(5i32).unwrap());
assert_eq!(5, tuple.index(8i32).unwrap());
assert!(tuple.index(42i32).is_err());
});
}
struct FaultyIter(Range<usize>, usize);
impl Iterator for FaultyIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl ExactSizeIterator for FaultyIter {
fn len(&self) -> usize {
self.1
}
}
#[test]
#[should_panic(
expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation."
)]
fn too_long_iterator() {
Python::attach(|py| {
let iter = FaultyIter(0..usize::MAX, 73);
let _tuple = PyTuple::new(py, iter);
})
}
#[test]
#[should_panic(
expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
)]
fn too_short_iterator() {
Python::attach(|py| {
let iter = FaultyIter(0..35, 73);
let _tuple = PyTuple::new(py, iter);
})
}
#[test]
#[should_panic(
expected = "out of range integral type conversion attempted on `elements.len()`"
)]
fn overflowing_size() {
Python::attach(|py| {
let iter = FaultyIter(0..0, usize::MAX);
let _tuple = PyTuple::new(py, iter);
})
}
#[test]
#[cfg(panic = "unwind")]
fn bad_intopyobject_doesnt_cause_leaks() {
use crate::types::PyInt;
use core::convert::Infallible;
use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
struct Bad(usize);
impl Drop for Bad {
fn drop(&mut self) {
NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
}
}
impl<'py> IntoPyObject<'py> for Bad {
type Target = PyInt;
type Output = crate::Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
assert_ne!(self.0, 42);
self.0.into_pyobject(py)
}
}
struct FaultyIter(Range<usize>, usize);
impl Iterator for FaultyIter {
type Item = Bad;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|i| {
NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
Bad(i)
})
}
}
impl ExactSizeIterator for FaultyIter {
fn len(&self) -> usize {
self.1
}
}
Python::attach(|py| {
std::panic::catch_unwind(|| {
let iter = FaultyIter(0..50, 50);
let _tuple = PyTuple::new(py, iter);
})
.unwrap_err();
});
assert_eq!(
NEEDS_DESTRUCTING_COUNT.load(SeqCst),
0,
"Some destructors did not run"
);
}
#[test]
#[cfg(panic = "unwind")]
fn bad_intopyobject_doesnt_cause_leaks_2() {
use crate::types::PyInt;
use core::convert::Infallible;
use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
struct Bad(usize);
impl Drop for Bad {
fn drop(&mut self) {
NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
}
}
impl<'py> IntoPyObject<'py> for &Bad {
type Target = PyInt;
type Output = crate::Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
assert_ne!(self.0, 3);
self.0.into_pyobject(py)
}
}
let s = (Bad(1), Bad(2), Bad(3), Bad(4));
NEEDS_DESTRUCTING_COUNT.store(4, SeqCst);
Python::attach(|py| {
std::panic::catch_unwind(|| {
let _tuple = (&s).into_pyobject(py).unwrap();
})
.unwrap_err();
});
drop(s);
assert_eq!(
NEEDS_DESTRUCTING_COUNT.load(SeqCst),
0,
"Some destructors did not run"
);
}
#[test]
fn test_tuple_to_list() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
let list = tuple.to_list();
let list_expected = PyList::new(py, vec![1, 2, 3]).unwrap();
assert!(list.eq(list_expected).unwrap());
})
}
#[test]
fn test_tuple_as_sequence() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
let sequence = tuple.as_sequence();
assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
assert_eq!(tuple.len(), 3);
assert_eq!(sequence.len().unwrap(), 3);
})
}
#[test]
fn test_tuple_into_sequence() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
let sequence = tuple.into_sequence();
assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
assert_eq!(sequence.len().unwrap(), 3);
})
}
#[test]
fn test_bound_tuple_get_item() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
assert_eq!(tuple.len(), 4);
assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
assert_eq!(
tuple
.get_borrowed_item(1)
.unwrap()
.extract::<i32>()
.unwrap(),
2
);
assert_eq!(
unsafe { tuple.get_item_unchecked(2) }
.extract::<i32>()
.unwrap(),
3
);
assert_eq!(
unsafe { tuple.get_borrowed_item_unchecked(3) }
.extract::<i32>()
.unwrap(),
4
);
})
}
fn test_iterators(
tuple: &Bound<'_, PyTuple>,
f: impl Fn(&mut dyn DoubleEndedIterator<Item = Bound<'_, PyAny>>),
) {
let mut iter = tuple.iter();
f(&mut iter);
let mut borrowed_iter = tuple.iter_borrowed().map(|item| item.to_owned());
f(&mut borrowed_iter);
}
#[test]
fn test_tuple_iter_nth() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3, 4]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 2);
assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 4);
assert!(iter.nth(1).is_none());
});
let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
test_iterators(&tuple, |iter| {
iter.next();
assert!(iter.nth(1).is_none());
});
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
test_iterators(&tuple, |iter| {
assert!(iter.nth(10).is_none());
});
let tuple = PyTuple::new(py, vec![6, 7, 8, 9, 10]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 6);
assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 9);
assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 10);
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 9);
assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 8);
assert!(iter.next().is_none());
});
test_iterators(&tuple, |iter| {
assert!(iter.nth(100).is_none());
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
});
test_iterators(&tuple, |iter| {
assert!(iter.next().is_some());
assert!(iter.nth(usize::MAX).is_none());
});
});
}
#[test]
fn test_tuple_iter_nth_back() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 5);
assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
assert!(iter.nth_back(2).is_none());
});
let tuple = PyTuple::new(py, Vec::<i32>::new()).unwrap();
test_iterators(&tuple, |iter| {
assert!(iter.nth_back(0).is_none());
assert!(iter.nth_back(1).is_none());
});
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
test_iterators(&tuple, |iter| {
assert!(iter.nth_back(5).is_none());
});
let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
test_iterators(&tuple, |iter| {
iter.next_back(); assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 1);
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 4);
assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 1);
});
test_iterators(&tuple, |iter| {
iter.next_back();
assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
});
test_iterators(&tuple, |iter| {
iter.nth(1);
assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 3);
assert!(iter.nth_back(0).is_none());
});
test_iterators(&tuple, |iter| {
assert!(iter.nth_back(100).is_none());
assert!(iter.next_back().is_none());
assert!(iter.next().is_none());
});
test_iterators(&tuple, |iter| {
iter.nth(1);
assert!(iter.nth_back(usize::MAX).is_none());
});
});
}
#[cfg(feature = "nightly")]
#[test]
fn test_tuple_iter_advance_by() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_by(2), Ok(()));
assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
assert_eq!(iter.advance_by(0), Ok(()));
assert_eq!(iter.advance_by(100), Err(NonZero::new(98).unwrap()));
assert!(iter.next().is_none());
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_by(6), Err(NonZero::new(1).unwrap()));
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_by(5), Ok(()));
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_by(0), Ok(()));
assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 1);
});
test_iterators(&tuple, |iter| {
assert_eq!(
iter.advance_by(usize::MAX),
Err(NonZero::new(usize::MAX - 5).unwrap())
);
assert_eq!(
iter.advance_by(usize::MAX),
Err(NonZero::new(usize::MAX).unwrap())
);
});
})
}
#[cfg(feature = "nightly")]
#[test]
fn test_tuple_iter_advance_back_by() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3, 4, 5]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_back_by(2), Ok(()));
assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 3);
assert_eq!(iter.advance_back_by(0), Ok(()));
assert_eq!(iter.advance_back_by(100), Err(NonZero::new(98).unwrap()));
assert!(iter.next_back().is_none());
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_back_by(6), Err(NonZero::new(1).unwrap()));
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_back_by(5), Ok(()));
});
test_iterators(&tuple, |iter| {
assert_eq!(iter.advance_back_by(0), Ok(()));
assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 5);
});
test_iterators(&tuple, |iter| {
assert_eq!(
iter.advance_back_by(usize::MAX),
Err(NonZero::new(usize::MAX - 5).unwrap())
);
assert_eq!(
iter.advance_back_by(usize::MAX),
Err(NonZero::new(usize::MAX).unwrap())
);
});
})
}
#[test]
fn test_tuple_iter_last() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
test_iterators(&tuple, |iter| {
let last = iter.last();
assert_eq!(last.unwrap().extract::<i32>().unwrap(), 3);
});
})
}
#[test]
fn test_tuple_iter_count() {
Python::attach(|py| {
let tuple = PyTuple::new(py, vec![1, 2, 3]).unwrap();
test_iterators(&tuple, |iter| {
assert_eq!(iter.count(), 3);
});
})
}
}