Merge pull request #3755 from davidhewitt/list-bound

add list bound constructors
This commit is contained in:
David Hewitt 2024-01-27 21:27:22 +00:00 committed by GitHub
commit 796e4192b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 178 additions and 84 deletions

View File

@ -13,7 +13,7 @@ fails, so usually you will use something like
# use pyo3::types::PyList;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let list = PyList::new(py, b"foo");
# let list = PyList::new_bound(py, b"foo");
let v: Vec<i32> = list.extract()?;
# assert_eq!(&v, &[102, 111, 111]);
# Ok(())

View File

@ -75,12 +75,12 @@ Python has an [`isinstance`](https://docs.python.org/3/library/functions.html#is
In PyO3 every object has the [`PyAny::is_instance`] and [`PyAny::is_instance_of`] methods which do the same thing.
```rust
use pyo3::Python;
use pyo3::prelude::*;
use pyo3::types::{PyBool, PyList};
Python::with_gil(|py| {
assert!(PyBool::new(py, true).is_instance_of::<PyBool>());
let list = PyList::new(py, &[1, 2, 3, 4]);
let list = PyList::new_bound(py, &[1, 2, 3, 4]);
assert!(!list.is_instance_of::<PyBool>());
assert!(list.is_instance_of::<PyList>());
});

View File

@ -45,7 +45,7 @@ pyo3 = { version = "0.21", features = ["gil-refs"] }
The `PyTryFrom` trait has aged poorly, its [`try_from`] method now conflicts with `try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`.
To tighten up the PyO3 traits ahead of [a proposed upcoming API change](https://github.com/PyO3/pyo3/issues/3382) the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods [`PyAny::downcast`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast) and [`PyAny::downcast_exact`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast_exact) no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively.
To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods [`PyAny::downcast`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast) and [`PyAny::downcast_exact`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast_exact) no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively.
To migrate, switch all type casts to use `obj.downcast()` instead of `try_from(obj)` (and similar for `downcast_exact`).
@ -71,6 +71,9 @@ After:
# use pyo3::types::{PyInt, PyList};
# fn main() -> PyResult<()> {
Python::with_gil(|py| {
// Note that PyList::new is deprecated for PyList::new_bound as part of the GIL Refs API removal,
// see the section below on migration to Bound<T>.
#[allow(deprecated)]
let list = PyList::new(py, 0..5);
let b = list.get_item(0).unwrap().downcast::<PyInt>()?;
Ok(())
@ -234,7 +237,15 @@ impl PyClassAsyncIter {
### Migrating from the GIL-Refs API to `Bound<T>`
TODO
To minimise breakage of code using the GIL-Refs API, the `Bound<T>` smart pointer has been introduced by adding complements to all functions which accept or return GIL Refs. This allows code to migrate by replacing the deprecated APIs with the new ones.
For example, the following APIs have gained updated variants:
- `PyList::new`, `PyTyple::new` and similar constructors have replacements `PyList::new_bound`, `PyTuple::new_bound` etc.
Because the new `Bound<T>` API brings ownership out of the PyO3 framework and into user code, there are a few places where user code is expected to need to adjust while switching to the new API:
- Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound<T>` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count)
- `Bound<PyList>` and `Bound<PyTuple>` cannot support indexing with `list[0]`, you should use `list.get_item(0)` instead.
- `Bound<PyTuple>::iter_borrowed` is slightly more efficient than `Bound<PyTuple>::iter`. The default iteration of `Bound<PyTuple>` cannot return borrowed references because Rust does not (yet) have "lending iterators". Similarly `Bound<PyTuple>::get_borrowed_item` is more efficient than `Bound<PyTuple>::get_item` for the same reason.
## from 0.19.* to 0.20
@ -853,6 +864,7 @@ that these types can now also support Rust's indexing operators as part of a
consistent API:
```rust
#![allow(deprecated)]
use pyo3::{Python, types::PyList};
Python::with_gil(|py| {
@ -1073,7 +1085,7 @@ This should require no code changes except removing `use pyo3::AsPyRef` for code
`pyo3::prelude::*`.
Before:
```rust,compile_fail
```rust,ignore
use pyo3::{AsPyRef, Py, types::PyList};
# pyo3::Python::with_gil(|py| {
let list_py: Py<PyList> = PyList::empty(py).into();
@ -1082,7 +1094,7 @@ let list_ref: &PyList = list_py.as_ref(py);
```
After:
```rust
```rust,ignore
use pyo3::{Py, types::PyList};
# pyo3::Python::with_gil(|py| {
let list_py: Py<PyList> = PyList::empty(py).into();

