Let's say I have the script below:
import signal
import sys
class Klass:
def __init__(self, name):
self.name = name
def disconnect_gracefully(*args):
print(self.name)
sys.exit()
signal.signal(signal.SIGINT, disconnect_gracefully)
a = Klass("A")
b = Klass("B")
while True:
pass
Note that both classes have a graceful exit after a SIGINT. When I run this, and crtl-c, only "B" is printed.
Generally, in a case like this, who or what should call sys.exit()
- should both instances, neither?
The Python documentation for signal only has this to say about what happens when you set a handler for the same signal multiple times:
A handler for a particular signal, once set, remains installed until it is explicitly reset
and
[when calling
signal.signal
] The previous signal handler will be returned
which seems to imply that, like POSIX signals, a process can have just a single handler for a given signal at a time.
We can add some statements to your program to show what signal.signal
returns (in CPython):
import signal
import sys
class K:
def __init__(self, name):
self.name=name
def disconnect(*args):
print(self.name)
sys.exit()
self.disconnectfuncid=id(disconnect)
self.oldsig=signal.signal(signal.SIGINT, disconnect)
>>> a=K("a")
>>> hex(a.disconnectfuncid)
'0x7fc5c1057f28'
>>> a.oldsig
<built-in function default_int_handler>
>>> signal.getsignal(signal.SIGINT)
<function K.__init__.<locals>.disconnect at 0x7fc5c1057f28>
>>> b=K("b")
>>> hex(b.disconnectfuncid)
'0x7fc5c0ed0950'
>>> b.oldsig
<function K.__init__.<locals>.disconnect at 0x7fc5c1057f28>
>>> signal.getsignal(signal.SIGINT)
<function K.__init__.<locals>.disconnect at 0x7fc5c0ed0950>
>>> while True:
... pass
...^C b
So the handler starts out as <built-in function default_int_handler>
, then gets set to the disconnect
function in the a
instance, then gets set to the disconnect
function in the b
instance, and the latter is what gets called when the signal is delivered.
If you want to exit when receiving SIGINT, and run multiple functions just before exit, one way to do that is to call the following (which will suppress printing the traceback and KeyboardInterrupt
messages):
signal.signal(signal.SIGINT, lambda *args: sys.exit())
and then register each function using atexit.register.