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

View File

@ -6,10 +6,10 @@ use pyo3::types::{PyList, PySequence};
fn iter_list(b: &mut Bencher<'_>) { fn iter_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
const LEN: usize = 100_000; const LEN: usize = 100_000;
let list = PyList::new(py, 0..LEN); let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0; let mut sum = 0;
b.iter(|| { b.iter(|| {
for x in list { for x in list.iter() {
let i: u64 = x.extract().unwrap(); let i: u64 = x.extract().unwrap();
sum += i; sum += i;
} }
@ -20,14 +20,14 @@ fn iter_list(b: &mut Bencher<'_>) {
fn list_new(b: &mut Bencher<'_>) { fn list_new(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
const LEN: usize = 50_000; 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<'_>) { fn list_get_item(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
const LEN: usize = 50_000; const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN); let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0; let mut sum = 0;
b.iter(|| { b.iter(|| {
for i in 0..LEN { for i in 0..LEN {
@ -41,7 +41,7 @@ fn list_get_item(b: &mut Bencher<'_>) {
fn list_get_item_unchecked(b: &mut Bencher<'_>) { fn list_get_item_unchecked(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
const LEN: usize = 50_000; const LEN: usize = 50_000;
let list = PyList::new(py, 0..LEN); let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0; let mut sum = 0;
b.iter(|| { b.iter(|| {
for i in 0..LEN { for i in 0..LEN {
@ -56,9 +56,10 @@ fn list_get_item_unchecked(b: &mut Bencher<'_>) {
fn sequence_from_list(b: &mut Bencher<'_>) { fn sequence_from_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
const LEN: usize = 50_000; 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(|| { 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::types::{PyDict, PyList}; use crate::types::{any::PyAnyMethods, PyDict, PyList};
#[test] #[test]
fn test_smallvec_into_py() { fn test_smallvec_into_py() {
Python::with_gil(|py| { Python::with_gil(|py| {
let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect(); let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
let hso: PyObject = sv.clone().into_py(py); 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()); assert!(l.eq(hso).unwrap());
}); });
} }
@ -110,7 +110,7 @@ mod tests {
#[test] #[test]
fn test_smallvec_from_py_object() { fn test_smallvec_from_py_object() {
Python::with_gil(|py| { 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(); let sv: SmallVec<[u64; 8]> = l.extract().unwrap();
assert_eq!(sv.as_slice(), [1, 2, 3, 4, 5]); assert_eq!(sv.as_slice(), [1, 2, 3, 4, 5]);
}); });
@ -133,7 +133,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect(); let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
let hso: PyObject = sv.to_object(py); 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()); assert!(l.eq(hso).unwrap());
}); });
} }

View File

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

View File

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

View File

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

View File

@ -56,6 +56,24 @@ pub(crate) fn new_from_iter<'py>(
} }
impl PyList { 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. /// 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 /// 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 /// 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. /// function with (for example) [`Vec`]`<T>` or `&[T]` will always succeed.
#[track_caller] #[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 where
T: ToPyObject, T: ToPyObject,
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));
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. /// Constructs a new empty list.
pub fn empty(py: Python<'_>) -> &PyList { pub fn empty_bound(py: Python<'_>) -> Bound<'_, PyList> {
unsafe { py.from_owned_ptr(ffi::PyList_New(0)) } unsafe {
ffi::PyList_New(0)
.assume_owned(py)
.downcast_into_unchecked()
}
} }
/// Returns the length of the list. /// Returns the length of the list.
@ -667,6 +705,7 @@ impl<'py> IntoIterator for Bound<'py, PyList> {
} }
#[cfg(test)] #[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests { mod tests {
use crate::types::{PyList, PyTuple}; use crate::types::{PyList, PyTuple};
use crate::Python; 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), Ok(idx) => idx.downcast_into().map_err(PyErr::from),
Err(err) => { Err(err) => {
if err.is_instance_of::<exceptions::PyAttributeError>(self.py()) { 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)?; self.setattr(__all__, &l).map_err(PyErr::from)?;
Ok(l) Ok(l)
} else { } else {

View File

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

View File

@ -564,7 +564,8 @@ pub struct TransparentFromPyWith {
#[test] #[test]
fn test_transparent_from_py_with() { fn test_transparent_from_py_with() {
Python::with_gil(|py| { 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 }; let expected = TransparentFromPyWith { len: 3 };
assert_eq!(result, expected); assert_eq!(result, expected);

View File

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

View File

@ -689,12 +689,12 @@ struct MethodWithLifeTime {}
#[pymethods] #[pymethods]
impl MethodWithLifeTime { 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![]; let mut items = vec![];
for _ in 0..set.len() { for _ in 0..set.len() {
items.push(set.pop().unwrap()); items.push(set.pop().unwrap());
} }
let list = PyList::new(py, items); let list = PyList::new_bound(py, items);
list.sort()?; list.sort()?;
Ok(list) Ok(list)
} }

View File

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