0

I have a python program that runs another python script in a subprocess (yes I know this architecture is not ideal but the nature of the project makes it necessary). I am trying to:

1. send a sigint to the python subprocess (simulate a keyboard kill)

2. catch the sigint in the subprocess and perform some cleanup before exit

The code follows this general pattern:

A.py

import signal
import subprocess
import time

proc = subprocess.Popen('python B.py', shell=True)
time.sleep(1)
proc.send_signal(signal.SIGINT)

B.py

try:
  while True:
    # some application logic
    pass
except:
  # some cleanup logic
  print('bye')

But when I run A.py, B.py never receives the signal or does it's cleanup logic. Any idea what is going on here?

Dan Ruswick
  • 2,990
  • 2
  • 12
  • 14
  • [duplicate](https://stackoverflow.com/questions/30732683/python-sigkill-catching-strategies)? – Stuart Mar 18 '20 at 18:16
  • no that question seems to be about force killing (signal 9). I need to send signal 2 and catch it in the child process – Dan Ruswick Mar 18 '20 at 18:28
  • 1
    `shell=True` means the chilld process is a shell and the python is the grandchild. https://bugs.python.org/issue4855 – stark Mar 18 '20 at 19:48

2 Answers2

3

Maybe try:

#!/usr/local/cpython-3.8/bin/python3

import signal
import subprocess
import time

proc = subprocess.Popen(['python3', 'B.py'], shell=False)
time.sleep(1)
proc.send_signal(signal.SIGINT)
proc.wait()

...as your A.py?

I suspect the shell is ignoring SIGINT rather than passing it down to B.py.

dstromberg
  • 6,954
  • 1
  • 26
  • 27
2

Based on the comment below, I launched a Kubuntu VM and it works if you specify bash to be used and shell=False. The reason is given in the comment, but /bin/sh is aliased to dash on Debain/Ubuntu (perhaps other distros as well). To determine what your system uses, open a terminal and type ls -l /bin/sh.

A.py

proc = subprocess.Popen(['/bin/bash', '-c', 'python3 B.py'], shell=False)

I ran the file with python3 A.py. Version: Python 3.6.7.

Prior Answer

It works for me. My thought is perhaps you need to use python3 in your string.

I'm on Arch Linux and python is actually python3.

Here is what I have.

A.py

proc = subprocess.Popen('python B.py', shell=True)
time.sleep(1)
proc.send_signal(signal.SIGINT)

B.py

import time

try:
    while True:
        time.sleep(0.25)
        print('running B')
except KeyboardInterrupt:
    print('bye')

Output:

running B
running B
running B
bye
Stuart
  • 465
  • 3
  • 6
  • 1
    This works on systems where `sh` is `bash`, because bash has a tail call optimization in which it tries to avoid forking on its final command. It will fail on e.g. Debian where `sh` is `dash`. – that other guy Mar 18 '20 at 22:58
  • I updated answer. And tested in on kubuntu, which has `/bin/sh` aliased to `dash`. [This also seems useful to know](https://unix.stackexchange.com/questions/138509/why-does-bash-c-somecommand-sometimes-not-leave-a-bash-process/138510#138510), even though I don't understand it in its entirety. – Stuart Mar 19 '20 at 18:07