Add extract_bound method to FromPyObject#3706
Conversation
fad596b to
c47e528
Compare
CodSpeed Performance ReportMerging #3706 will degrade performances by 32.02%Comparing Summary
Benchmarks breakdown
|
| /// Extracts `Self` from the source GIL Ref `obj`. | ||
| /// | ||
| /// Implementors are encouraged to implement `extract_bound` and leave this method as the | ||
| /// default implementation, which will forward calls to `extract_bound`. | ||
| fn extract(ob: &'source PyAny) -> PyResult<Self> { | ||
| Self::extract_bound(&ob.as_borrowed()) | ||
| } | ||
|
|
||
| /// Extracts `Self` from the bound smart pointer `obj`. | ||
| /// | ||
| /// Implementors are encouraged to implement this method and leave `extract` defaulted, as | ||
| /// this will be most compatible with PyO3's future API. | ||
| fn extract_bound(ob: &Bound<'source, PyAny>) -> PyResult<Self> { | ||
| Self::extract(ob.clone().into_gil_ref()) | ||
| } |
There was a problem hiding this comment.
The default implementation has infinite recursion if a user implements neither method. This is a slightly clunky footgun but I'm fine with it as a temporary measure; I think in 0.22 once we start pushing harder then we can just default extract and require an implementation of extract_bound.
There was a problem hiding this comment.
Does implementing this using only the defaults trigger a warning due to unconditional mutual recursion?
There was a problem hiding this comment.
Sadly no, it just leads to a stack overflow at runtime. I've tried to repeat the expectation in documentation that implementors should implement extract_bound.
Hopefully it's a fairly obvious programming error, and at least we can get rid of the .extract_bound() default in 0.22 and remove this footgun again.
|
Unless we can think of alternatives here, I'd quite like to merge this PR, as merging this unblocks us to migrate the rest of PyO3's implementations of |
adamreichold
left a comment
There was a problem hiding this comment.
I agree that the general approach is the best we can do for 0.21. I think this should be highlighted in the migration guide though, either as part of this PR or as a task item on the tracking issue.
|
Thank you for all the reviews today! I'll have plenty of PRs to push once this one goes in ;) Completely agree that this is worthy of going in the migration guide. I'll add it to this PR; I'll rebase once #3755 goes in and add some detail before merging this. |
68927d8 to
ffaa03e
Compare
This PR proposes a way that we can support migration of
FromPyObjectoff the GIL Refs API in a backwards-compatible fashion.It does so by adding a new
extract_boundmethod toFromPyObject<'py>, which takes&Bound<'py, PyAny>. Note that the lifetime'pyin the trait becomes only the'pylifetime and not the lifetime for which theBoundsmart pointer is alive. This is important for backwards-compatibility; maybe in the future we could haveFromPyObject<'a, 'py>and the argument could be&'a Bound<'py, PyAny>, but let's cross that bridge another time.Also for backwards-compatibility's sake, both
extractandextract_boundhave default implementations in terms of each other. This allows for users to migrate from using one set of implementations to the other at their own pace. The documentation encourages to implement justextract_bound, as the default implementation requires a.into_gil_ref()call to insert into the pool.To avoid a huge diff here, I've pushed a second commit which just includes a couple of randomly-selected implementations which I migrate from
extracttoextract_bound, to give a feeling for how this works.If we like this and merge it, I can work on one or more follow-ups which migrate the rest of our internals to
extract_bound(which will go a long way towards getting PyO3's internals off the pool).