Skip to content

Commit 5e68706

Browse files
committed
improve tuple methods test coverage
1 parent 03f5cfd commit 5e68706

File tree

2 files changed

+121
-13
lines changed

2 files changed

+121
-13
lines changed

src/types/tuple.rs

Lines changed: 120 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyT
6060
impl PyTuple {
6161
/// Deprecated form of `PyTuple::new_bound`.
6262
#[track_caller]
63-
#[deprecated(
64-
since = "0.21.0",
65-
note = "`PyTuple::new` will be replaced by `PyTuple::new_bound` in a future PyO3 version"
63+
#[cfg_attr(
64+
not(feature = "gil-refs"),
65+
deprecated(
66+
since = "0.21.0",
67+
note = "`PyTuple::new` will be replaced by `PyTuple::new_bound` in a future PyO3 version"
68+
)
6669
)]
6770
pub fn new<T, U>(
6871
py: Python<'_>,
@@ -115,9 +118,12 @@ impl PyTuple {
115118
}
116119

117120
/// Deprecated form of `PyTuple::empty_bound`.
118-
#[deprecated(
119-
since = "0.21.0",
120-
note = "`PyTuple::empty` will be replaced by `PyTuple::empty_bound` in a future PyO3 version"
121+
#[cfg_attr(
122+
not(feature = "gil-refs"),
123+
deprecated(
124+
since = "0.21.0",
125+
note = "`PyTuple::empty` will be replaced by `PyTuple::empty_bound` in a future PyO3 version"
126+
)
121127
)]
122128
pub fn empty(py: Python<'_>) -> &PyTuple {
123129
Self::empty_bound(py).into_gil_ref()
@@ -361,7 +367,7 @@ impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
361367
}
362368

363369
fn get_borrowed_item<'a>(&'a self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
364-
Borrowed::from(self).get_borrowed_item(index)
370+
self.as_borrowed().get_borrowed_item(index)
365371
}
366372

367373
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
@@ -371,7 +377,7 @@ impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
371377

372378
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
373379
unsafe fn get_borrowed_item_unchecked<'a>(&'a self, index: usize) -> Borrowed<'a, 'py, PyAny> {
374-
Borrowed::from(self).get_borrowed_item_unchecked(index)
380+
self.as_borrowed().get_borrowed_item_unchecked(index)
375381
}
376382

377383
#[cfg(not(Py_LIMITED_API))]
@@ -406,7 +412,7 @@ impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
406412
}
407413

408414
fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
409-
BorrowedTupleIterator::new(Borrowed::from(self))
415+
BorrowedTupleIterator::new(self.as_borrowed())
410416
}
411417

