add list bound constructors

This commit is contained in:
David Hewitt 2024-01-23 08:39:19 +00:00
parent 0973da27e9
commit eed196329d
13 changed files with 79 additions and 48 deletions

View File

@ -23,21 +23,17 @@ fn enum_from_pyobject(b: &mut Bencher<'_>) {
fn list_via_downcast(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let any: &PyAny = PyList::empty(py).into();
let any: &Bound<'_, PyAny> = &PyList::empty_bound(py);
b.iter(|| {
let _list: &PyList = black_box(any).downcast().unwrap();
});
b.iter(|| black_box(any).downcast::<PyList>().unwrap());
})
}
fn list_via_extract(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let any: &PyAny = PyList::empty(py).into();
let any: &Bound<'_, PyAny> = &PyList::empty_bound(py);
b.iter(|| {
let _list: &PyList = black_box(any).extract().unwrap();
});
b.iter(|| black_box(any).extract::<Bound<'_, PyList>>().unwrap());
})
}
@ -45,9 +41,7 @@ fn not_a_list_via_downcast(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let any: &PyAny = PyString::new(py, "foobar").into();
b.iter(|| {
black_box(any).downcast::<PyList>().unwrap_err();
});
b.iter(|| black_box(any).downcast::<PyList>().unwrap_err());
})
}
@ -55,9 +49,7 @@ fn not_a_list_via_extract(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let any: &PyAny = PyString::new(py, "foobar").into();
b.iter(|| {
black_box(any).extract::<&PyList>().unwrap_err();
});
b.iter(|| black_box(any).extract::<&PyList>().unwrap_err());
})
}

View File

@ -6,10 +6,10 @@ use pyo3::types::{PyList, PySequence};
fn iter_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 100_000;
let list = PyList::new(py, 0..LEN);
let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for x in list {
for x in list.iter() {
let i: u64 = x.extract().unwrap();
sum += i;
}
@ -20,14 +20,14 @@ fn iter_list(b: &mut Bencher<'_>) {
fn list_new(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
b.iter(|| PyList::new(py, 0..LEN));
b.iter(|| PyList::new_bound(py, 0..LEN));
});
}
fn list_get_item(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN);
let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
@ -41,7 +41,7 @@ fn list_get_item(b: &mut Bencher<'_>) {
fn list_get_item_unchecked(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN);
let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
@ -56,9 +56,10 @@ fn list_get_item_unchecked(b: &mut Bencher<'_>) {
fn sequence_from_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN).to_object(py);
let list = PyList::new_bound(py, 0..LEN).to_object(py);
b.iter(|| {
let _: &PySequence = list.extract(py).unwrap();
let seq: &PySequence = list.extract(py).unwrap();
seq
});
});
}

View File

@ -95,14 +95,14 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{PyDict, PyList};
use crate::types::{any::PyAnyMethods, PyDict, PyList};
#[test]
fn test_smallvec_into_py() {
Python::with_gil(|py| {
let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
let hso: PyObject = sv.clone().into_py(py);
let l = PyList::new(py, [1, 2, 3, 4, 5]);
let l = PyList::new_bound(py, [1, 2, 3, 4, 5]);
assert!(l.eq(hso).unwrap());
});
}
@ -110,7 +110,7 @@ mod tests {
#[test]
fn test_smallvec_from_py_object() {
Python::with_gil(|py| {
let l = PyList::new(py, [1, 2, 3, 4, 5]);
let l = PyList::new_bound(py, [1, 2, 3, 4, 5]);
let sv: SmallVec<[u64; 8]> = l.extract().unwrap();
assert_eq!(sv.as_slice(), [1, 2, 3, 4, 5]);
});
@ -133,7 +133,7 @@ mod tests {
Python::with_gil(|py| {
let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
let hso: PyObject = sv.to_object(py);
let l = PyList::new(py, [1, 2, 3, 4, 5]);
let l = PyList::new_bound(py, [1, 2, 3, 4, 5]);
assert!(l.eq(hso).unwrap());
});
}

View File

@ -1067,8 +1067,7 @@ impl<'unbound> Python<'unbound> {
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{IntoPyDict, PyDict, PyList};
use crate::Py;
use crate::types::{any::PyAnyMethods, IntoPyDict, PyDict, PyList};
use std::sync::Arc;
#[test]
@ -1150,17 +1149,14 @@ mod tests {
// If allow_threads is implemented correctly, this thread still owns the GIL here
// so the following Python calls should not cause crashes.
let list = PyList::new(py, [1, 2, 3, 4]);
let list = PyList::new_bound(py, [1, 2, 3, 4]);
assert_eq!(list.extract::<Vec<i32>>().unwrap(), vec![1, 2, 3, 4]);
});
}
#[test]
fn test_allow_threads_pass_stuff_in() {
let list: Py<PyList> = Python::with_gil(|py| {
let list = PyList::new(py, vec!["foo", "bar"]);
list.into()
});
let list = Python::with_gil(|py| PyList::new_bound(py, vec!["foo", "bar"]).unbind());
let mut v = vec![1, 2, 3];
let a = Arc::new(String::from("foo"));

View File

@ -76,8 +76,8 @@ impl Dummy {
fn __delattr__(&mut self, name: ::std::string::String) {}
fn __dir__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyList {
crate::types::PyList::new(py, ::std::vec![0_u8])
fn __dir__<'py>(&self, py: crate::Python<'py>) -> crate::Bound<'py, crate::types::PyList> {
crate::types::PyList::new_bound(py, ::std::vec![0_u8])
}
//////////////////////
@ -469,8 +469,8 @@ impl Dummy {
fn __delattr__(&mut self, name: ::std::string::String) {}
fn __dir__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyList {
crate::types::PyList::new(py, ::std::vec![0_u8])
fn __dir__<'py>(&self, py: crate::Python<'py>) -> crate::Bound<'py, crate::types::PyList> {
crate::types::PyList::new_bound(py, ::std::vec![0_u8])
}
//////////////////////

View File

@ -697,6 +697,7 @@ where
}
#[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests {
use super::*;
#[cfg(not(PyPy))]

View File

@ -56,6 +56,24 @@ pub(crate) fn new_from_iter<'py>(
}
impl PyList {
/// Deprecated form of [`PyList::new_bound`].
#[inline]
#[track_caller]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyList::new` will be replaced by `PyList::new_bound` in a future PyO3 version"
)
)]
pub fn new<T, U>(py: Python<'_>, elements: impl IntoIterator<Item = T, IntoIter = U>) -> &PyList
where
T: ToPyObject,
U: ExactSizeIterator<Item = T>,
{
Self::new_bound(py, elements).into_gil_ref()
}
/// Constructs a new list with the given elements.
///
/// If you want to create a [`PyList`] with elements of different or unknown types, or from an
@ -82,18 +100,38 @@ impl PyList {
/// All standard library structures implement this trait correctly, if they do, so calling this
/// function with (for example) [`Vec`]`<T>` or `&[T]` will always succeed.
#[track_caller]
pub fn new<T, U>(py: Python<'_>, elements: impl IntoIterator<Item = T, IntoIter = U>) -> &PyList
pub fn new_bound<T, U>(
py: Python<'_>,
elements: impl IntoIterator<Item = T, IntoIter = U>,
) -> Bound<'_, PyList>
where
T: ToPyObject,
U: ExactSizeIterator<Item = T>,
{
let mut iter = elements.into_iter().map(|e| e.to_object(py));
new_from_iter(py, &mut iter).into_gil_ref()
new_from_iter(py, &mut iter)
}
/// Deprecated form of [`PyList::empty_bound`].
#[inline]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyList::empty` will be replaced by `PyList::empty_bound` in a future PyO3 version"
)
)]
pub fn empty(py: Python<'_>) -> &PyList {
Self::empty_bound(py).into_gil_ref()
}
/// Constructs a new empty list.
pub fn empty(py: Python<'_>) -> &PyList {
unsafe { py.from_owned_ptr(ffi::PyList_New(0)) }
pub fn empty_bound(py: Python<'_>) -> Bound<'_, PyList> {
unsafe {
ffi::PyList_New(0)
.assume_owned(py)
.downcast_into_unchecked()
}
}
/// Returns the length of the list.
@ -667,6 +705,7 @@ impl<'py> IntoIterator for Bound<'py, PyList> {
}
#[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests {
use crate::types::{PyList, PyTuple};
use crate::Python;

View File

@ -560,7 +560,7 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
Ok(idx) => idx.downcast_into().map_err(PyErr::from),
Err(err) => {
if err.is_instance_of::<exceptions::PyAttributeError>(self.py()) {
let l = PyList::empty(self.py()).as_borrowed().to_owned();
let l = PyList::empty_bound(self.py());
self.setattr(__all__, &l).map_err(PyErr::from)?;
Ok(l)
} else {

View File

@ -566,6 +566,7 @@ impl<'v> crate::PyTryFrom<'v> for PySequence {
}
#[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests {
use crate::types::{PyList, PySequence, PyTuple};
use crate::{PyObject, Python, ToPyObject};

View File

@ -564,7 +564,8 @@ pub struct TransparentFromPyWith {
#[test]
fn test_transparent_from_py_with() {
Python::with_gil(|py| {
let result = TransparentFromPyWith::extract(PyList::new(py, [1, 2, 3])).unwrap();
let result =
TransparentFromPyWith::extract(PyList::new_bound(py, [1, 2, 3]).as_gil_ref()).unwrap();
let expected = TransparentFromPyWith { len: 3 };
assert_eq!(result, expected);

View File

@ -42,8 +42,8 @@ impl ClassWithProperties {
}
#[getter]
fn get_data_list<'py>(&self, py: Python<'py>) -> &'py PyList {
PyList::new(py, [self.num])
fn get_data_list<'py>(&self, py: Python<'py>) -> Bound<'py, PyList> {
PyList::new_bound(py, [self.num])
}
}

View File

@ -689,12 +689,12 @@ struct MethodWithLifeTime {}
#[pymethods]
impl MethodWithLifeTime {
fn set_to_list<'py>(&self, py: Python<'py>, set: &'py PySet) -> PyResult<&'py PyList> {
fn set_to_list<'py>(&self, py: Python<'py>, set: &'py PySet) -> PyResult<Bound<'py, PyList>> {
let mut items = vec![];
for _ in 0..set.len() {
items.push(set.pop().unwrap());
}
let list = PyList::new(py, items);
let list = PyList::new_bound(py, items);
list.sort()?;
Ok(list)
}

View File

@ -850,7 +850,7 @@ struct DefaultedContains;
#[pymethods]
impl DefaultedContains {
fn __iter__(&self, py: Python<'_>) -> PyObject {
PyList::new(py, ["a", "b", "c"])
PyList::new_bound(py, ["a", "b", "c"])
.as_ref()
.iter()
.unwrap()
@ -864,7 +864,7 @@ struct NoContains;
#[pymethods]
impl NoContains {
fn __iter__(&self, py: Python<'_>) -> PyObject {
PyList::new(py, ["a", "b", "c"])
PyList::new_bound(py, ["a", "b", "c"])
.as_ref()
.iter()
.unwrap()