Skip to content

Commit

Permalink
Add PyList::slice and fix index types of PyTuple::slice, PyList::inse…
Browse files Browse the repository at this point in the history
…rt and PyList::set_item.

NB: the behavior on out-of-range indices hasn't changed;
it was merely wrongly documented before.

See #1667
  • Loading branch information
birkenfeld committed Aug 17, 2021
1 parent 336e87e commit ed51694
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 13 deletions.
50 changes: 43 additions & 7 deletions src/types/list.rs
Expand Up @@ -83,17 +83,34 @@ impl PyList {
}
}

/// Takes the slice `self[low:high]` and returns it as a new list.
///
/// Out-of-range indices are clipped to `self.len()`.
pub fn slice(&self, low: usize, high: usize) -> &PyList {
unsafe {
self.py().from_owned_ptr(ffi::PyList_GetSlice(
self.as_ptr(),
low as Py_ssize_t,
high as Py_ssize_t,
))
}
}

/// Sets the item at the specified index.
///
/// Panics if the index is out of range.
pub fn set_item<I>(&self, index: isize, item: I) -> PyResult<()>
/// Raises `IndexError` if the index is out of range.
pub fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToPyObject,
{
unsafe {
err::error_on_minusone(
self.py(),
ffi::PyList_SetItem(self.as_ptr(), index, item.to_object(self.py()).into_ptr()),
ffi::PyList_SetItem(
self.as_ptr(),
index as Py_ssize_t,
item.to_object(self.py()).into_ptr(),
),
)
}
}
Expand All @@ -110,13 +127,16 @@ impl PyList {

/// Inserts an item at the specified index.
///
/// Panics if the index is out of range.
pub fn insert<I>(&self, index: isize, item: I) -> PyResult<()>
/// If `index >= self.len()`, inserts at the end.
pub fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToBorrowedObject,
{
item.with_borrowed_ptr(self.py(), |item| unsafe {
err::error_on_minusone(self.py(), ffi::PyList_Insert(self.as_ptr(), index, item))
err::error_on_minusone(
self.py(),
ffi::PyList_Insert(self.as_ptr(), index as Py_ssize_t, item),
)
})
}

Expand Down Expand Up @@ -251,14 +271,27 @@ mod tests {
});
}

#[test]
fn test_slice() {
Python::with_gil(|py| {
let list = PyList::new(py, &[2, 3, 5, 7]);
let slice = list.slice(1, 3);
assert_eq!(2, slice.len());
let slice = list.slice(1, 7);
assert_eq!(3, slice.len());
});
}

#[test]
fn test_set_item() {
Python::with_gil(|py| {
let list = PyList::new(py, &[2, 3, 5, 7]);
let val = 42i32.to_object(py);
let val2 = 42i32.to_object(py);
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
list.set_item(0, val).unwrap();
assert_eq!(42, list.get_item(0).extract::<i32>().unwrap());
assert!(list.set_item(10, val2).is_err());
});
}

Expand All @@ -285,12 +318,15 @@ mod tests {
Python::with_gil(|py| {
let list = PyList::new(py, &[2, 3, 5, 7]);
let val = 42i32.to_object(py);
let val2 = 43i32.to_object(py);
assert_eq!(4, list.len());
assert_eq!(2, list.get_item(0).extract::<i32>().unwrap());
list.insert(0, val).unwrap();
assert_eq!(5, list.len());
list.insert(1000, val2).unwrap();
assert_eq!(6, list.len());
assert_eq!(42, list.get_item(0).extract::<i32>().unwrap());
assert_eq!(2, list.get_item(1).extract::<i32>().unwrap());
assert_eq!(43, list.get_item(5).extract::<i32>().unwrap());
});
}

Expand Down
29 changes: 23 additions & 6 deletions src/types/tuple.rs
Expand Up @@ -57,18 +57,24 @@ impl PyTuple {
self.len() == 0
}

/// Takes a slice of the tuple pointed from `low` to `high` and returns it as a new tuple.
pub fn slice(&self, low: isize, high: isize) -> &PyTuple {
/// Takes the slice `self[low:high]` and returns it as a new tuple.
///
/// Out-of-range indices are clipped to `self.len()`.
pub fn slice(&self, low: usize, high: usize) -> &PyTuple {
unsafe {
self.py()
.from_owned_ptr(ffi::PyTuple_GetSlice(self.as_ptr(), low, high))
self.py().from_owned_ptr(ffi::PyTuple_GetSlice(
self.as_ptr(),
low as Py_ssize_t,
high as Py_ssize_t,
))
}
}

/// Takes a slice of the tuple from `low` to the end and returns it as a new tuple.
pub fn split_from(&self, low: isize) -> &PyTuple {
pub fn split_from(&self, low: usize) -> &PyTuple {
unsafe {
let ptr = ffi::PyTuple_GetSlice(self.as_ptr(), low, self.len() as Py_ssize_t);
let ptr =
ffi::PyTuple_GetSlice(self.as_ptr(), low as Py_ssize_t, self.len() as Py_ssize_t);
self.py().from_owned_ptr(ptr)
}
}
Expand Down Expand Up @@ -346,6 +352,17 @@ mod tests {
});
}

#[test]
fn test_slice() {
Python::with_gil(|py| {
let tup = PyTuple::new(py, &[2, 3, 5, 7]);
let slice = tup.slice(1, 3);
assert_eq!(2, slice.len());
let slice = tup.slice(1, 7);
assert_eq!(3, slice.len());
});
}

#[test]
fn test_iter() {
Python::with_gil(|py| {
Expand Down

0 comments on commit ed51694

Please sign in to comment.