bpo-40607: Reraise exception during task cancelation in asyncio.wait_for()#20054
bpo-40607: Reraise exception during task cancelation in asyncio.wait_for()#200541st1 merged 1 commit intopython:masterfrom
Conversation
|
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept this contribution by verifying everyone involved has signed the PSF contributor agreement (CLA). Recognized GitHub usernameWe couldn't find a bugs.python.org (b.p.o) account corresponding to the following GitHub usernames: This might be simply due to a missing "GitHub Name" entry in one's b.p.o account settings. This is necessary for legal reasons before we can look at this contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. You can check yourself to see if the CLA has been received. Thanks again for the contribution, we look forward to reviewing it! |
Lib/asyncio/tasks.py
Outdated
There was a problem hiding this comment.
Could it be
if not fut.cancelled():
return fut.result()?
There was a problem hiding this comment.
Building on this, how about--
try:
return fut.result()
except CancelledError as exc:
raise TimeoutError() from excWhat's nice about this is that you'll see the CancelledError that caused the TimeoutError in the traceback. This will be even better when this PR is merged: #19951
In the traceback, you'll get to see exactly what line of code was interrupted by the cancellation.
There was a problem hiding this comment.
OK, thanks, I find this even more appropriate way to handle such situations.
Lib/asyncio/tasks.py
Outdated
There was a problem hiding this comment.
What about the case when task cancellation failed with some result (not exception)? Is it OK to ignore it? I mean something like
async def inner():
try:
await asyncio.sleep(0.2)
except asyncio.CancellationError:
return 123 # instead of `raise FooException`|
I signed CLA yesterday, but for some reason checker returns 500 for my username. Is it OK and I should wait some more time? |
There was a problem hiding this comment.
A couple higher-level comments before a more detailed review:
-
In the
TimeoutErrorcase, it would be good to check that theCancelledErroris getting chained. You can see an example of how to check for that in a test here: #19858
Maybe this can be added to another existing test. I'm not sure as I haven't looked. -
It looks like your tests take 0.1 seconds to run, which adds up a lot given the number of tests. It would be better to design them so they run without having to sleep for non-negligible amounts of time but still be tests that are (1) simple and (2) not flaky. This may take a little more creativity, or it might be easy. I'm not sure, but it should be doable.
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
Seems to work fine when I entered |
Lib/asyncio/tasks.py
Outdated
There was a problem hiding this comment.
I'd put also an else: raise TimeoutError
There was a problem hiding this comment.
Sorry, but I think that else branch is unreachable: if there isn't any exception in try block, then return works normally and we leave this method. Please explain if I am wrong.
There was a problem hiding this comment.
Yes, but your new code is simply not equivalent to the previous code that raised TimeoutError unconditionally. Now I see that there might be a condition for clear return and I don't want that.
There was a problem hiding this comment.
The linked issue talks about the correct exception propagation. Basically, wait_for timeouts with an exception and we want that original exception to be propagated. That's fine and I'm +1 for that.
The code here is slightly different though and a bit hard to trace so it would be easier to read if it was replaced with:
try:
fut.result()
except exceptions.CancelledError as exc:
raise exceptions.TimeoutError() from exc
else:
raise exceptions.TimeoutError()There was a problem hiding this comment.
I think the issue here is that there is a race condition where the task can complete after the timeout has elapsed. So the question is whether the caller should get to learn that the task completed in this case.
There was a problem hiding this comment.
Yes, the current implementation discards the result, as the probability of actually having it is low. I'd like to keep it that way. If we want to change the current behavior that should be done in a separate issue, with a new PR and new discussion.
|
I entered "@romasku" (with '@' symbol) as my github username in b.p.o. account, and was checking against it. Now everything works, thank you! |
|
Thank you for your review! |
|
I have made the requested changes; please review again |
|
Thanks for making the requested changes! @cjerdonek: please review the changes made to this pull request. |
Lib/asyncio/tasks.py
Outdated
There was a problem hiding this comment.
Yes, but your new code is simply not equivalent to the previous code that raised TimeoutError unconditionally. Now I see that there might be a condition for clear return and I don't want that.
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
This looks great now. @cjerdonek please review again. |
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
I have made the requested changes; please review again |
|
Thanks for making the requested changes! @cjerdonek, @1st1: please review the changes made to this pull request. |
|
@romasku, I forgot there are a couple more administrative things. Can you add your name to |
…or() Currently, if asyncio.wait_for() timeout expires, it cancels inner future and then always raises TimeoutError. In case those future is task, it can handle cancelation mannually, and those process can lead to some other exception. Current implementation silently loses thoses exception. To resolve this, wait_for will check was the cancelation successfull or not. In case there was exception, wait_for will reraise it.
|
@cjerdonek, done. I am happy to contribute! |
|
Thanks so much, Roman! |
…for() (pythonGH-20054) Currently, if asyncio.wait_for() timeout expires, it cancels inner future and then always raises TimeoutError. In case those future is task, it can handle cancelation mannually, and those process can lead to some other exception. Current implementation silently loses thoses exception. To resolve this, wait_for will check was the cancelation successfull or not. In case there was exception, wait_for will reraise it. Co-authored-by: Roman Skurikhin <roman.skurikhin@cruxlab.com>
https://bugs.python.org/issue40607