View File

@ -71,6 +71,7 @@ a list:
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# Python::with_gil(|py| -> PyResult<()> {
#[allow(deprecated)] // PyList::empty is part of the deprecated "GIL Refs" API.
let obj: &PyAny = PyList::empty(py);
// To &PyList with PyAny::downcast
@ -133,6 +134,7 @@ To see all Python types exposed by `PyO3` you should consult the
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# Python::with_gil(|py| -> PyResult<()> {
#[allow(deprecated)] // PyList::empty is part of the deprecated "GIL Refs" API.
let list = PyList::empty(py);
// Use methods from PyAny on all Python types with Deref implementation
@ -173,7 +175,7 @@ For a `Py<PyList>`, the conversions are as below:
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# Python::with_gil(|py| {
let list: Py<PyList> = PyList::empty(py).into();
let list: Py<PyList> = PyList::empty_bound(py).unbind();
// To &PyList with Py::as_ref() (borrows from the Py)
let _: &PyList = list.as_ref(py);

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_with_large_drop(|| 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

@ -6,10 +6,10 @@ use pyo3::types::{PyList, PySequence, PyTuple};
fn iter_tuple(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 100_000;
let tuple = PyTuple::new(py, 0..LEN);
let tuple = PyTuple::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for x in tuple {
for x in tuple.iter_borrowed() {
let i: u64 = x.extract().unwrap();
sum += i;
}
@ -20,14 +20,14 @@ fn iter_tuple(b: &mut Bencher<'_>) {
fn tuple_new(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
b.iter(|| PyTuple::new(py, 0..LEN));
b.iter_with_large_drop(|| PyTuple::new_bound(py, 0..LEN));
});
}
fn tuple_get_item(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN);
let tuple = PyTuple::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
@ -41,7 +41,7 @@ fn tuple_get_item(b: &mut Bencher<'_>) {
fn tuple_get_item_unchecked(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN);
let tuple = PyTuple::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
@ -53,10 +53,46 @@ fn tuple_get_item_unchecked(b: &mut Bencher<'_>) {
});
}
fn tuple_get_borrowed_item(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
sum += tuple
.get_borrowed_item(i)
.unwrap()
.extract::<usize>()
.unwrap();
}
});
});
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
fn tuple_get_borrowed_item_unchecked(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for i in 0..LEN {
unsafe {
sum += tuple
.get_borrowed_item_unchecked(i)
.extract::<usize>()
.unwrap();
}
}
});
});
}
fn sequence_from_tuple(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN).to_object(py);
let tuple = PyTuple::new_bound(py, 0..LEN).to_object(py);
b.iter(|| tuple.extract::<&PySequence>(py).unwrap());
});
}
@ -64,22 +100,16 @@ fn sequence_from_tuple(b: &mut Bencher<'_>) {
fn tuple_new_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN);
b.iter(|| {
let _pool = unsafe { py.new_pool() };
let _ = PyList::new(py, tuple);
});
let tuple = PyTuple::new_bound(py, 0..LEN);
b.iter_with_large_drop(|| PyList::new_bound(py, tuple.iter_borrowed()));
});
}
fn tuple_to_list(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
const LEN: usize = 50_000;
let tuple = PyTuple::new(py, 0..LEN);
b.iter(|| {
let _pool = unsafe { py.new_pool() };
let _ = tuple.to_list();
});
let tuple = PyTuple::new_bound(py, 0..LEN);
b.iter_with_large_drop(|| tuple.to_list());
});
}
@ -95,6 +125,12 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("tuple_get_item", tuple_get_item);
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
c.bench_function("tuple_get_item_unchecked", tuple_get_item_unchecked);
c.bench_function("tuple_get_borrowed_item", tuple_get_borrowed_item);
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
c.bench_function(
"tuple_get_borrowed_item_unchecked",
tuple_get_borrowed_item_unchecked,
);
c.bench_function("sequence_from_tuple", sequence_from_tuple);
c.bench_function("tuple_new_list", tuple_new_list);
c.bench_function("tuple_to_list", tuple_to_list);

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

