If you stop a python script with Ctrl+C, will it execute any finally blocks, or will it literally stop the script where it is?
Well, the answer is mostly it depends. Here is what actually happens:
- Python executes code in a
try:... finally:
block - a Ctrl-C is emitted and is translated in a KeyboardInterrupt Exception
- processing is interrupted and controls passes to the finally block
So at first sight, all works as expected. But...
When a user (not you, but others...) wants to interrupt a task he generally hits multiple times Ctrl-C. The first one will branch execution in the finally
block. If another Ctrl-C occurs in the middle of the finally block because it contains slow operations like closing files, a new KeyboardInterrupt will be raised and nothing guarantees that the whole block will be executed, and you could have something like:
Traceback (most recent call last):
File "...", line ..., in ...
...
KeyboardInterrupt
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...", line ..., in ...
...
File "...", line ..., in ...
...
KeyboardInterrupt
signal
gives an example where the cleanup doesn't happen as expected. The example is based on a context manager, not a plain finally
block though. I would have expected the same behavior for a __exit__
of a context manager and a finally
block, but perhaps there is a difference? My take on the docs is that even for the first KeyboardInterrupt
the execution of finally
isn't guaranteed (but likely). Did you use a different reference for that? –
Dogear Yes, assuming a single Ctrl+C, at least under Linux it will. You can test it with the following Python 3 code:
import time
try:
print('In try.')
time.sleep(1000)
finally:
print(' <-- Note the Ctrl+C.')
for i in range(1, 6):
print(f'Finishing up part {i} of 5.')
time.sleep(.1)
Here is the output:
$ ./finally.py
In try.
^C <-- Note the Ctrl+C.
Finishing up part 1 of 5.
Finishing up part 2 of 5.
Finishing up part 3 of 5.
Finishing up part 4 of 5.
Finishing up part 5 of 5.
Traceback (most recent call last):
File "./finally.py", line 7, in <module>
time.sleep(1000)
KeyboardInterrupt
Yes, it will usually raise a KeyboardInterrupt exception, but remember that your application can be unexpectedly terminated at any time, so you shouldn't rely on that.
By default, finally
blocks will run following ctrl-c, as per other answers. However, if someone has overridden the default Python behaviour in an attempt to "avoid ugly KeyboardInterrupt tracebacks", then finally
blocks will not run.
The way I've seen this done is with signal.signal(signal.SIGINT, signal.SIG_DFL)
. That will cause the application to exit immediately without any cleanup as soon as an interrupt is detected. As far as I can tell, this signal handler doesn't even bother to raise a KeyboardInterrupt
. The standard behaviour can be restored by removing the above line or by adding signal.signal(signal.SIGINT, signal.default_int_handler)
.
© 2022 - 2024 — McMap. All rights reserved.