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.
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.
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.
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.
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.
| .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.
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.
davidhewitt
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
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.
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.
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.
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.
I've opened an issue for the warning.
__name__/__qualname__
davidhewitt
left a comment
There was a problem hiding this comment.
This looks good to me, thanks! Will follow up with discussion on the issue.
Relates to #1632