Merge pull request #3680 from davidhewitt/list2

implement `PyListMethods`
This commit is contained in:
David Hewitt 2023-12-21 11:13:04 +00:00 committed by GitHub
commit 2788f4a110
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 422 additions and 80 deletions

View File

@ -221,6 +221,8 @@ unsafe impl<T> AsPyPointer for Py2<'_, T> {
/// ///
/// The advantage of this over `&Py2` is that it avoids the need to have a pointer-to-pointer, as Py2 /// The advantage of this over `&Py2` is that it avoids the need to have a pointer-to-pointer, as Py2
/// is already a pointer to an `ffi::PyObject``. /// is already a pointer to an `ffi::PyObject``.
///
/// Similarly, this type is `Copy` and `Clone`, like a shared reference (`&T`).
#[repr(transparent)] #[repr(transparent)]
pub(crate) struct Py2Borrowed<'a, 'py, T>( pub(crate) struct Py2Borrowed<'a, 'py, T>(
NonNull<ffi::PyObject>, NonNull<ffi::PyObject>,
@ -228,6 +230,17 @@ pub(crate) struct Py2Borrowed<'a, 'py, T>(
Python<'py>, Python<'py>,
); );
impl<'py, T> Py2Borrowed<'_, 'py, T> {
/// Creates a new owned `Py2` from this borrowed reference by increasing the reference count.
pub(crate) fn to_owned(self) -> Py2<'py, T> {
unsafe { ffi::Py_INCREF(self.as_ptr()) };
Py2(
self.py(),
ManuallyDrop::new(unsafe { Py::from_non_null(self.0) }),
)
}
}
impl<'a, 'py> Py2Borrowed<'a, 'py, PyAny> { impl<'a, 'py> Py2Borrowed<'a, 'py, PyAny> {
/// # Safety /// # Safety
/// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by /// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by
@ -315,6 +328,14 @@ impl<'py, T> Deref for Py2Borrowed<'_, 'py, T> {
} }
} }
impl<T> Clone for Py2Borrowed<'_, '_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Py2Borrowed<'_, '_, T> {}
/// A GIL-independent reference to an object allocated on the Python heap. /// A GIL-independent reference to an object allocated on the Python heap.
/// ///
/// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it. /// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it.

View File

@ -32,6 +32,7 @@ pub use crate::wrap_pyfunction;
// pub(crate) use crate::types::bytes::PyBytesMethods; // pub(crate) use crate::types::bytes::PyBytesMethods;
// pub(crate) use crate::types::dict::PyDictMethods; // pub(crate) use crate::types::dict::PyDictMethods;
// pub(crate) use crate::types::float::PyFloatMethods; // pub(crate) use crate::types::float::PyFloatMethods;
// pub(crate) use crate::types::list::PyListMethods;
// pub(crate) use crate::types::mapping::PyMappingMethods; // pub(crate) use crate::types::mapping::PyMappingMethods;
// pub(crate) use crate::types::sequence::PySequenceMethods; // pub(crate) use crate::types::sequence::PySequenceMethods;
// pub(crate) use crate::types::string::PyStringMethods; // pub(crate) use crate::types::string::PyStringMethods;

View File

