Merge pull request #3755 from davidhewitt/list-bound
add list bound constructors
This commit is contained in:
commit
796e4192b7
|
@ -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(())
|
||||
|
|
|
@ -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>());
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
|
|
|
@ -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]");
|
||||
/// });
|
||||
/// ```
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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));
|
||||
/// ```
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
|
|
@ -697,6 +697,7 @@ where
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(not(PyPy))]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue