"""Bounded owned subprocess groups; cancellation never targets unrelated processes."""
import os
import signal
import subprocess


def run(args, timeout=30, *, input=None, capture_output=False, check=False, **kwargs):
    if capture_output:
        if 'stdout' in kwargs or 'stderr' in kwargs:
            raise ValueError('capture_output conflicts with stdout/stderr')
        kwargs.update(stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if input is not None:
        kwargs['stdin'] = subprocess.PIPE
    child = subprocess.Popen(args, start_new_session=True, **kwargs)
    try:
        stdout, stderr = child.communicate(input, timeout=timeout)
    except BaseException:
        # The unreaped child reserves its PID/PGID while this group is signaled.
        try:
            os.killpg(child.pid, signal.SIGTERM)
        except ProcessLookupError:
            pass
        try:
            stdout, stderr = child.communicate(timeout=2)
        except subprocess.TimeoutExpired:
            try:
                os.killpg(child.pid, signal.SIGKILL)
            except ProcessLookupError:
                pass
            child.communicate(timeout=2)
        raise
    result = subprocess.CompletedProcess(args, child.returncode, stdout, stderr)
    if check:
        result.check_returncode()
    return result