@ -2,7 +2,7 @@ use super::PyMapping;
use crate::err::{self, PyErr, PyResult}; use crate::err::{self, PyErr, PyResult};
use crate::ffi::Py_ssize_t; use crate::ffi::Py_ssize_t;
use crate::ffi_ptr_ext::FfiPtrExt; use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::Py2; use crate::instance::{Py2, Py2Borrowed};
use crate::py_result_ext::PyResultExt; use crate::py_result_ext::PyResultExt;
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyList}; use crate::types::{PyAny, PyList};
@ -406,7 +406,7 @@ impl<'py> PyDictMethods<'py> for Py2<'py, PyDict> {
match unsafe { match unsafe {
ffi::PyDict_GetItemWithError(dict.as_ptr(), key.as_ptr()) ffi::PyDict_GetItemWithError(dict.as_ptr(), key.as_ptr())
.assume_borrowed_or_opt(py) .assume_borrowed_or_opt(py)
.map(|borrowed_any| borrowed_any.clone()) .map(Py2Borrowed::to_owned)
} { } {
some @ Some(_) => Ok(some), some @ Some(_) => Ok(some),
None => PyErr::take(py).map(Err).transpose(), None => PyErr::take(py).map(Err).transpose(),
@ -595,8 +595,8 @@ impl<'py> Iterator for PyDictIterator2<'py> {
// - PyDict_Next returns borrowed values // - PyDict_Next returns borrowed values
// - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
Some(( Some((
unsafe { key.assume_borrowed_unchecked(py) }.clone(), unsafe { key.assume_borrowed_unchecked(py) }.to_owned(),
unsafe { value.assume_borrowed_unchecked(py) }.clone(), unsafe { value.assume_borrowed_unchecked(py) }.to_owned(),
)) ))
} else { } else {
None None

View File

@ -3,9 +3,14 @@ use std::iter::FusedIterator;
use crate::err::{self, PyResult}; use crate::err::{self, PyResult};
use crate::ffi::{self, Py_ssize_t}; use crate::ffi::{self, Py_ssize_t};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::Py2Borrowed;
use crate::internal_tricks::get_ssize_index; use crate::internal_tricks::get_ssize_index;
use crate::types::{PySequence, PyTuple}; use crate::types::{PySequence, PyTuple};
use crate::{Py, PyAny, PyObject, Python, ToPyObject}; use crate::{Py2, PyAny, PyObject, Python, ToPyObject};
use crate::types::any::PyAnyMethods;
use crate::types::sequence::PySequenceMethods;
/// Represents a Python `list`. /// Represents a Python `list`.
#[repr(transparent)] #[repr(transparent)]
@ -15,10 +20,10 @@ pyobject_native_type_core!(PyList, pyobject_native_static_type_object!(ffi::PyLi
#[inline] #[inline]
#[track_caller] #[track_caller]
pub(crate) fn new_from_iter( pub(crate) fn new_from_iter<'py>(
py: Python<'_>, py: Python<'py>,
elements: &mut dyn ExactSizeIterator<Item = PyObject>, elements: &mut dyn ExactSizeIterator<Item = PyObject>,
) -> Py<PyList> { ) -> Py2<'py, PyList> {
unsafe { unsafe {
// PyList_New checks for overflow but has a bad error message, so we check ourselves // PyList_New checks for overflow but has a bad error message, so we check ourselves
let len: Py_ssize_t = elements let len: Py_ssize_t = elements
@ -28,10 +33,10 @@ pub(crate) fn new_from_iter(
let ptr = ffi::PyList_New(len); let ptr = ffi::PyList_New(len);
// We create the `Py` pointer here for two reasons: // We create the `Py2` pointer here for two reasons:
// - panics if the ptr is null // - panics if the ptr is null
// - its Drop cleans up the list if user code or the asserts panic. // - its Drop cleans up the list if user code or the asserts panic.
let list: Py<PyList> = Py::from_owned_ptr(py, ptr); let list = ptr.assume_owned(py).downcast_into_unchecked();
let mut counter: Py_ssize_t = 0; let mut counter: Py_ssize_t = 0;
@ -83,31 +88,22 @@ impl PyList {
U: ExactSizeIterator<Item = T>, U: ExactSizeIterator<Item = T>,
{ {
let mut iter = elements.into_iter().map(|e| e.to_object(py)); let mut iter = elements.into_iter().map(|e| e.to_object(py));
let list = new_from_iter(py, &mut iter); new_from_iter(py, &mut iter).into_gil_ref()
list.into_ref(py)
} }
/// Constructs a new empty list. /// Constructs a new empty list.
pub fn empty(py: Python<'_>) -> &PyList { pub fn empty(py: Python<'_>) -> &PyList {
unsafe { py.from_owned_ptr::<PyList>(ffi::PyList_New(0)) } unsafe { py.from_owned_ptr(ffi::PyList_New(0)) }
} }
/// Returns the length of the list. /// Returns the length of the list.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
unsafe { Py2::borrowed_from_gil_ref(&self).len()
#[cfg(not(Py_LIMITED_API))]
let size = ffi::PyList_GET_SIZE(self.as_ptr());
#[cfg(Py_LIMITED_API)]
let size = ffi::PyList_Size(self.as_ptr());
// non-negative Py_ssize_t should always fit into Rust usize
size as usize
}
} }
/// Checks if the list is empty. /// Checks if the list is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 Py2::borrowed_from_gil_ref(&self).is_empty()
} }
/// Returns `self` cast as a `PySequence`. /// Returns `self` cast as a `PySequence`.
@ -126,12 +122,9 @@ impl PyList {
/// }); /// });
/// ``` /// ```
pub fn get_item(&self, index: usize) -> PyResult<&PyAny> { pub fn get_item(&self, index: usize) -> PyResult<&PyAny> {
unsafe { Py2::borrowed_from_gil_ref(&self)
let item = ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t); .get_item(index)
// PyList_GetItem return borrowed ptr; must make owned for safety (see #890). .map(Py2::into_gil_ref)
ffi::Py_XINCREF(item);
self.py().from_owned_ptr_or_err(item)
}
} }
/// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution. /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
@ -141,10 +134,9 @@ impl PyList {
/// Caller must verify that the index is within the bounds of the list. /// Caller must verify that the index is within the bounds of the list.
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny { pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny {
let item = ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t); Py2::borrowed_from_gil_ref(&self)
// PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890). .get_item_unchecked(index)
ffi::Py_XINCREF(item); .into_gil_ref()
self.py().from_owned_ptr(item)
} }
/// Takes the slice `self[low:high]` and returns it as a new list. /// Takes the slice `self[low:high]` and returns it as a new list.
@ -152,13 +144,9 @@ impl PyList {
/// Indices must be nonnegative, and out-of-range indices are clipped to /// Indices must be nonnegative, and out-of-range indices are clipped to
/// `self.len()`. /// `self.len()`.
pub fn get_slice(&self, low: usize, high: usize) -> &PyList { pub fn get_slice(&self, low: usize, high: usize) -> &PyList {
unsafe { Py2::borrowed_from_gil_ref(&self)
self.py().from_owned_ptr(ffi::PyList_GetSlice( .get_slice(low, high)
self.as_ptr(), .into_gil_ref()
get_ssize_index(low),
get_ssize_index(high),
))
}
} }
/// Sets the item at the specified index. /// Sets the item at the specified index.
@ -168,13 +156,7 @@ impl PyList {
where where
I: ToPyObject, I: ToPyObject,
{ {
fn inner(list: &PyList, index: usize, item: PyObject) -> PyResult<()> { Py2::borrowed_from_gil_ref(&self).set_item(index, item)
err::error_on_minusone(list.py(), unsafe {
ffi::PyList_SetItem(list.as_ptr(), get_ssize_index(index), item.into_ptr())
})
}
inner(self, index, item.to_object(self.py()))
} }
/// Deletes the `index`th element of self. /// Deletes the `index`th element of self.
@ -182,7 +164,7 @@ impl PyList {
/// This is equivalent to the Python statement `del self[i]`. /// This is equivalent to the Python statement `del self[i]`.
#[inline] #[inline]
pub fn del_item(&self, index: usize) -> PyResult<()> { pub fn del_item(&self, index: usize) -> PyResult<()> {
self.as_sequence().del_item(index) Py2::borrowed_from_gil_ref(&self).del_item(index)
} }
/// Assigns the sequence `seq` to the slice of `self` from `low` to `high`. /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
@ -190,6 +172,285 @@ impl PyList {
/// This is equivalent to the Python statement `self[low:high] = v`. /// This is equivalent to the Python statement `self[low:high] = v`.
#[inline] #[inline]
pub fn set_slice(&self, low: usize, high: usize, seq: &PyAny) -> PyResult<()> { pub fn set_slice(&self, low: usize, high: usize, seq: &PyAny) -> PyResult<()> {
Py2::borrowed_from_gil_ref(&self).set_slice(low, high, Py2::borrowed_from_gil_ref(&seq))
}
/// Deletes the slice from `low` to `high` from `self`.
///
/// This is equivalent to the Python statement `del self[low:high]`.
#[inline]
pub fn del_slice(&self, low: usize, high: usize) -> PyResult<()> {
Py2::borrowed_from_gil_ref(&self).del_slice(low, high)
}
/// Appends an item to the list.
pub fn append<I>(&self, item: I) -> PyResult<()>
where
I: ToPyObject,
{
Py2::borrowed_from_gil_ref(&self).append(item)
}
/// Inserts an item at the specified index.
///
/// If `index >= self.len()`, inserts at the end.
pub fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToPyObject,
{
Py2::borrowed_from_gil_ref(&self).insert(index, item)
}
/// Determines if self contains `value`.
///
/// This is equivalent to the Python expression `value in self`.
#[inline]
pub fn contains<V>(&self, value: V) -> PyResult<bool>
where
V: ToPyObject,
{
Py2::borrowed_from_gil_ref(&self).contains(value)
}
/// Returns the first index `i` for which `self[i] == value`.
///
/// This is equivalent to the Python expression `self.index(value)`.
#[inline]
pub fn index<V>(&self, value: V) -> PyResult<usize>
where
V: ToPyObject,
{
Py2::borrowed_from_gil_ref(&self).index(value)
}
/// Returns an iterator over this list's items.
pub fn iter(&self) -> PyListIterator<'_> {
PyListIterator(Py2::borrowed_from_gil_ref(&self).iter())
}
/// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
pub fn sort(&self) -> PyResult<()> {
Py2::borrowed_from_gil_ref(&self).sort()
}
/// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
pub fn reverse(&self) -> PyResult<()> {
Py2::borrowed_from_gil_ref(&self).reverse()
}
/// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
///
/// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
pub fn to_tuple(&self) -> &PyTuple {
Py2::borrowed_from_gil_ref(&self).to_tuple().into_gil_ref()
}
}
index_impls!(PyList, "list", PyList::len, PyList::get_slice);
/// Implementation of functionality for [`PyList`].
///
/// These methods are defined for the `Py2<'py, PyList>` smart pointer, so to use method call
/// syntax these methods are separated into a trait, because stable Rust does not yet support
/// `arbitrary_self_types`.
#[doc(alias = "PyList")]
pub(crate) trait PyListMethods<'py> {
/// Returns the length of the list.
fn len(&self) -> usize;
/// Checks if the list is empty.
fn is_empty(&self) -> bool;
/// Returns `self` cast as a `PySequence`.
fn as_sequence(&self) -> &Py2<'py, PySequence>;
/// Gets the list item at the specified index.
/// # Example
/// ```
/// use pyo3::{prelude::*, types::PyList};
/// Python::with_gil(|py| {
/// let list = PyList::new(py, [2, 3, 5, 7]);
/// let obj = list.get_item(0);
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
/// });
/// ```
fn get_item(&self, index: usize) -> PyResult<Py2<'py, PyAny>>;
/// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
///
/// # Safety
///
/// Caller must verify that the index is within the bounds of the list.
#[cfg(not(Py_LIMITED_API))]
unsafe fn get_item_unchecked(&self, index: usize) -> Py2<'py, PyAny>;
/// Takes the slice `self[low:high]` and returns it as a new list.
///
/// Indices must be nonnegative, and out-of-range indices are clipped to
/// `self.len()`.
fn get_slice(&self, low: usize, high: usize) -> Py2<'py, PyList>;
/// Sets the item at the specified index.
///
/// Raises `IndexError` if the index is out of range.
fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToPyObject;
/// Deletes the `index`th element of self.
///
/// This is equivalent to the Python statement `del self[i]`.
fn del_item(&self, index: usize) -> PyResult<()>;
/// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
///
/// This is equivalent to the Python statement `self[low:high] = v`.
fn set_slice(&self, low: usize, high: usize, seq: &Py2<'_, PyAny>) -> PyResult<()>;
/// Deletes the slice from `low` to `high` from `self`.
///
/// This is equivalent to the Python statement `del self[low:high]`.
fn del_slice(&self, low: usize, high: usize) -> PyResult<()>;
/// Appends an item to the list.
fn append<I>(&self, item: I) -> PyResult<()>
where
I: ToPyObject;
/// Inserts an item at the specified index.
///
/// If `index >= self.len()`, inserts at the end.
fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToPyObject;
/// Determines if self contains `value`.
///
/// This is equivalent to the Python expression `value in self`.
fn contains<V>(&self, value: V) -> PyResult<bool>
where
V: ToPyObject;
/// Returns the first index `i` for which `self[i] == value`.
///
/// This is equivalent to the Python expression `self.index(value)`.
fn index<V>(&self, value: V) -> PyResult<usize>
where
V: ToPyObject;
/// Returns an iterator over this list's items.
fn iter(&self) -> PyListIterator2<'py>;
/// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
fn sort(&self) -> PyResult<()>;
/// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
fn reverse(&self) -> PyResult<()>;
/// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
///
/// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
fn to_tuple(&self) -> Py2<'py, PyTuple>;
}
impl<'py> PyListMethods<'py> for Py2<'py, PyList> {
/// Returns the length of the list.
fn len(&self) -> usize {
unsafe {
#[cfg(not(Py_LIMITED_API))]
let size = ffi::PyList_GET_SIZE(self.as_ptr());
#[cfg(Py_LIMITED_API)]
let size = ffi::PyList_Size(self.as_ptr());
// non-negative Py_ssize_t should always fit into Rust usize
size as usize
}
}
/// Checks if the list is empty.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns `self` cast as a `PySequence`.
fn as_sequence(&self) -> &Py2<'py, PySequence> {
unsafe { self.downcast_unchecked() }
}
/// Gets the list item at the specified index.
/// # Example
/// ```
/// use pyo3::{prelude::*, types::PyList};
/// Python::with_gil(|py| {
/// let list = PyList::new(py, [2, 3, 5, 7]);
/// let obj = list.get_item(0);
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
/// });
/// ```
fn get_item(&self, index: usize) -> PyResult<Py2<'py, PyAny>> {
unsafe {
// PyList_GetItem return borrowed ptr; must make owned for safety (see #890).
ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t)
.assume_borrowed_or_err(self.py())
.map(Py2Borrowed::to_owned)
}
}
/// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
///
/// # Safety
///
/// Caller must verify that the index is within the bounds of the list.
#[cfg(not(Py_LIMITED_API))]
unsafe fn get_item_unchecked(&self, index: usize) -> Py2<'py, PyAny> {
// PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890).
ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t)
.assume_borrowed(self.py())
.to_owned()
}
/// Takes the slice `self[low:high]` and returns it as a new list.
///
/// Indices must be nonnegative, and out-of-range indices are clipped to
/// `self.len()`.
fn get_slice(&self, low: usize, high: usize) -> Py2<'py, PyList> {
unsafe {
ffi::PyList_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
.assume_owned(self.py())
.downcast_into_unchecked()
}
}
/// Sets the item at the specified index.
///
/// Raises `IndexError` if the index is out of range.
fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
where
I: ToPyObject,
{
fn inner(list: &Py2<'_, PyList>, index: usize, item: Py2<'_, PyAny>) -> PyResult<()> {
err::error_on_minusone(list.py(), unsafe {
ffi::PyList_SetItem(list.as_ptr(), get_ssize_index(index), item.into_ptr())
})
}
let py = self.py();
inner(self, index, item.to_object(py).attach_into(py))
}
/// Deletes the `index`th element of self.
///
/// This is equivalent to the Python statement `del self[i]`.
#[inline]
fn del_item(&self, index: usize) -> PyResult<()> {
self.as_sequence().del_item(index)
}
/// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
///
/// This is equivalent to the Python statement `self[low:high] = v`.
#[inline]
fn set_slice(&self, low: usize, high: usize, seq: &Py2<'_, PyAny>) -> PyResult<()> {
err::error_on_minusone(self.py(), unsafe { err::error_on_minusone(self.py(), unsafe {
ffi::PyList_SetSlice( ffi::PyList_SetSlice(
self.as_ptr(), self.as_ptr(),
@ -204,45 +465,47 @@ impl PyList {
/// ///
/// This is equivalent to the Python statement `del self[low:high]`. /// This is equivalent to the Python statement `del self[low:high]`.
#[inline] #[inline]
pub fn del_slice(&self, low: usize, high: usize) -> PyResult<()> { fn del_slice(&self, low: usize, high: usize) -> PyResult<()> {
self.as_sequence().del_slice(low, high) self.as_sequence().del_slice(low, high)
} }
/// Appends an item to the list. /// Appends an item to the list.
pub fn append<I>(&self, item: I) -> PyResult<()> fn append<I>(&self, item: I) -> PyResult<()>
where where
I: ToPyObject, I: ToPyObject,
{ {
fn inner(list: &PyList, item: PyObject) -> PyResult<()> { fn inner(list: &Py2<'_, PyList>, item: Py2<'_, PyAny>) -> PyResult<()> {
err::error_on_minusone(list.py(), unsafe { err::error_on_minusone(list.py(), unsafe {
ffi::PyList_Append(list.as_ptr(), item.as_ptr()) ffi::PyList_Append(list.as_ptr(), item.as_ptr())
}) })
} }
inner(self, item.to_object(self.py())) let py = self.py();
inner(self, item.to_object(py).attach_into(py))
} }
/// Inserts an item at the specified index. /// Inserts an item at the specified index.
/// ///
/// If `index >= self.len()`, inserts at the end. /// If `index >= self.len()`, inserts at the end.
pub fn insert<I>(&self, index: usize, item: I) -> PyResult<()> fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
where where
I: ToPyObject, I: ToPyObject,
{ {
fn inner(list: &PyList, index: usize, item: PyObject) -> PyResult<()> { fn inner(list: &Py2<'_, PyList>, index: usize, item: Py2<'_, PyAny>) -> PyResult<()> {
err::error_on_minusone(list.py(), unsafe { err::error_on_minusone(list.py(), unsafe {
ffi::PyList_Insert(list.as_ptr(), get_ssize_index(index), item.as_ptr()) ffi::PyList_Insert(list.as_ptr(), get_ssize_index(index), item.as_ptr())
}) })
} }
inner(self, index, item.to_object(self.py())) let py = self.py();
inner(self, index, item.to_object(py).attach_into(py))
} }
/// Determines if self contains `value`. /// Determines if self contains `value`.
/// ///
/// This is equivalent to the Python expression `value in self`. /// This is equivalent to the Python expression `value in self`.
#[inline] #[inline]
pub fn contains<V>(&self, value: V) -> PyResult<bool> fn contains<V>(&self, value: V) -> PyResult<bool>
where where
V: ToPyObject, V: ToPyObject,
{ {
@ -253,7 +516,7 @@ impl PyList {
/// ///
/// This is equivalent to the Python expression `self.index(value)`. /// This is equivalent to the Python expression `self.index(value)`.
#[inline] #[inline]
pub fn index<V>(&self, value: V) -> PyResult<usize> fn index<V>(&self, value: V) -> PyResult<usize>
where where
V: ToPyObject, V: ToPyObject,
{ {
@ -261,43 +524,91 @@ impl PyList {
} }
/// Returns an iterator over this list's items. /// Returns an iterator over this list's items.
pub fn iter(&self) -> PyListIterator<'_> { fn iter(&self) -> PyListIterator2<'py> {
PyListIterator { PyListIterator2::new(self.clone())
list: self,
index: 0,
length: self.len(),
}
} }
/// Sorts the list in-place. Equivalent to the Python expression `l.sort()`. /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
pub fn sort(&self) -> PyResult<()> { fn sort(&self) -> PyResult<()> {
err::error_on_minusone(self.py(), unsafe { ffi::PyList_Sort(self.as_ptr()) }) err::error_on_minusone(self.py(), unsafe { ffi::PyList_Sort(self.as_ptr()) })
} }
/// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`. /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
pub fn reverse(&self) -> PyResult<()> { fn reverse(&self) -> PyResult<()> {
err::error_on_minusone(self.py(), unsafe { ffi::PyList_Reverse(self.as_ptr()) }) err::error_on_minusone(self.py(), unsafe { ffi::PyList_Reverse(self.as_ptr()) })
} }
/// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`. /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
/// ///
/// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`. /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
pub fn to_tuple(&self) -> &PyTuple { fn to_tuple(&self) -> Py2<'py, PyTuple> {
unsafe { self.py().from_owned_ptr(ffi::PyList_AsTuple(self.as_ptr())) } unsafe {
ffi::PyList_AsTuple(self.as_ptr())
.assume_owned(self.py())
.downcast_into_unchecked()
}
} }
} }
index_impls!(PyList, "list", PyList::len, PyList::get_slice); /// Used by `PyList::iter()`.
pub struct PyListIterator<'a>(PyListIterator2<'a>);
impl<'a> Iterator for PyListIterator<'a> {
type Item = &'a PyAny;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Py2::into_gil_ref)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a> DoubleEndedIterator for PyListIterator<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(Py2::into_gil_ref)
}
}
impl<'a> ExactSizeIterator for PyListIterator<'a> {
fn len(&self) -> usize {
self.0.len()
}
}
impl FusedIterator for PyListIterator<'_> {}
impl<'a> IntoIterator for &'a PyList {
type Item = &'a PyAny;
type IntoIter = PyListIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// Used by `PyList::iter()`. /// Used by `PyList::iter()`.
pub struct PyListIterator<'a> { pub(crate) struct PyListIterator2<'py> {
list: &'a PyList, list: Py2<'py, PyList>,
index: usize, index: usize,
length: usize, length: usize,
} }
impl<'a> PyListIterator<'a> { impl<'py> PyListIterator2<'py> {
unsafe fn get_item(&self, index: usize) -> &'a PyAny { fn new(list: Py2<'py, PyList>) -> Self {
let length: usize = list.len();
PyListIterator2 {
list,
index: 0,
length,
}
}
unsafe fn get_item(&self, index: usize) -> Py2<'py, PyAny> {
#[cfg(any(Py_LIMITED_API, PyPy))] #[cfg(any(Py_LIMITED_API, PyPy))]
let item = self.list.get_item(index).expect("list.get failed"); let item = self.list.get_item(index).expect("list.get failed");
#[cfg(not(any(Py_LIMITED_API, PyPy)))] #[cfg(not(any(Py_LIMITED_API, PyPy)))]
@ -306,8 +617,8 @@ impl<'a> PyListIterator<'a> {
} }
} }
impl<'a> Iterator for PyListIterator<'a> { impl<'py> Iterator for PyListIterator2<'py> {
type Item = &'a PyAny; type Item = Py2<'py, PyAny>;
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -329,7 +640,7 @@ impl<'a> Iterator for PyListIterator<'a> {
} }
} }
impl<'a> DoubleEndedIterator for PyListIterator<'a> { impl DoubleEndedIterator for PyListIterator2<'_> {
#[inline] #[inline]
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
let length = self.length.min(self.list.len()); let length = self.length.min(self.list.len());
@ -344,23 +655,32 @@ impl<'a> DoubleEndedIterator for PyListIterator<'a> {
} }
} }
impl<'a> ExactSizeIterator for PyListIterator<'a> { impl ExactSizeIterator for PyListIterator2<'_> {
fn len(&self) -> usize { fn len(&self) -> usize {
self.length.saturating_sub(self.index) self.length.saturating_sub(self.index)
} }
} }
impl FusedIterator for PyListIterator<'_> {} impl FusedIterator for PyListIterator2<'_> {}
impl<'a> IntoIterator for &'a PyList { impl<'a, 'py> IntoIterator for &'a Py2<'py, PyList> {
type Item = &'a PyAny; type Item = Py2<'py, PyAny>;
type IntoIter = PyListIterator<'a>; type IntoIter = PyListIterator2<'py>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.iter() self.iter()
} }
} }
impl<'py> IntoIterator for Py2<'py, PyList> {
type Item = Py2<'py, PyAny>;
type IntoIter = PyListIterator2<'py>;
fn into_iter(self) -> Self::IntoIter {
PyListIterator2::new(self)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{PyList, PyTuple}; use crate::types::{PyList, PyTuple};