[Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?
Peter Otten
__peter__ at web.de
Sat Mar 21 09:19:42 CET 2015
Alan Gauld wrote:
> On 20/03/15 09:37, Peter Otten wrote:
>
>>> def close_relay(e=None,v=None,t=None):
>>> try:
>>> if not relay_closed()
>>> really_close_relay()
>>> except:
>>> really_close_relay()
>
> The purpose of the if clause is to ensure that
> if the function is called many times you only
> close the relay once (I surmised that more than
> once could be harmful?)
>
>>> import sys, atexit
>>> atexit.register(close_relay)
>>> sys.excepthook = close_relay
>
> atexit should be overkill, but it might be needed
> if for some reason the interpreter dies while
> performing the usual cleanup.
>
> excepthook replaces the usual exception mechanism
> with a clean up. This is needed for cases where an
> exception occurs before getting to the finally. We
> want to close the relay ASAP, not waiting till
> the interpreter decides to call the finally.
>
>>> try:
>>> main program here
>>> finally:
>>> close_relay()
>
> This is the happy path where everything shuts down as expected.
>
>> That reeks of cargo cult. Are there actual scenarios for each of the
>> three mechanisms where it is the only one that works?
>
> In real-time you never trust anything.
> Always cover your back.
>
>> I would expect that
>>
>> try:
>> main program here
>> finally:
>> close_relay()
>>
>> provides the same level of confidence,
>
> Only if the interpreter is behaving as normal.
> The hooks are to try (we hope) to catch cases where
> the interpreter has broken its normal flow.
>
> So the scenarios are:
>
> 1) an unexpected exception occurs - close the relay ASAP.
> - Use excepthook
>
> 2) The interpreter gets sent a kill or similar unexpected
> termination - use atexit because finally may not get
> called. (I'm not sure, so belt n' braces here)
> (BTW Does anyone know what the interpreter does when
> suspending - Ctrl-Z in Unix land?)
>
> 3) Normal program exit. Use the finally clause.
>
> But its only ever going to be a best endeavour, that's
> why Python is not suitable for true real-time/critical apps...
> But I'd never trust any environment to its usual behaviour
> if there is a possibility of something being broken.
> In this case the relay and its battery pack.
>
>> the program closes normally or the main code raises an exception, but not
>> if the process is killed.
>
> What's not clear in the Python documentation is how Python responds
> to a kill(or suspend). I'd hope the atexit got called even in a kill.
> I would not expect the finally to be executed.
>
> Of course, if its a seg fault you are probably stuffed either way...
I ran a few experiments:
$ cat bnb.py
import atexit
import os
import signal
import sys
import time
def handle_except(*args):
print("except", args, flush=True)
def handle_exit():
print("exit", flush=True)
def register_signalhandler(sig):
def handler(*args):
print("receiving signal", sig, args, flush=True)
signal.signal(sig, handler)
def main():
print("Hello from", os.getpid())
while True:
print(".", flush=True, end="")
time.sleep(1)
sys.excepthook = handle_except
atexit.register(handle_exit)
for sig in sys.argv[1:]:
register_signalhandler(getattr(signal, sig))
try:
main()
finally:
print("finally", flush=True)
$ python3 bnb.py
Hello from 32578
....^Cfinally
except (<class 'KeyboardInterrupt'>, KeyboardInterrupt(), <traceback object
at 0x7ff97b001bc8>)
exit
When there is no signal handler all three mechanisms work, in the order
- finally
- except hook
- exit handler
Now let's kill:
$ python3 bnb.py
Hello from 32584
.............Terminated
None of the three are invoked. Let's install a signal handler for SIGTERM:
$ python3 bnb.py SIGTERM
Hello from 32593
.................receiving signal 15 (15, <frame object at 0x7f818e0bb648>)
...............^Cfinally
except (<class 'KeyboardInterrupt'>, KeyboardInterrupt(), <traceback object
at 0x7f818cdc6bc8>)
exit
The signal is intercepted (and ignored by the no-op handler thus the
additional Ctrl-C). If we raise a SystemExit in the handler
- finally
- exit handler
will be invoked, but not the except hook.
$ kill -9
of course cannot be intercepted.
My conclusions:
- If finally does not work nothing does.
- Signal handlers increase safety
Bonus:
$ python3 bnb.py SIGTSTP
Hello from 32614
........^Zreceiving signal 20 (20, <frame object at 0x7f2f8a897648>)
........^Cfinally
except (<class 'KeyboardInterrupt'>, KeyboardInterrupt(), <traceback object
at 0x7f2f895a2bc8>)
exit
So Ctrl-Z can be intercepted. The program could put the relay into a safe
state before it suspends.
More information about the Tutor
mailing list