@ -355,6 +355,20 @@ impl<T> Clone for Borrowed<'_, '_, T> {
impl<T> Copy for Borrowed<'_, '_, T> {}
impl<T> ToPyObject for Borrowed<'_, '_, T> {
/// Converts `Py` instance -> PyObject.
fn to_object(&self, py: Python<'_>) -> PyObject {
(*self).into_py(py)
}
}
impl<T> IntoPy<PyObject> for Borrowed<'_, '_, T> {
/// Converts `Py` instance -> PyObject.
fn into_py(self, py: Python<'_>) -> PyObject {
self.to_owned().into_py(py)
}
}
/// 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.
@ -601,10 +615,9 @@ where
/// # use pyo3::types::PyList;
/// #
/// Python::with_gil(|py| {
/// let list: Py<PyList> = PyList::empty(py).into();
/// // FIXME as_ref() no longer makes sense with new Py API, remove this doc
/// // let list: &PyList = list.as_ref(py);
/// // assert_eq!(list.len(), 0);
/// let list: Py<PyList> = PyList::empty_bound(py).into();
/// let list: &PyList = list.as_ref(py);
/// assert_eq!(list.len(), 0);
/// });
/// ```
///

View File

@ -12,7 +12,7 @@
/// use pyo3::{prelude::*, py_run, types::PyList};
///
/// Python::with_gil(|py| {
/// let list = PyList::new(py, &[1, 2, 3]);
/// let list = PyList::new_bound(py, &[1, 2, 3]);
/// py_run!(py, list, "assert list == [1, 2, 3]");
/// });
/// ```

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

@ -75,10 +75,10 @@ unsafe impl<T> Sync for GILProtected<T> where T: Send {}
///
/// static LIST_CELL: GILOnceCell<Py<PyList>> = GILOnceCell::new();
///
/// pub fn get_shared_list(py: Python<'_>) -> &PyList {
/// pub fn get_shared_list(py: Python<'_>) -> &Bound<'_, PyList> {
/// LIST_CELL
/// .get_or_init(py, || PyList::empty(py).into())
/// .as_ref(py)
/// .get_or_init(py, || PyList::empty_bound(py).unbind())
/// .bind(py)
/// }
/// # Python::with_gil(|py| assert_eq!(get_shared_list(py).len(), 0));
/// ```

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
@ -70,7 +88,7 @@ impl PyList {
/// # fn main() {
/// Python::with_gil(|py| {
/// let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
/// let list: &PyList = PyList::new(py, elements);
/// let list = PyList::new_bound(py, elements);
/// assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5]");
/// });
/// # }
@ -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.
@ -116,7 +154,7 @@ impl PyList {
/// ```
/// use pyo3::{prelude::*, types::PyList};
/// Python::with_gil(|py| {
/// let list = PyList::new(py, [2, 3, 5, 7]);
/// let list = PyList::new_bound(py, [2, 3, 5, 7]);
/// let obj = list.get_item(0);
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
/// });
@ -263,7 +301,7 @@ pub trait PyListMethods<'py> {
/// ```
/// use pyo3::{prelude::*, types::PyList};
/// Python::with_gil(|py| {
/// let list = PyList::new(py, [2, 3, 5, 7]);
/// let list = PyList::new_bound(py, [2, 3, 5, 7]);
/// let obj = list.get_item(0);
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
/// });
@ -376,7 +414,7 @@ impl<'py> PyListMethods<'py> for Bound<'py, PyList> {
/// ```
/// use pyo3::{prelude::*, types::PyList};
/// Python::with_gil(|py| {
/// let list = PyList::new(py, [2, 3, 5, 7]);
/// let list = PyList::new_bound(py, [2, 3, 5, 7]);
/// let obj = list.get_item(0);
/// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
/// });
@ -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()