process.py 2.79 KB
Newer Older
Will Daly committed
1 2 3
"""
Helper functions for managing processes.
"""
4
from __future__ import print_function
Will Daly committed
5 6 7 8 9
import sys
import os
import subprocess
import signal
import psutil
10
import atexit
Will Daly committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48


def kill_process(proc):
    """
    Kill the process `proc` created with `subprocess`.
    """
    p1_group = psutil.Process(proc.pid)

    child_pids = p1_group.get_children(recursive=True)

    for child_pid in child_pids:
        os.kill(child_pid.pid, signal.SIGKILL)


def run_multi_processes(cmd_list, out_log=None, err_log=None):
    """
    Run each shell command in `cmd_list` in a separate process,
    piping stdout to `out_log` (a path) and stderr to `err_log` (also a path).

    Terminates the processes on CTRL-C and ensures the processes are killed
    if an error occurs.
    """
    kwargs = {'shell': True, 'cwd': None}
    pids = []

    if out_log:
        out_log_file = open(out_log, 'w')
        kwargs['stdout'] = out_log_file

    if err_log:
        err_log_file = open(err_log, 'w')
        kwargs['stderr'] = err_log_file

    try:
        for cmd in cmd_list:
            pids.extend([subprocess.Popen(cmd, **kwargs)])

        def _signal_handler(*args):
49 50 51
            """
            What to do when process is ended
            """
Will Daly committed
52 53 54 55 56 57 58 59
            print("\nEnding...")

        signal.signal(signal.SIGINT, _signal_handler)
        print("Enter CTL-C to end")
        signal.pause()
        print("Processes ending")

    except Exception as err:
60
        print("Error running process {}".format(err), file=sys.stderr)
Will Daly committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74

    finally:
        for pid in pids:
            kill_process(pid)


def run_process(cmd, out_log=None, err_log=None):
    """
    Run the shell command `cmd` in a separate process,
    piping stdout to `out_log` (a path) and stderr to `err_log` (also a path).

    Terminates the process on CTRL-C or if an error occurs.
    """
    return run_multi_processes([cmd], out_log=out_log, err_log=err_log)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107


def run_background_process(cmd, out_log=None, err_log=None, cwd=None):
    """
    Runs a command as a background process. Sends SIGINT at exit.
    """

    kwargs = {'shell': True, 'cwd': cwd}
    if out_log:
        out_log_file = open(out_log, 'w')
        kwargs['stdout'] = out_log_file

    if err_log:
        err_log_file = open(err_log, 'w')
        kwargs['stderr'] = err_log_file

    proc = subprocess.Popen(cmd, **kwargs)

    def exit_handler():
        """
        Send SIGINT to the process's children. This is important
        for running commands under coverage, as coverage will not
        produce the correct artifacts if the child process isn't
        killed properly.
        """
        p1_group = psutil.Process(proc.pid)

        child_pids = p1_group.get_children(recursive=True)

        for child_pid in child_pids:
            os.kill(child_pid.pid, signal.SIGINT)

    atexit.register(exit_handler)