When thinking both Exceptions and interrupts at the same time, things can get confusing so here I write down some simple experiments that I did to clear some confusing concepts.
sigkill can’t be catched
signal handling can be done with signal
package that is included in python by default. Last time I read the docs, there was SIGKILL
so I blatantly thought KILL signal can also be caught. But even in the docs, it says that it cannot catch it.
I ran this under Ubuntu environment, python3.8.10
import signal def kill_handler(signum, frame): print("kill hanlder") signal.signal(signal.SIGKILL, kill_handler) print("hello")
and the script even fails to run.
Traceback (most recent call last):
File "t2.py", line 7, in <module>
signal.signal(signal.SIGKILL, kill_handler)
File "/usr/lib/python3.8/signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
OSError: [Errno 22] Invalid argument
KeyBoardInterrupt cannot be catched by try..except Exception
I just thought Ctrl+C keyboard interrupt was just another Exception subclass, so I expected the following catch
statement to be able to catch it but I was surprised to see that it didn’t.
import signal try: print("entering loop") while True: pass except Exception as e: print("exception catched")
output:
entering loop
^CTraceback (most recent call last):
File "t2.py", line 6, in <module>
pass
KeyboardInterrupt
As you can see the KeyboardInterrupt was not catched by except Exception ...
statement.
From the python docs:
exception
KeyboardInterrupt
Raised when the user hits the interrupt key (normally Control-C or Delete). During execution, a check for interrupts is made regularly. The exception inherits fromBaseException
so as to not be accidentally caught by code that catchesException
and thus prevent the interpreter from exiting.
So I have mistaken KeyboardInterrupt
as a subclass of Exception
when it wasn’t. The proper way to handle this exception would be:
try: print("entering loop") while True: pass except KeyboardInterrupt: print("keyboardinterrupt detected") except Exception as e: print("exception catched")
output:
entering loop
^Ckeyboardinterrupt detected
SIGINT signal handler comes first than except KeyboardInterrupt
However, signal
package also allows to handle keyboard interrupts through SIGINT
. In this case which one would be prioritized? The following code puts this to the test:
import signal, sys def int_handler(signum, frame): print("sigint handler") sys.exit(0) signal.signal(signal.SIGINT, int_handler) try: print("entering loop") while True: pass except KeyboardInterrupt: print("keyboardinterrupt detected") except Exception as e: print("exception catched")
output:
entering loop
^Csigint handler
The SIGINT handler comes first.
SIGTERM handler works when terminated from outside
say we run the following script:
import signal, sys, os def term_handler(signum, frame): print("sig term handler") sys.exit(0) signal.signal(signal.SIGTERM, term_handler) print(f"pid: {os.getpid()}") print("entering loop") try: while True: pass except Exception as e: print("exception detected") except BaseException: print("base exception detected") # output: # pid: 18927 # entering loop
and from another shell, we terminate (not kill) it with cmd:
$ sudo kill 18927
then we get the following output from the python script running terminal:
pid: 18927
entering loop
sig term handler
base exception detected
we can confirm that SIGTERM is handled well as expected and is not somehow filtered by except Exception
. But surprisingly even after sigint handler, it is caught by except BaseException
statement. As it turns out, the BaseException was raised by sys.exit(0)
in the sigint handler. I guess the calling of sigint handler was still inside the context of try...except
and thus the call of sys.exit(0)
triggered except BaseException
.
SIGTERM handler is not triggered by killing the process from outside
With the same code above, I tested $ sudo kill -9 <pid>
which sends a KILL signal instead of TERMINATE signal. The result terminal output was something like this:
pid: 19738
entering loop
Killed
As expected, it failed to even call sigint handler.
0 Comments