Skip to content

Commit c598465

Browse files
author
Aviram Hassan
committed
deprecate {PyList, PyTuple}::get_item and implement ::get and ::get_unchecked
1 parent bd0e0d8 commit c598465

File tree

3 files changed

+165
-51
lines changed

3 files changed

+165
-51
lines changed

src/types/dict.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,8 @@ mod test {
644644
let mut value_sum = 0;
645645
for el in dict.items().iter() {
646646
let tuple = el.cast_as::<PyTuple>().unwrap();
647-
key_sum += tuple.get_item(0).extract::<i32>().unwrap();
648-
value_sum += tuple.get_item(1).extract::<i32>().unwrap();
647+
key_sum += tuple.get(0).unwrap().extract::<i32>().unwrap();
648+
value_sum += tuple.get(1).unwrap().extract::<i32>().unwrap();
649649
}
650650
assert_eq!(7 + 8 + 9, key_sum);
651651
assert_eq!(32 + 42 + 123, value_sum);

src/types/list.rs

Lines changed: 99 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl PyList {
6969
/// Gets the item at the specified index.
7070
///
7171
/// Panics if the index is out of range.
72+
#[deprecated = "use PyList::get, which does not panic"]
7273
pub fn get_item(&self, index: isize) -> &PyAny {
7374
assert!(index >= 0 && index < self.len() as isize);
7475
unsafe {
@@ -83,6 +84,24 @@ impl PyList {
8384
}
8485
}
8586

87+
/// Gets the list item at the specified index.
88+
pub fn get(&self, index: usize) -> PyResult<&PyAny> {
89+
unsafe {
90+
let item = ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t);
91+
self.py().from_borrowed_ptr_or_err(item)
92+
}
93+
}
94+
95+
/// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
96+
/// # Safety
97+
///
98+
/// Caller must verify that the index is within the bounds of the list.
99+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
100+
pub unsafe fn get_unchecked(&self, index: usize) -> &PyAny {
101+
let item = ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t);
102+
self.py().from_borrowed_ptr(item)
103+
}
104+
86105
/// Sets the item at the specified index.
87106
///
88107
/// Panics if the index is out of range.
@@ -142,16 +161,19 @@ impl PyList {
142161
/// Used by `PyList::iter()`.
143162
pub struct PyListIterator<'a> {
144163
list: &'a PyList,
145-
index: isize,
164+
index: usize,
146165
}
147166

148167
impl<'a> Iterator for PyListIterator<'a> {
149168
type Item = &'a PyAny;
150169

151170
#[inline]
152171
fn next(&mut self) -> Option<&'a PyAny> {
153-
if self.index < self.list.len() as isize {
154-
let item = self.list.get_item(self.index);
172+
if self.index < self.list.len() {
173+
#[cfg(any(Py_LIMITED_API, PyPy))]
174+
let item = self.list.get(self.index).expect("tuple.get failed");
175+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
176+
let item = unsafe { self.list.get_unchecked(self.index) };
155177
self.index += 1;
156178
Some(item)
157179
} else {
@@ -217,10 +239,10 @@ mod test {
217239
let gil = Python::acquire_gil();
218240
let py = gil.python();
219241
let list = PyList::new(py, &[2, 3, 5, 7]);
220-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
221-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
222-
assert_eq!(5, list.get_item(2).extract::<i32>().unwrap());
223-
assert_eq!(7, list.get_item(3).extract::<i32>().unwrap());
242+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
243+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
244+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
245+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
224246
}
225247

226248
#[test]
@@ -236,19 +258,10 @@ mod test {
236258
let gil = Python::acquire_gil();
237259
let py = gil.python();
238260
let list = PyList::new(py, &[2, 3, 5, 7]);
239-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
240-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
241-
assert_eq!(5, list.get_item(2).extract::<i32>().unwrap());
242-
assert_eq!(7, list.get_item(3).extract::<i32>().unwrap());
243-
}
244-
245-
#[test]
246-
#[should_panic]
247-
fn test_get_item_invalid() {
248-
let gil = Python::acquire_gil();
249-
let py = gil.python();
250-
let list = PyList::new(py, &[2, 3, 5, 7]);
251-
list.get_item(-1);
261+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
262+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
263+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
264+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
252265
}
253266

254267
#[test]
@@ -257,9 +270,9 @@ mod test {
257270
let py = gil.python();
258271
let list = PyList::new(py, &[2, 3, 5, 7]);
259272
let val = 42i32.to_object(py);
260-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
273+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
261274
list.set_item(0, val).unwrap();
262-
assert_eq!(42, list.get_item(0).extract::<i32>().unwrap());
275+
assert_eq!(42, list.get(0).unwrap().extract::<i32>().unwrap());
263276
}
264277

265278
#[test]
@@ -288,11 +301,11 @@ mod test {
288301
let list = PyList::new(py, &[2, 3, 5, 7]);
289302
let val = 42i32.to_object(py);
290303
assert_eq!(4, list.len());
291-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
304+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
292305
list.insert(0, val).unwrap();
293306
assert_eq!(5, list.len());
294-
assert_eq!(42, list.get_item(0).extract::<i32>().unwrap());
295-
assert_eq!(2, list.get_item(1).extract::<i32>().unwrap());
307+
assert_eq!(42, list.get(0).unwrap().extract::<i32>().unwrap());
308+
assert_eq!(2, list.get(1).unwrap().extract::<i32>().unwrap());
296309
}
297310

298311
#[test]
@@ -318,8 +331,8 @@ mod test {
318331
let py = gil.python();
319332
let list = PyList::new(py, &[2]);
320333
list.append(3).unwrap();
321-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
322-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
334+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
335+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
323336
}
324337

325338
#[test]
@@ -397,15 +410,15 @@ mod test {
397410
let py = gil.python();
398411
let v = vec![7, 3, 2, 5];
399412
let list = PyList::new(py, &v);
400-
assert_eq!(7, list.get_item(0).extract::<i32>().unwrap());
401-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
402-
assert_eq!(2, list.get_item(2).extract::<i32>().unwrap());
403-
assert_eq!(5, list.get_item(3).extract::<i32>().unwrap());
413+
assert_eq!(7, list.get(0).unwrap().extract::<i32>().unwrap());
414+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
415+
assert_eq!(2, list.get(2).unwrap().extract::<i32>().unwrap());
416+
assert_eq!(5, list.get(3).unwrap().extract::<i32>().unwrap());
404417
list.sort().unwrap();
405-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
406-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
407-
assert_eq!(5, list.get_item(2).extract::<i32>().unwrap());
408-
assert_eq!(7, list.get_item(3).extract::<i32>().unwrap());
418+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
419+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
420+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
421+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
409422
}
410423

411424
#[test]
@@ -414,15 +427,15 @@ mod test {
414427
let py = gil.python();
415428
let v = vec![2, 3, 5, 7];
416429
let list = PyList::new(py, &v);
417-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
418-
assert_eq!(3, list.get_item(1).extract::<i32>().unwrap());
419-
assert_eq!(5, list.get_item(2).extract::<i32>().unwrap());
420-
assert_eq!(7, list.get_item(3).extract::<i32>().unwrap());
430+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
431+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
432+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
433+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
421434
list.reverse().unwrap();
422-
assert_eq!(7, list.get_item(0).extract::<i32>().unwrap());
423-
assert_eq!(5, list.get_item(1).extract::<i32>().unwrap());
424-
assert_eq!(3, list.get_item(2).extract::<i32>().unwrap());
425-
assert_eq!(2, list.get_item(3).extract::<i32>().unwrap());
435+
assert_eq!(7, list.get(0).unwrap().extract::<i32>().unwrap());
436+
assert_eq!(5, list.get(1).unwrap().extract::<i32>().unwrap());
437+
assert_eq!(3, list.get(2).unwrap().extract::<i32>().unwrap());
438+
assert_eq!(2, list.get(3).unwrap().extract::<i32>().unwrap());
426439
}
427440

428441
#[test]
@@ -431,7 +444,48 @@ mod test {
431444
let py = gil.python();
432445
let array: PyObject = [1, 2].into_py(py);
433446
let list = <PyList as PyTryFrom>::try_from(array.as_ref(py)).unwrap();
434-
assert_eq!(1, list.get_item(0).extract::<i32>().unwrap());
435-
assert_eq!(2, list.get_item(1).extract::<i32>().unwrap());
447+
assert_eq!(1, list.get(0).unwrap().extract::<i32>().unwrap());
448+
assert_eq!(2, list.get(1).unwrap().extract::<i32>().unwrap());
449+
}
450+
451+
#[test]
452+
fn test_list_get_invalid_index() {
453+
pyo3::Python::with_gil(|py| {
454+
let list = PyList::new(py, &[2, 3, 5, 7]);
455+
let obj = list.get(5);
456+
assert!(obj.is_err());
457+
assert_eq!(
458+
obj.unwrap_err().to_string(),
459+
"IndexError: list index out of range"
460+
);
461+
});
462+
}
463+
464+
#[test]
465+
fn test_list_get_sanity() {
466+
pyo3::Python::with_gil(|py| {
467+
let list = PyList::new(py, &[2, 3, 5, 7]);
468+
let obj = list.get(0);
469+
assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
470+
});
471+
}
472+
473+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
474+
#[test]
475+
fn test_list_get_unchecked_sanity() {
476+
pyo3::Python::with_gil(|py| {
477+
let list = PyList::new(py, &[2, 3, 5, 7]);
478+
let obj = unsafe { list.get_unchecked(0) };
479+
assert_eq!(obj.extract::<i32>().unwrap(), 2);
480+
});
481+
}
482+
483+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
484+
#[test]
485+
fn test_list_get_unchecked_invalid_index() {
486+
pyo3::Python::with_gil(|py| {
487+
let list = PyList::new(py, &[2, 3, 5, 7]);
488+
unsafe { list.get_unchecked(5) };
489+
});
436490
}
437491
}

src/types/tuple.rs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl PyTuple {
7676
/// Gets the tuple item at the specified index.
7777
///
7878
/// Panics if the index is out of range.
79+
#[deprecated = "use PyTuple::get, which does not panic"]
7980
pub fn get_item(&self, index: usize) -> &PyAny {
8081
assert!(index < self.len());
8182
unsafe {
@@ -88,6 +89,24 @@ impl PyTuple {
8889
}
8990
}
9091

92+
/// Gets the tuple item at the specified index.
93+
pub fn get(&self, index: usize) -> PyResult<&PyAny> {
94+
unsafe {
95+
let item = ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t);
96+
self.py().from_borrowed_ptr_or_err(item)
97+
}
98+
}
99+
100+
/// Gets the tuple item at the specified index. Undefined behavior on bad index. Use with caution.
101+
/// # Safety
102+
///
103+
/// Caller must verify that the index is within the bounds of the list.
104+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
105+
pub unsafe fn get_unchecked(&self, index: usize) -> &PyAny {
106+
let item = ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t);
107+
self.py().from_borrowed_ptr(item)
108+
}
109+
91110
/// Returns `self` as a slice of objects.
92111
#[cfg(not(Py_LIMITED_API))]
93112
#[cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))]
@@ -124,7 +143,10 @@ impl<'a> Iterator for PyTupleIterator<'a> {
124143
#[inline]
125144
fn next(&mut self) -> Option<&'a PyAny> {
126145
if self.index < self.length {
127-
let item = self.tuple.get_item(self.index);
146+
#[cfg(any(Py_LIMITED_API, PyPy))]
147+
let item = self.tuple.get(self.index).expect("tuple.get failed");
148+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
149+
let item = unsafe { self.tuple.get_unchecked(self.index) };
128150
self.index += 1;
129151
Some(item)
130152
} else {
@@ -200,9 +222,12 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
200222
{
201223
let t = <PyTuple as PyTryFrom>::try_from(obj)?;
202224
if t.len() == $length {
203-
Ok((
204-
$(t.get_item($n).extract::<$T>()?,)+
205-
))
225+
#[cfg(any(Py_LIMITED_API, PyPy))]
226+
return Ok(($(t.get($n)?.extract::<$T>()?,)+));
227+
228+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
229+
unsafe {return Ok(($(t.get_unchecked($n).extract::<$T>()?,)+));}
230+
206231
} else {
207232
Err(wrong_tuple_length(t, $length))
208233
}
@@ -458,4 +483,39 @@ mod test {
458483
);
459484
})
460485
}
486+
487+
#[test]
488+
fn test_tuple_get_invalid_index() {
489+
pyo3::Python::with_gil(|py| {
490+
let ob = (1, 2, 3).to_object(py);
491+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
492+
let obj = tuple.get(5);
493+
assert!(obj.is_err());
494+
assert_eq!(
495+
obj.unwrap_err().to_string(),
496+
"IndexError: tuple index out of range"
497+
);
498+
});
499+
}
500+
501+
#[test]
502+
fn test_tuple_get_sanity() {
503+
pyo3::Python::with_gil(|py| {
504+
let ob = (1, 2, 3).to_object(py);
505+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
506+
let obj = tuple.get(0);
507+
assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
508+
});
509+
}
510+
511+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
512+
#[test]
513+
fn test_tuple_get_unchecked_sanity() {
514+
pyo3::Python::with_gil(|py| {
515+
let ob = (1, 2, 3).to_object(py);
516+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
517+
let obj = unsafe { tuple.get_unchecked(0) };
518+
assert_eq!(obj.extract::<i32>().unwrap(), 1);
519+
});
520+
}
461521
}

0 commit comments

Comments
 (0)