-
Notifications
You must be signed in to change notification settings - Fork 782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update #[derive(FromPyObject)]
to use extract_bound
#3828
Conversation
I think we want to abstract over the return value and are in macro context? So we could use autoref-based specialization like we do for iterator return values and other things? But admittedly I am not a fan of this and would personally prefer option 2. |
We might be able to get away without specialization and just use some type inference to wrap the closure at the points where we need it. I was playing with a similar trick for |
I think we would need to abstract over the whole (user provided) closure type, no?
I tried something like this, but wrapping closures is always a bit weird, because we can't name them. I also ran into problems with overlapping trait implementations for the conversions. (In principle there could be a single type which implements |
With function pointers we could do something like this: pub enum Extractor<'a, 'py, T> {
Bound(fn(&'a Bound<'py, PyAny>) -> PyResult<T>),
GilRef(fn(&'a PyAny) -> PyResult<T>),
}
impl<'a, 'py, T> From<fn(&'a Bound<'py, PyAny>) -> PyResult<T>> for Extractor<'a, 'py, T> {
fn from(value: fn(&'a Bound<'py, PyAny>) -> PyResult<T>) -> Self {
Self::Bound(value)
}
}
impl<'a, T> From<fn(&'a PyAny) -> PyResult<T>> for Extractor<'a, '_, T> {
fn from(value: fn(&'a PyAny) -> PyResult<T>) -> Self {
Self::GilRef(value)
}
}
impl<'a, 'py, T> Extractor<'a, 'py, T> {
fn call(self, obj: &'a Bound<'py, PyAny>) -> PyResult<T> {
match self {
Extractor::Bound(f) => f(obj),
Extractor::GilRef(f) => f(obj.as_gil_ref()),
}
}
} And since |
CodSpeed Performance ReportMerging #3828 will degrade performances by 15.02%Comparing Summary
Benchmarks breakdown
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, nicely solved!
And since
from_py_with
has to be a ExprPath I think all uses have to real functions (or methods) anyway.
I agree with this thinking too.
I think this implementation will probably lead to some painful error messages for users if they get the type of the from_py_with
function wrong. I suspect that it'd be hard to do much better and we can remove the Extractor
wrapper in the future to get better type hints in errors again 👍
In this PR I've converted the
FromPyObject
proc-macro to output the newextract_bound
function. Most of this should be completely invisible to downstream, but one place where this leaks out is the#[pyo3(from_py_with = ...)]
attribute.So leaving this as is, would be a breaking change. I don't really see a good way of abstracting over two function types with different signatures.
I can see two options to avoid the breakage:
#[pyo3(from_py_with_bound = ...)]
attributeProbably option 2 is preferable, so that we can provide an upgrade path. But maybe there is another option which I don't see right now... 🙃