Search code examples
python-2.7subprocesssigintkeyboardinterrupt

send_signal(signal.SIGINT) not working?


I created two simple scripts:

script.py:

import time
import sys
import signal
try:
    print('i am running')
    time.sleep(10)
    print('i am done')
except KeyboardInterrupt:
    print("you don't like me??")

and test.py:

import subprocess
import signal
from threading import Thread
import time
import os

p = subprocess.Popen('python script.py', shell=True)
t = Thread(target=p.wait)
t.start()
print('sleeping')
time.sleep(2)
print('interrupt')
p.send_signal(signal.SIGINT)
#p.send_signal(signal.SIGTERM)
t.join()
print('process finished')

If I run test.py (on ubuntu) the expected result would be:

sleeping
i am running
interrupt
you don't like me??
process finished

instead the SIGINT seems to be ignored:

sleeping
i am running
interrupt
i am done
process finished

SIGTERM terminates the process as anticipated. However no KeyboardInterrupt is raised.

Even if I add the following lines to script.py

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
signal.signal(signal.SIGINT, signal_handler)

no SIGINT seems to be received.

However, when I press C+CTRL myself a SIGINT is received. But that's not an option for me since the SIGINT must be time triggered.

Does anybody have a clue why this happens?

Cheers, Thomas


Solution

  • (I've removed the use of threading in my examples because it doesn't add anything to the example other than more lines of code)

    This is to do with how signals are handled in process groups, you might find this other SO answer answer helpful.

    import subprocess
    import signal
    import time
    import os
    
    p = subprocess.Popen('python script.py', shell=True, preexec_fn=os.setsid) 
    
    print('sleeping')
    time.sleep(2)
    os.killpg(os.getpgid(p.pid), signal.SIGINT)
    print('interrupt')
    
    
    p.wait()
    print('process finished')
    

    This yields the expected result:

    andy@batman[14:58:04]:~/so$ python test.py 
    sleeping
    i am running
    interrupt
    you don't like me??
    process finished
    

    Signals are handled by process groups, so sending one from a process within the process group doesn't work like you think.


    Interestingly, if you don't use shell=True (which you shouldn't use if you can avoid it), it works just fine.

    import subprocess
    import signal
    import time
    import os
    
    p = subprocess.Popen(['python', 'script.py'])
    
    print('sleeping')
    time.sleep(2)
    p.send_signal(signal.SIGINT)
    print('interrupt')
    
    
    p.wait()
    print('process finished')
    

    So if I'm honest this answer is a little bit crap because I can show you two things which ostensibly work, but not really explain why.