-
-
Notifications
You must be signed in to change notification settings - Fork 34k
bpo-30679: __aexit__ is not called on KeyboardInterrupt #2219
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
Conversation
|
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately our records indicate you have not signed the CLA. For legal reasons we need you to sign this before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. Thanks again to your contribution and we look forward to looking at it! |
| # A signal from outside interrupted the execution. Cancel the | ||
| # coroutine and wait for the exit handlers to finish | ||
| future.cancel() | ||
| self.run_forever() |
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.
What's a 'signal'? It could be a simple unhandled exception. Right now, the 'except' clause is too broad to do what you are doing here.
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.
If we are running a coroutine (ie new_task is True), and future.done() is false, then the exception is not coming from inside the coroutine and has to be some kind of external signal.
The raw except: clause is very broad indeed, but the exception can actually be any kind of user defined exception here, so it makes sense to catch them all. For instance, the signal example from the docs would raise an OSError:
import asyncio, signal
def handler(signum, frame):
raise OSError("Couldn't open device!")
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.sleep(10))And in this example, the sleep(10) coroutine is never cancelled. This isn't an issue here, but is an issue in the example I provided with the PR: if you mix async contextes into this, the __aexit__ are not called
|
This PR is stale because it has been open for 30 days with no activity. |
|
This is still an existing problem with the latest version of Python, and as far as I can see, aexit is pretty broken because of this. @1st1 I replied to your comment back in 2017 - can you let me know how you think we should fix this if this PR isn't ok? |
|
This PR is stale because it has been open for 30 days with no activity. |
|
This is already fixed as commented on the issue #74864. |
Hi,
Here is the example code I am running:
When this gets interrupted by a SIGINT,I would expect this code to display
EXITbefore theKeyboardInterruptstacktrace. But instead the__aexit__function is simply not called.Here is how I understand things:
A
KeyboardInterruptgets raised inloop.run_forever, which is caught by the except clause inrun_until_complete. But this clause is supposed to handle errors that occurs in the coroutine, and not outside of it. And so, when thisKeyboardInterruptis raised again at the end of theexcept, the underlying coroutine is not cancelled and simply discarded (and the associated warning is hidden because it is explicitly disabled here.The proposed change cancels the underlying coroutine when
run_foreveris interrupted from outside, and then relaunchrun_foreverso that any remaining event handler can finish. It will be stopped right after because of thedone_callbackthat is still present.I have absolutely no idea if this proposed solution is anywhere near the right approach. If it is, I will add some tests to this PR.
Thank you, and sorry for the noise if this is "expected behaviour"