-
Notifications
You must be signed in to change notification settings - Fork 30
Update to pyo3 v0.22 #104
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 to pyo3 v0.22 #104
Conversation
@dicej I don't think I fully grasped that the runtime crate was getting embedded. I'll take another closer look at what made the tests fail before you have to review to save you some time |
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.
@dicej I have gotten it close, but there are still a few failing tests around resources.
Overall, I think I am likely missing some context on how best to represent lifetimes for some of these pointers, but hopefully this is still a helpful starting place?
anyhow = "1.0.87" | ||
once_cell = "1.19.0" | ||
pyo3 = { version = "0.20.0", features = ["abi3-py311", "num-bigint"] } | ||
pyo3 = { version = "0.22.2", features = ["abi3-py312", "num-bigint"] } |
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.
It seems like since this is embedded in the WIT world, we can tie this to the version of cpython we are using?
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.
Another thought: If I understand the resolver correctly, this will likely actually be targeting abi for 3.9? Since pyo3 is shared across the crates and it goes to the lowest enabled feature?
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.
Features are additive, so presumably both the abi3-py312
and abi3-py39
features will be enabled. Not sure what that means in practice.
Looks like the resource tests are failing due to reference count issues; finalizers are either being run too soon or not at all. Formerly, we relied on pyo3 v0.20's |
Thanks for looking. If there isn't an immediately obvious solution, I'm also fine putting this on pause for a bit depending on your availability |
Hmm rereading the migration docs (yet again) I think I missed a few things in there as well. I can take another look as well to try and make some of this closer to the previous implementation |
Signed-off-by: Joel Dice <[email protected]>
Py03 0.22.x no longer has `Py::into_ref`, which returned a reference that was guaranteed to survive until the GIL was released. We were relying on that to ensure borrow handles were released before returning to the caller. Since there's no equivalent feature in 0.22.x AFAICT, we need to clean up borrow handles ourselves. This also fixes `componentize_py_to_canon_handle`, which was causing reference counts to be decremented too early. Note that, although the tests are now passing, there may still be leaks due to reference counts not being decremented where they should. Not sure what the best way to test that might be; we might need to research Python tools for heap profiling and leak detection. Finally, I haven't tested the examples yet; we'll want to do that before we merge any of this into main. Signed-off-by: Joel Dice <[email protected]>
It felt strange that the C apis were all returning objects with lifetimes. This updates everything that returned a Bound to always return a Py, which seems safer, and is also closer to what the WASM side expects (even though currently they are laid out in memory the same).
ty: usize, | ||
field: usize, | ||
) -> &'a PyAny { | ||
) -> Py<PyAny> { |
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.
@dicej I did another pass and changed all of these C apis to return a Py
. On second thought, it felt really strange to be returning something with a lifetime created in the scope of this function over this boundary. Maybe it was technically ok, but this also seems to be closer to what the WASM side expects (a single pointer).
#[export_name = "componentize-py#ToCanonBool"] | ||
pub extern "C" fn componentize_py_to_canon_bool(_py: &Python, value: &PyAny) -> u32 { | ||
if value.is_true().unwrap() { | ||
pub unsafe extern "C" fn componentize_py_to_canon_bool(py: &Python, value: &PyAny) -> u32 { |
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'm a bit concerned that we've gone from a function that is safe and has no unsafe code, to a safe-to-call function with an unsafe block inside it, to an unsafe-to-call function whose entire body is unsafe.
Granted, the only caller currently is generated Wasm code, which doesn't have any notion of Rust's rules, but now we have to document the requirements to call these functions beyond what's indicated by the types. Speaking of which, what makes this function unsafe to call (versus being safe to call but containing unsafe code in the body)?
Also, more (and larger) unsafe code blocks greatly increase the maintenance burden, so I want to confirm that there's no way to avoid them when upgrading PyO3. In principle, this function has pretty simple requirements: it needs proof that the caller holds the GIL (as indicated by the &Python
parameter) and it needs a valid reference to a Python value which lives at least as long as the call (the &PyAny
parameter). With both of those things satisfied, it's not clear to me why we'd need unsafe code to examine the value. Was the old PyO3 API unsound (i.e. providing only the illusion of safety), or have we just not hit upon the correct way to use the new API while avoiding unsafe
?
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.
Here's another option that doesn't require any unsafe
:
#[export_name = "componentize-py#ToCanonBool"]
pub extern "C" fn componentize_py_to_canon_bool(
py: &Python,
value: ManuallyDrop<Py<PyAny>>,
) -> u32 {
if value.is_truthy(*py).unwrap() {
1
} else {
0
}
}
It's not terribly pretty, and I suspect there are better options, but it works. I'll need to study the docs more.
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.
This might be better:
#[export_name = "componentize-py#ToCanonBool"]
pub extern "C" fn componentize_py_to_canon_bool(_py: &Python, value: Borrowed<PyAny>) -> u32 {
if value.is_truthy().unwrap() {
1
} else {
0
}
}
I feel like there's a type missing in PyO3. We have Bound
(owned, with proof of GIL), Borrowed
(borrowed, with proof of GIL), and Py
(owned, no proof of GIL), but there's no BorrowedPy
(borrowed, no proof of GIL) -- that's the one we really want here.
@dicej fair points. I think the ultimate issue is that we don't currently have a good way to test the changes. And while pyo3 claims to fix some soundness issues, I am worried I've potentially just created a lot more. I think I'll put a pause on this to give us both a break as there isn't currently a pressing need to upgrade. And I might try and come at this with fresh eyes later |
SGTM. For what it's worth, I think that if we make appropriate use of the |
Second attempt at solving the issues in bytecodealliance#104 This time I didn't need to introduce any extra unsafe, and I think the signatures match the intention of the previous code as best as I can understand.
* Update to pyo3 v0.22.5 Second attempt at solving the issues in #104 This time I didn't need to introduce any extra unsafe, and I think the signatures match the intention of the previous code as best as I can understand. * bump minor version deps * fix python versions
Updates to the latest version of py03. They have had quite a few breaking changes, but I attempted to do the minimum amount of changes to get to the latest version