-
Notifications
You must be signed in to change notification settings - Fork 763
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
feat: add coroutine __name__
/__qualname__
#3588
Conversation
CodSpeed Performance ReportMerging #3588 will not alter performanceComparing Summary
|
@wyfo if you can rebase this please I'll get around to reviewing as soon as possible after 👍 |
9502795
to
5cafd8b
Compare
src/coroutine.rs
Outdated
#[getter] | ||
fn __name__(&self) -> PyResult<Py<PyString>> { | ||
self.name | ||
.clone() |
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.
Not sure if it is worth it, but using clone_ref
would avoid the implicit check for the GIL.
src/coroutine.rs
Outdated
.as_ref() | ||
.map_or(Ok("<coroutine>"), |n| n.as_ref(gil).to_str()) | ||
.unwrap(); | ||
let message = format!("coroutine {} was never awaited", qualname); |
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.
Does this only happen if it was never awaited or also if it was not polled to completion?
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 seemed to me that the warning was always raised when the coroutine was not run to completion (even partially), but I've checked again, and I was wrong.
I will fix that behavior.
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.
In fact, instead of testing self.future.is_some()
, I should test self.waker.is_none()
, as it's always Some(_)
when the coroutine has been polled at least once.
src/coroutine.rs
Outdated
.map_or(Ok("<coroutine>"), |n| n.as_ref(gil).to_str()) | ||
.unwrap(); |
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.
.map_or(Ok("<coroutine>"), |n| n.as_ref(gil).to_str()) | |
.unwrap(); | |
.and_then(|n| n.as_ref(gil).to_str().ok()) | |
.unwrap_or("<coroutine>"); |
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.
Your suggestion change the effect of the code. I wanted to panic in case of to_str
error, but panicking may not be the best for a warning, you're right.
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, this diff is now nicely focussed just on the async code so I've given it a full review 👍
I agree with @adamreichold's points and also added a couple of brief thoughts of my own.
pyo3-macros-backend/src/method.rs
Outdated
call = quote! {{ | ||
let future = #call; | ||
_pyo3::impl_::coroutine::new_coroutine( | ||
_pyo3::types::PyString::new(py, stringify!(#python_name)).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 would probably be worth interning here using our intern!
macro, and changing new_coroutine
to take N: IntoPy<Py<PyString>>
.
src/impl_/coroutine.rs
Outdated
pub fn coroutine_qualname<'py>( | ||
py: Python<'py>, | ||
module: Option<&PyModule>, | ||
name: &str, | ||
) -> &'py PyString { | ||
match module.and_then(|m| m.name().ok()) { | ||
Some(module) => PyString::new(py, &format!("{}.{}", module, name)), | ||
None => PyString::new(py, name), | ||
} | ||
} | ||
|
||
pub fn method_coroutine_qualname<'py, T: PyClass>(py: Python<'py>, name: &str) -> &'py PyString { | ||
let class = T::NAME; | ||
let qualname = match T::MODULE { | ||
Some(module) => format!("{}.{}.{}", module, class, name), | ||
None => format!("{}.{}", class, name), | ||
}; | ||
PyString::new(py, &qualname) | ||
} |
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 wonder, to avoid Python string creation with every coroutine, can this be done lazily (by e.g. storing a reference to T::type_object
, or taking a function pointer, or storing a reference to the module etc).
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.
You're right. Actually, methods __qualname__
could be interned too, as class name and module name are static. The issue is for free functions, because it would requires to store the module.
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've finally used a qualname_prefix
instead of passing the __qualname__
directly.
The constructor parameter may be modified when it will be exposed (maybe using Option<Cow<'static, str>>
instead of Option<&'static str>
for example), but for now, it's adapted to the macro and that's enough.
src/coroutine.rs
Outdated
if self.future.is_some() { | ||
Python::with_gil(|gil| { |
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 structure reminds me of #3386 (comment).
I'm not entirely sure if it's a blocker to not have try_with_gil
or some other guard to avoid potential nasty crashes here. We should have a little think about that before merge.
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 don't really understand how could with_gil
crash here, but I know that Drop::dorp
is not the best place to put this warning. In fact, it should go in __del__
(the same as CPython coroutines), but it's not supported yet in PyO3.
Maybe we can postpone the warning implementation until #2479 is resolved, as this feature is quite nonessential? We can also already write the __del__
method, as it has no effect for now, and it will work as a side effect of the __del__
support.
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.
Ahh that's a good suggestion, I should really pick up __del__
support again soon. Are you suggesting split this PR in two; merge the names now and keep the warning as a draft pr for the moment?
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 am indeed.
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've opened an issue for the warning.
__name__
/__qualname__
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 looks good to me, thanks! Will follow up with discussion on the issue.
Relates to #1632