cobie
cobie

Reputation: 7271

Popen send_signal signal not received in subprocess

I am running multiple processes using subprocess.Popen and when I detect a change in any of some of a group of files, I want to send a signal to one of these process. I have defined a signal handler in the process but it doesnt seem that it is being sent any signal. some help would be appreciated. The function that does the sending of the signal and the signal handler are shown below.

def start_up():
    p, i = None, None
    while 1:
        subprocess.call(['clear'])
        logging.info('starting overlay on host %s' % socket.gethostname())
        p = subprocess.Popen([sys.executable, 'sdp_proc.py'])
        i = subprocess.Popen([sys.executable, 'kernel.py', sys.argv[1],
                                 sys.argv[2]])
        if file_modified():
            p.terminate()
            i.send_signal(signal.SIGINT)
        time.sleep(1)

The signal handler is shown below:

def signal_handler(signum, frame):
    with open('log.txt', 'w') as f:
        f.write(' so what mate, received signal with signal number %s' % signum)


signal.signal(signal.SIGINT, signal_handler)

Upvotes: 4

Views: 3699

Answers (2)

pylover
pylover

Reputation: 8055

According to the official documentation in both Unix and NT, you need to use process groups to receive signals if the shell=True is used.

So, I decided to wrap the built-in Popen function to achieve this behaviour.

import subprocess as sp
import os

def Popen(command, env=None, **kw):
    if os.name == 'nt':
        # On Windows, the specified env must include a valid SystemRoot
        # Use a current value
        if env is None:
            env = {}

        env['SystemRoot'] = os.environ['SystemRoot']
        kw['creationflags'] = sp.CREATE_NEW_PROCESS_GROUP
    else:
        kw['preexec_fn'] = os.setpgrp

    return sp.Popen(command, env=env, **kw)

Cross-platform kill function:

if os.name == 'nt':
    # On Windows, os module can't kill processes by group
    # Kill all children indiscriminately instead
    def killpg_by_pid(pid, s):
        # Ignoring s due the lack of support in windows.j
        sp.call(['taskkill', '/T', '/F', '/PID', str(pid)])

else:

    def killpg_by_pid(pid, s):
        os.killpg(os.getpgid(pid), s)

Usage:

tested in Linux and Windows.

import signal

process = Popen(
    command,
    stdout=sp.PIPE,
    stderr=sp.PIPE,
    shell=True,
    **kw,
)

# Kill
killpg_by_pid(process.pid, signal.SIGTERM)

For a complete example please take a look at: bddcli.

Upvotes: 0

the paul
the paul

Reputation: 9161

I'd guess that the SIGINT is being sent to the subprocess before it even has a chance to load up all of Python, so before it installs the SIGINT handler, meaning it will die right away.

You probably want to watch the subprocess for some successful-load condition to be met (perhaps just sending a byte on a pipe) before sending it any SIGINT signals that you expect to be handled by your own handler code.

Upvotes: 2

Related Questions