-
Notifications
You must be signed in to change notification settings - Fork 766
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
add FromPyObjectBound
trait for extracting &str
without GIL Refs
#3928
Conversation
This does need changelog as it implements |
Yeah this will definitely impact us. Thanks for flagging. |
@alex as a user do you think you'd rather have a smooth experience upgrading and know that there are some GIL Ref uses internally, or have a little pain and get cryptography completely migrated? (The pain will only come on deactivating the |
From my perspective, our migration process is going to be:
I don't think it's super useful to have "well you disabled gil-refs, but actually they're secretly still on in the background". And yes, the fact that |
Great, that is how I'm thinking about the migration too. I will aim to get this PR cleaned up with a better migration guide entry shortly. |
78425cc
to
bd01818
Compare
CodSpeed Performance ReportMerging #3928 will improve performances by 50.86%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.
Generally this looks like a good approach for the migration period. I was wondering whether we should even allow downstream implementations of FromPyObjectBound
. Since this is pretty much the next form of FromPyObject
(except for the name), maybe it's fine to leave this escape hatch open. I guess only the names would need updating once we remove the current FromPyObject
and replace it with this one.
src/types/bytes.rs
Outdated
@@ -225,7 +225,8 @@ mod tests { | |||
let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| { | |||
b.copy_from_slice(b"Hello Rust"); | |||
Ok(()) | |||
})?; | |||
})? | |||
.as_borrowed(); |
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.
Any particular reason to not switch to new_bound_with
instead now? (Same below)
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.
None other than me being a bit sloppy! To make things more manageable at the beginning of the migration I put #[allow(deprecated)]
on quite a few of the test modules, which I think we will need to clean up on the way to 0.22.
/// | ||
/// Users are advised against calling this method directly: instead, use this via | ||
/// [`Bound<'_, PyAny>::extract`] or [`Py::extract`]. | ||
fn from_py_object_bound(ob: &'a Bound<'py, PyAny>) -> PyResult<Self>; |
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.
We could also already call this from_py_object
, but maybe it's better to have a clear distinction for now...
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.
I think the one question which made me hesitate on encouraging users to use this trait too much (and also make this name clunky) is that I think to resolve the TODO
in instance.rs:
// TODO it might be possible to relax this bound in future, to allow
// e.g. `.extract::<&str>(py)` where `py` is short-lived.
'py: 'a,
we would need to make this trait accept ob: Borrowed<'a, 'py, PyAny>
as the input, as that's basically &Bound
with additional flexibility on the relationship between 'a
and 'py
.
It felt to me that this is a hurdle we can cross down the line, and working with Bound
for now keeps it simple...
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, using Borrowed
there would certainly be an option. I agree that this is a question best answered once we actually removed the gil-refs and do the final migration. We could also seal this trait for now (but maybe that's a bit drastic). Removing a seal in a patch release, if it turns out to be necessary, would also be an option.
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.
Let's seal this, and leave a note saying that "if you have a need to use this trait, please let us know and help us work out the design".
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.
Ok, I pushed an additional commit to seal the trait.
&str
FromPyObject questionFromPyObjectBound
trait for extracting &str
without GIL Refs
Coverage aside (which I'm now resigned to being a bit lax on while the gil-refs API leads to horrendous duplication), I think this is now good to review. I'm aiming to draft (probably tomorrow evening) a few notes for the documentation. After that, and with this merged, I think it's time we shipped a beta release! |
Just thinking further on this, I think the alternative would be to make one small breaking change to If we changed the definition of pub trait FromPyObject<'a, 'py>: Sized {
fn extract(ob: &'py PyAny) -> PyResult<Self> {
Self::extract_bound(&ob.as_borrowed())
}
fn extract_bound(ob: &'a Bound<'py, PyAny>) -> PyResult<Self> {
Self::extract(ob.clone().into_gil_ref())
}
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::Any
}
} that is, we add the lifetime We could explain in the migration guide exactly how users update their code. I think the two main cases would be:
I think this would be acceptable breakage in my eyes if it leaves PyO3 in a more consistent state. I actually suspect that many users don't name the Maybe the one unresolved problem would be how the Ungh, I really want to get that beta release out, but also I think the decision about what to do here is worth doing correctly... 😂 |
Ok, so I tried the above but it doesn't work because that proposed definition of So I think that we cannot achieve that proposal in 0.21 without more breakage. I think this confirms that such a change will need to wait to 0.23 when we remove Glad I explored that, as it makes me confident that proceeding with this PR is the right approach. |
832285a
to
50277e9
Compare
I believe I have also explored this in the past (maybe during the initial |
e10d919
to
263fc02
Compare
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.
LGTM
This is an attempt to solve the problem that
FromPyObject for &str
needs GIL Refs for versions under 3.10, and also can't be expressed until we add a second lifetime toFromPyObject
because the data needs to be borrowed from the input, not from'py
.I do this by creating a new trait
FromPyObjectBound
which is expected to be the future form ofFromPyObject
. I think for now we recommend users not to implement it unless they have the same borrow problem.For backwards compatibility reasons I move as little code as possible onto this new trait. Just
.extract()
methods and also argument extraction code use this new trait internally.On the implementation side, this trait gets a blanket for anything implementing
FromPyObject
.Finally, without the
gil-refs
feature we remove the existing implementation ofFromPyObject for &str
and instead implement this new trait. This works except forabi3
on old Python versions, where we have to settle forPyFunctionArgument
only and allow users of.extract()
to break.This does implement some very targeted breakage for specific considerations, but I wonder if this is necessary as the correct way to users off the GIL Ref API in a case where otherwise it's near impossible for them to realise this uses it.
I can't see a way to do this any gentler.