412418
fn to_list(&self) -> Bound<'py, PyList> {
@@ -498,7 +504,7 @@ impl<'py> Iterator for BoundTupleIterator<'py> {
498504
fn next(&mut self) -> Option<Self::Item> {
499505
if self.index < self.length {
500506
let item = unsafe {
501-
BorrowedTupleIterator::get_item(Borrowed::from(&self.tuple), self.index).to_owned()
507+
BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.index).to_owned()
502508
};
503509
self.index += 1;
504510
Some(item)
@@ -519,7 +525,7 @@ impl<'py> DoubleEndedIterator for BoundTupleIterator<'py> {
519525
fn next_back(&mut self) -> Option<Self::Item> {
520526
if self.index < self.length {
521527
let item = unsafe {
522-
BorrowedTupleIterator::get_item(Borrowed::from(&self.tuple), self.length - 1)
528+
BorrowedTupleIterator::get_item(self.tuple.as_borrowed(), self.length - 1)
523529
.to_owned()
524530
};
525531
self.length -= 1;
@@ -799,7 +805,7 @@ tuple_conversion!(
799805
#[cfg(test)]
800806
#[allow(deprecated)] // TODO: remove allow when GIL Pool is removed
801807
mod tests {
802-
use crate::types::{PyAny, PyList, PyTuple};
808+
use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods, PyAny, PyList, PyTuple};
803809
use crate::{Python, ToPyObject};
804810
use std::collections::HashSet;
805811

@@ -824,11 +830,21 @@ mod tests {
824830
let ob = (1, 2, 3).to_object(py);
825831
let tuple: &PyTuple = ob.downcast(py).unwrap();
826832
assert_eq!(3, tuple.len());
833+
assert!(!tuple.is_empty());
827834
let ob: &PyAny = tuple.into();
828835
assert_eq!((1, 2, 3), ob.extract().unwrap());
829836
});
830837
}
831838

839+
#[test]
840+
fn test_empty() {
841+
Python::with_gil(|py| {
842+
let tuple = PyTuple::empty(py);
843+
assert!(tuple.is_empty());
844+
assert_eq!(0, tuple.len());
845+
});
846+
}
847+
832848
#[test]
833849
fn test_slice() {
834850
Python::with_gil(|py| {
@@ -888,6 +904,52 @@ mod tests {
888904
});
889905
}
890906

907+
#[test]
908+
fn test_bound_iter() {
909+
Python::with_gil(|py| {
910+
let tuple = PyTuple::new_bound(py, [1, 2, 3]);
911+
assert_eq!(3, tuple.len());
912+
let mut iter = tuple.iter();
913+
914+
assert_eq!(iter.size_hint(), (3, Some(3)));
915+
916+
assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
917+
assert_eq!(iter.size_hint(), (2, Some(2)));
918+
919+
assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
920+
assert_eq!(iter.size_hint(), (1, Some(1)));
921+
922+
assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
923+
assert_eq!(iter.size_hint(), (0, Some(0)));
924+
925+
assert!(iter.next().is_none());
926+
assert!(iter.next().is_none());
927+
});
928+
}
929+
930+
#[test]
931+
fn test_bound_iter_rev() {
932+
Python::with_gil(|py| {
933+
let tuple = PyTuple::new_bound(py, [1, 2, 3]);
934+
assert_eq!(3, tuple.len());
935+
let mut iter = tuple.iter().rev();
936+
937+
assert_eq!(iter.size_hint(), (3, Some(3)));
938+
939+
assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
940+
assert_eq!(iter.size_hint(), (2, Some(2)));
941+
942+
assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
943+
assert_eq!(iter.size_hint(), (1, Some(1)));
944+
945+
assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap());
946+
assert_eq!(iter.size_hint(), (0, Some(0)));
947+
948+
assert!(iter.next().is_none());
949+
assert!(iter.next().is_none());
950+
});
951+
}
952+
891953
#[test]
892954
fn test_into_iter() {
893955
Python::with_gil(|py| {
@@ -1310,4 +1372,50 @@ mod tests {
13101372
assert!(list.eq(list_expected).unwrap());
13111373
})
13121374
}
1375+
1376+
#[test]
1377+
fn test_tuple_as_sequence() {
1378+
Python::with_gil(|py| {
1379+
let tuple = PyTuple::new(py, vec![1, 2, 3]);
1380+
let sequence = tuple.as_sequence();
1381+
assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
1382+
assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
1383+
1384+
assert_eq!(tuple.len(), 3);
1385+
assert_eq!(sequence.len().unwrap(), 3);
1386+
})
1387+
}
1388+
1389+
#[test]
1390+
fn test_bound_tuple_get_item() {
1391+
Python::with_gil(|py| {
1392+
let tuple = PyTuple::new_bound(py, vec![1, 2, 3, 4]);
1393+
1394+
assert_eq!(tuple.len(), 4);
1395+
assert_eq!(tuple.get_item(0).unwrap().extract::<i32>().unwrap(), 1);
1396+
assert_eq!(
1397+
tuple
1398+
.get_borrowed_item(1)
1399+
.unwrap()
1400+
.extract::<i32>()
1401+
.unwrap(),
1402+
2
1403+
);
1404+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
1405+
{
1406+
assert_eq!(
1407+
unsafe { tuple.get_item_unchecked(2) }
1408+
.extract::<i32>()
1409+
.unwrap(),
1410+
3
1411+
);
1412+
assert_eq!(
1413+
unsafe { tuple.get_borrowed_item_unchecked(3) }
1414+
.extract::<i32>()
1415+
.unwrap(),
1416+
4
1417+
);
1418+
}
1419+
})
1420+
}
13131421
}

tests/ui/invalid_result_conversion.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
66
|
77
= help: the following other types implement trait `From<T>`:
88
<PyErr as From<PyBorrowError>>
9-
<PyErr as From<std::io::Error>>
109
<PyErr as From<PyBorrowMutError>>
10+
<PyErr as From<std::io::Error>>
1111
<PyErr as From<PyDowncastError<'a>>>
1212
<PyErr as From<DowncastError<'_, '_>>>
1313
<PyErr as From<DowncastIntoError<'_>>>

0 commit comments

Comments
 (0)