-
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
Pymodule bound #3897
Pymodule bound #3897
Conversation
2f8f123
to
031f033
Compare
Co-authored-by: David Hewitt <[email protected]>
031f033
to
9db0ea9
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.
Nice to see this getting closer to landing. After this I think we can really start to update documentation and examples! 🚀
I have added some thoughts, but you should definitely wait for a second opinion.
@@ -126,13 +126,42 @@ macro_rules! py_run_impl { | |||
macro_rules! wrap_pyfunction { |
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 one should now get a deprecation warning I thing
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.
Good spot! I think maybe this makes sense to do in a separate PR since it'll cause a lot of churn in tests.
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.
Makes sense 👍
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.
Looks like that is #3899
Co-authored-by: Matthew Neeley <[email protected]>
This allows us to remove the awkward `PyFunctionArgumentsBound` enum.
I pushed a final commit which removes the I couldn't find a good way to describe what I was thinking until I'd tried it out, at which point I had the commit ready to go 🙈 Do let me know what you think of that last commit, if it seems good, maybe this is ready to merge?! |
Ok so I had a slightly nutty magical idea to use some trickery to automatically infer the output type of If the input argument is The nice thing about this is that then for the vast majority of user code they don't need to change anything in their I pushed that as a final commit. What do you think? |
CodSpeed Performance ReportMerging #3897 will improve performances by 11.35%Comparing Summary
Benchmarks breakdown
|
|
||
pub fn add_to_module(module: &#krate::Bound<'_, #krate::types::PyModule>) -> #krate::PyResult<()> { | ||
use #krate::prelude::PyModuleMethods; | ||
use ::std::convert::Into; | ||
module.add_function(&#krate::types::PyCFunction::internal_new(&DEF, module.as_gil_ref().into())?) | ||
module.add_function(#krate::types::PyCFunction::internal_new(&DEF, module.as_gil_ref().into())?) |
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's a bit sad to still need this as_gil_ref()
call 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.
LGTM. I think it'd be good to move forward with this and we can revisit the macro import hygiene in a follow-up.
let krate = get_pyo3_crate(&options.krate); | ||
|
||
let mut stmts: Vec<syn::Stmt> = vec![syn::parse_quote!( | ||
#[allow(unknown_lints, unused_imports, redundant_imports)] | ||
use #krate::{PyNativeType, types::PyModuleMethods}; |
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 hesitant to add this kind of use
statement, which transparently injects things into the namespace of user code and so would seem to violate hygiene (see discussion in the follow-up PR here: #3899 (comment)). An alternative would be to use fully-qualified references to trait methods in the macro-generated code. But as @Icxolu noted in that discussion, the patterns around these new *Methods
traits are not very well established, so I could be persuaded :-)
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.
Yeah, after that discussion I tend to agree, that we should not inject these traits silently. If we still want to do it we should probably at least import them anonymously e.g. as use "Trait" as _
. I would not consider it a blocker here, we can change in a followup if needed. Another thing would be if we should seal these traits, but that's another discussion entirely.
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.
Agreed. Maybe a good solution here it to write a private trait in impl_/pymodule.rs
which has just the methods we need implemented for both Bound<'_, PyModule>
and &'py PyModule
, which we can then call directly without imports.
(This is actually probably very similar to what @LilyFoote had already done with the #[doc(hidden)] fn wrap_pyfunction
, just located as a private trait inside the impl codebase.)
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.
Nice solution, this look pretty elegant. I have found a minor point regarding PyFunction::internal_new_bound
plus two general comments, otherwise LGTM!
@@ -189,6 +206,35 @@ impl PyCFunction { | |||
.downcast_into_unchecked() | |||
} | |||
} | |||
|
|||
#[doc(hidden)] | |||
pub(crate) fn internal_new_bound<'py>( |
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 we don't need this. We can change the signature of internal_new
to match the new _bound
APIs and move let (py, module) = py_or_module.into_py_and_maybe_module();
into the deprecated gil-ref APIs
@@ -590,7 +591,7 @@ pub trait PyModuleMethods<'py> { | |||
/// | |||
/// [1]: crate::prelude::pyfunction | |||
/// [2]: crate::wrap_pyfunction | |||
fn add_function(&self, fun: &Bound<'_, PyCFunction>) -> PyResult<()>; | |||
fn add_function(&self, fun: Bound<'_, PyCFunction>) -> PyResult<()>; |
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 guess this is just to make it nicer, so that we don't have to use&wrap_pyfunction!
instead of just wrap_pyfunction!
?
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.
Yeah I think it's probably worth making this change? It's not like we expect this to be called in a hot loop. I guess we could also have impl AsRef<Bound<'_, PyCFunction>>
?
pub trait WrapPyFunctionArg<'py, T> { | ||
fn wrap_pyfunction(self, method_def: &PyMethodDef) -> PyResult<T>; | ||
} |
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.
Very interesting solution! I guess T
could also be an associated type given that it's not intended to be implemented more than once per type. But since this is internal API, I'm fine either way.
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.
Yes actually I was thinking that later on today, I had generic originally because I wanted to play with type inference but given that each type now implements only once we could go for the associated type.
let krate = get_pyo3_crate(&options.krate); | ||
|
||
let mut stmts: Vec<syn::Stmt> = vec![syn::parse_quote!( | ||
#[allow(unknown_lints, unused_imports, redundant_imports)] | ||
use #krate::{PyNativeType, types::PyModuleMethods}; |
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.
Yeah, after that discussion I tend to agree, that we should not inject these traits silently. If we still want to do it we should probably at least import them anonymously e.g. as use "Trait" as _
. I would not consider it a blocker here, we can change in a followup if needed. Another thing would be if we should seal these traits, but that's another discussion entirely.
Thanks all! I think given this PR seems to be blocking a lot of the final refinements to the codebase which we need for 0.21, and we're happy enough with this as-is, I think let's merge this as-is. There are quite a few follow-up items. Anyone got time / interest in taking them on? Otherwise I can try to do these maybe at the weekend. (I also just made some comments on other issues specifically about the trait sealing...) |
Builds upon #3744.