Skip to content

Commit c4216a1

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

File tree

3 files changed

+151
-49
lines changed

3 files changed

+151
-49
lines changed

src/types/dict.rs

+2-2
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

+94-45
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 = "this is a deprecated alias for get and get_unchecked"]
7273
pub fn get_item(&self, index: isize) -> &PyAny {
7374
assert!(index >= 0 && index < self.len() as isize);
7475
unsafe {
@@ -83,6 +84,23 @@ 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+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
97+
pub fn get_unchecked(&self, index: usize) -> &PyAny {
98+
unsafe {
99+
let item = ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t);
100+
self.py().from_borrowed_ptr(item)
101+
}
102+
}
103+
86104
/// Sets the item at the specified index.
87105
///
88106
/// Panics if the index is out of range.
@@ -142,16 +160,16 @@ impl PyList {
142160
/// Used by `PyList::iter()`.
143161
pub struct PyListIterator<'a> {
144162
list: &'a PyList,
145-
index: isize,
163+
index: usize,
146164
}
147165

148166
impl<'a> Iterator for PyListIterator<'a> {
149167
type Item = &'a PyAny;
150168

151169
#[inline]
152170
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);
171+
if self.index < self.list.len() {
172+
let item = self.list.get(self.index).expect("list.get failed");
155173
self.index += 1;
156174
Some(item)
157175
} else {
@@ -217,10 +235,10 @@ mod test {
217235
let gil = Python::acquire_gil();
218236
let py = gil.python();
219237
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());
238+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
239+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
240+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
241+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
224242
}
225243

226244
#[test]
@@ -236,19 +254,10 @@ mod test {
236254
let gil = Python::acquire_gil();
237255
let py = gil.python();
238256
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);
257+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
258+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
259+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
260+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
252261
}
253262

254263
#[test]
@@ -257,9 +266,9 @@ mod test {
257266
let py = gil.python();
258267
let list = PyList::new(py, &[2, 3, 5, 7]);
259268
let val = 42i32.to_object(py);
260-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
269+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
261270
list.set_item(0, val).unwrap();
262-
assert_eq!(42, list.get_item(0).extract::<i32>().unwrap());
271+
assert_eq!(42, list.get(0).unwrap().extract::<i32>().unwrap());
263272
}
264273

265274
#[test]
@@ -288,11 +297,11 @@ mod test {
288297
let list = PyList::new(py, &[2, 3, 5, 7]);
289298
let val = 42i32.to_object(py);
290299
assert_eq!(4, list.len());
291-
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
300+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
292301
list.insert(0, val).unwrap();
293302
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());
303+
assert_eq!(42, list.get(0).unwrap().extract::<i32>().unwrap());
304+
assert_eq!(2, list.get(1).unwrap().extract::<i32>().unwrap());
296305
}
297306

298307
#[test]
@@ -318,8 +327,8 @@ mod test {
318327
let py = gil.python();
319328
let list = PyList::new(py, &[2]);
320329
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());
330+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
331+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
323332
}
324333

325334
#[test]
@@ -397,15 +406,15 @@ mod test {
397406
let py = gil.python();
398407
let v = vec![7, 3, 2, 5];
399408
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());
409+
assert_eq!(7, list.get(0).unwrap().extract::<i32>().unwrap());
410+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
411+
assert_eq!(2, list.get(2).unwrap().extract::<i32>().unwrap());
412+
assert_eq!(5, list.get(3).unwrap().extract::<i32>().unwrap());
404413
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());
414+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
415+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
416+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
417+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
409418
}
410419

411420
#[test]
@@ -414,15 +423,15 @@ mod test {
414423
let py = gil.python();
415424
let v = vec![2, 3, 5, 7];
416425
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());
426+
assert_eq!(2, list.get(0).unwrap().extract::<i32>().unwrap());
427+
assert_eq!(3, list.get(1).unwrap().extract::<i32>().unwrap());
428+
assert_eq!(5, list.get(2).unwrap().extract::<i32>().unwrap());
429+
assert_eq!(7, list.get(3).unwrap().extract::<i32>().unwrap());
421430
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());
431+
assert_eq!(7, list.get(0).unwrap().extract::<i32>().unwrap());
432+
assert_eq!(5, list.get(1).unwrap().extract::<i32>().unwrap());
433+
assert_eq!(3, list.get(2).unwrap().extract::<i32>().unwrap());
434+
assert_eq!(2, list.get(3).unwrap().extract::<i32>().unwrap());
426435
}
427436

428437
#[test]
@@ -431,7 +440,47 @@ mod test {
431440
let py = gil.python();
432441
let array: PyObject = [1, 2].into_py(py);
433442
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());
443+
assert_eq!(1, list.get(0).unwrap().extract::<i32>().unwrap());
444+
assert_eq!(2, list.get(1).unwrap().extract::<i32>().unwrap());
445+
}
446+
447+
#[test]
448+
fn test_list_get_invalid_index() {
449+
let gil = Python::acquire_gil();
450+
let py = gil.python();
451+
let list = PyList::new(py, &[2, 3, 5, 7]);
452+
let obj = list.get(5);
453+
assert!(obj.is_err());
454+
assert_eq!(
455+
obj.unwrap_err().to_string(),
456+
"IndexError: list index out of range"
457+
);
458+
}
459+
460+
#[test]
461+
fn test_list_get_sanity() {
462+
let gil = Python::acquire_gil();
463+
let py = gil.python();
464+
let list = PyList::new(py, &[2, 3, 5, 7]);
465+
let obj = list.get(0);
466+
assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
467+
}
468+
469+
#[test]
470+
fn test_list_get_unchecked_sanity() {
471+
let gil = Python::acquire_gil();
472+
let py = gil.python();
473+
let list = PyList::new(py, &[2, 3, 5, 7]);
474+
let obj = list.get_unchecked(0);
475+
assert_eq!(obj.extract::<i32>().unwrap(), 2);
476+
}
477+
478+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
479+
#[test]
480+
fn test_list_get_unchecked_invalid_index() {
481+
let gil = Python::acquire_gil();
482+
let py = gil.python();
483+
let list = PyList::new(py, &[2, 3, 5, 7]);
484+
list.get_unchecked(5);
436485
}
437486
}

src/types/tuple.rs

+55-2
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 = "this is a deprecated alias for get and get_unchecked"]
7980
pub fn get_item(&self, index: usize) -> &PyAny {
8081
assert!(index < self.len());
8182
unsafe {
@@ -88,6 +89,23 @@ 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+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
102+
pub fn get_unchecked(&self, index: usize) -> &PyAny {
103+
unsafe {
104+
let item = ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t);
105+
self.py().from_borrowed_ptr(item)
106+
}
107+
}
108+
91109
/// Returns `self` as a slice of objects.
92110
#[cfg(not(Py_LIMITED_API))]
93111
#[cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))]
@@ -124,7 +142,7 @@ impl<'a> Iterator for PyTupleIterator<'a> {
124142
#[inline]
125143
fn next(&mut self) -> Option<&'a PyAny> {
126144
if self.index < self.length {
127-
let item = self.tuple.get_item(self.index);
145+
let item = self.tuple.get(self.index).expect("tuple.get failed");
128146
self.index += 1;
129147
Some(item)
130148
} else {
@@ -201,7 +219,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
201219
let t = <PyTuple as PyTryFrom>::try_from(obj)?;
202220
if t.len() == $length {
203221
Ok((
204-
$(t.get_item($n).extract::<$T>()?,)+
222+
$(t.get($n)?.extract::<$T>()?,)+
205223
))
206224
} else {
207225
Err(wrong_tuple_length(t, $length))
@@ -458,4 +476,39 @@ mod test {
458476
);
459477
})
460478
}
479+
480+
#[test]
481+
fn test_tuple_get_invalid_index() {
482+
let gil = Python::acquire_gil();
483+
let py = gil.python();
484+
let ob = (1, 2, 3).to_object(py);
485+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
486+
let obj = tuple.get(5);
487+
assert!(obj.is_err());
488+
assert_eq!(
489+
obj.unwrap_err().to_string(),
490+
"IndexError: tuple index out of range"
491+
);
492+
}
493+
494+
#[test]
495+
fn test_tuple_get_sanity() {
496+
let gil = Python::acquire_gil();
497+
let py = gil.python();
498+
let ob = (1, 2, 3).to_object(py);
499+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
500+
let obj = tuple.get(0);
501+
assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1);
502+
}
503+
504+
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
505+
#[test]
506+
fn test_tuple_get_unchecked_sanity() {
507+
let gil = Python::acquire_gil();
508+
let py = gil.python();
509+
let ob = (1, 2, 3).to_object(py);
510+
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
511+
let obj = tuple.get_unchecked(0);
512+
assert_eq!(obj.extract::<i32>().unwrap(), 1);
513+
}
461514
}

0 commit comments

Comments
 (0)