Skip to content

Commit a60c182

Browse files
committed
implement PyFunctionArgument for &Bound<T>
1 parent a3eb328 commit a60c182

File tree

4 files changed

+32
-1
lines changed

4 files changed

+32
-1
lines changed

guide/src/migration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ Because the new `Bound<T>` API brings ownership out of the PyO3 framework and in
247247
- 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)
248248
- `Bound<PyList>` and `Bound<PyTuple>` cannot support indexing with `list[0]`, you should use `list.get_item(0)` instead.
249249
- `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.
250+
- `&Bound<T>` does not implement `FromPyObject` (although it might be possible to do this in the future once the GIL Refs API is completely removed). Use `bound_any.downcast::<T>()` instead of `bound_any.extract::<&Bound<T>>()`.
250251

251252
#### Migrating `FromPyObject` implementations
252253

src/impl_/extract_argument.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
ffi,
44
pyclass::boolean_struct::False,
55
types::{PyDict, PyString, PyTuple},
6-
FromPyObject, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, Python,
6+
Bound, FromPyObject, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, PyTypeCheck, Python,
77
};
88

99
/// A trait which is used to help PyO3 macros extract function arguments.
@@ -31,6 +31,18 @@ where
3131
}
3232
}
3333

34+
impl<'a, 'py, T> PyFunctionArgument<'a, 'py> for &'a Bound<'py, T>
35+
where
36+
T: PyTypeCheck,
37+
{
38+
type Holder = Option<Bound<'py, T>>;
39+
40+
#[inline]
41+
fn extract(obj: &'py PyAny, holder: &'a mut Option<Bound<'py, T>>) -> PyResult<Self> {
42+
Ok(&*holder.insert(obj.extract()?))
43+
}
44+
}
45+
3446
/// Trait for types which can be a function argument holder - they should
3547
/// to be able to const-initialize to an empty value.
3648
pub trait FunctionArgumentHolder: Sized {

tests/test_pyfunction.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,20 @@ fn test_some_wrap_arguments() {
529529
py_assert!(py, function, "function() == [1, 2, None, None]");
530530
})
531531
}
532+
533+
#[test]
534+
fn test_reference_to_bound_arguments() {
535+
#[pyfunction]
536+
fn reference_args<'py>(
537+
x: &Bound<'py, PyAny>,
538+
y: Option<&Bound<'py, PyAny>>,
539+
) -> PyResult<Bound<'py, PyAny>> {
540+
y.map_or_else(|| Ok(x.clone()), |y| y.add(x))
541+
}
542+
543+
Python::with_gil(|py| {
544+
let function = wrap_pyfunction!(reference_args, py).unwrap();
545+
py_assert!(py, function, "function(1) == 1");
546+
py_assert!(py, function, "function(1, 2) == 3");
547+
})
548+
}

tests/ui/invalid_argument_attributes.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ error[E0277]: the trait bound `CancelHandle: Clone` is not satisfied
9393
| ^^^^ the trait `Clone` is not implemented for `CancelHandle`
9494
|
9595
= help: the following other types implement trait `PyFunctionArgument<'a, 'py>`:
96+
&'a pyo3::Bound<'py, T>
9697
&'a pyo3::coroutine::Coroutine
9798
&'a mut pyo3::coroutine::Coroutine
9899
= note: required for `CancelHandle` to implement `FromPyObject<'_>`

0 commit comments

Comments
 (0)