Skip to content

support Bound for classmethod and pass_module#3831

Merged
davidhewitt merged 4 commits intoPyO3:mainfrom
davidhewitt:bound-classmethod-pass-module
Feb 16, 2024
Merged

support Bound for classmethod and pass_module#3831
davidhewitt merged 4 commits intoPyO3:mainfrom
davidhewitt:bound-classmethod-pass-module

Conversation

@davidhewitt
Copy link
Member

This is a tidied-up copy of #3744 without any attempts to change #[pymodule].

This PR focuses on updating #[classmethod] and #[pyfunction(pass_module)] to accept &Bound<PyType> and &Bound<PyModule> respectively. It allows them to accept GIL Refs for backwards compatibility.

We were already using Into for these conversions, so I created a new helper type BoundRef(&Bound<T>) which implements the Into conversions for &T, &Bound<T>, and Py<T>. The reason for the new helper type was because I didn't want to support From<&Bound<T>> for &T.

| ^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `i32: From<&PyType>` is not satisfied
error[E0277]: the trait bound `i32: From<BoundRef<'_, '_, PyType>>` is not satisfied
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is notably worse than it was before, but I think once we remove the GIL Ref compatibility and go back to just &Bound<PyType> it improves. I can live with it being a bit janky for a couple of releases. If reviewers feel strongly that we should try to make this nicer I can experiment at cost of making the implementation more complex.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(One upside of a more complex implementation is that I might be able to emit deprecation warnings for using GIL Refs in these positions.)

@davidhewitt davidhewitt added the CI-skip-changelog Skip checking changelog entry label Feb 13, 2024
src/instance.rs Outdated
}

#[inline]
pub(crate) unsafe fn from_ref_to_ptr<'a>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really get this name. How about from_borrowed_ptr?

Same on BoundRef

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I couldn't work out a good name either. I didn't feel borrowed is quite right here because that's got a very specific meaning in the Python C API to refer to a pointer return value which doesn't carry ownership.

How about just from_raw?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think this API will remain crate-private for the foreseeable future, so the name doesn't have to be perfect 😄)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, since this is internal its not super important. I'm also fine with from_raw. I think I also get now that you were intending to reference this (unusual) construct & *mut _, but yesterday i read it like it was turning a reference into a pointer, while it was doing kind of the opposite 🙈

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see why the from_x_to_y bit reads wrong.

On reflection I think Bound::from_raw isn't quite the right name for this API. I still can't think of anything better. from_shared_ref_ptr? from_borrow_on_raw_pointer? from_ptr_shared_ref?

I'm kinda ok with a clumsy name here given this is an internal API really only intended to give a lifetime 'a in &'a Bound<'_, PyAny>.

I think of the options I wrote above, from_ptr_shared_ref might make the most sense to me. Any of those which you like?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I have a particular preference among these. If i had to pick i would probably choose between from_shared_ref_ptr and from_ptr_shared_ref. So I'm fine with your preference. Another option that came to my mind was something like ref_from_ptr or shared_from_ptr, but I dont't feel strongly about these either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I quite like ref_from_ptr, let's go with that 👍

Copy link
Member

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

@davidhewitt
Copy link
Member Author

Thanks for the review! I decided to add a doc to ref_from_ptr and will proceed to merge 👍

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 16, 2024

CodSpeed Performance Report

Merging #3831 will degrade performances by 15.02%

Comparing davidhewitt:bound-classmethod-pass-module (ebfc2c3) with main (f3ddd02)

Summary

❌ 2 regressions
✅ 77 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark main davidhewitt:bound-classmethod-pass-module Change
list_via_downcast 157.2 ns 185 ns -15.02%
not_a_list_via_downcast 216.7 ns 244.4 ns -11.36%

@davidhewitt davidhewitt added this pull request to the merge queue Feb 16, 2024
Merged via the queue into PyO3:main with commit ec6d587 Feb 16, 2024
@davidhewitt davidhewitt deleted the bound-classmethod-pass-module branch February 16, 2024 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI-skip-changelog Skip checking changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants