Source code for pykern.pksubprocess

# -*- coding: utf-8 -*-
u"""Wrapper for subprocess.

:copyright: Copyright (c) 2016 RadiaSoft LLC.  All Rights Reserved.
:license: http://www.apache.org/licenses/LICENSE-2.0.html
"""
from __future__ import absolute_import, division, print_function
from pykern.pkdebug import pkdc, pkdexc, pkdp
import os
import signal
import six
import subprocess
import threading


#: Caught signals
_SIGNALS = (signal.SIGTERM, signal.SIGINT)


[docs]def check_call_with_signals(cmd, output=None, env=None, msg=None): """Run cmd, writing to output. stdin is `os.devnull`. Passes SIGTERM and SIGINT on to the child process. If `output` is a string, it will be opened in write ('w') mode. Args: cmd (list): passed to subprocess verbatim output (file or str): where to write stdout and stderr env (dict): environment to use """ assert _is_main_thread(), \ 'subprocesses which require signals need to be started in main thread' p = None prev_signal = dict([(sig, signal.getsignal(sig)) for sig in _SIGNALS]) def signal_handler(sig, frame): if p: p.send_signal(sig) ps = prev_signal[sig] if ps in (None, signal.SIG_IGN, signal.SIG_DFL): return ps(sig, frame) pid = None try: stdout = output if isinstance(output, six.string_types): stdout = open(output, 'w') stderr = subprocess.STDOUT if stdout else None for sig in _SIGNALS: signal.signal(sig, signal_handler) p = subprocess.Popen( cmd, stdin=open(os.devnull), stdout=stdout, stderr=stderr, env=env, ) pid = p.pid if msg: msg('{}: started: {}', pid, cmd) rc = p.wait() p = None if rc != 0: raise RuntimeError('error exit({})'.format(rc)) if msg: msg('{}: normal exit(0): {}', pid, cmd) except Exception as e: if msg: msg('{}: exception: {} {}', pid, cmd, pkdexc()) raise finally: for sig in _SIGNALS: signal.signal(sig, prev_signal[sig]) if not p is None: if msg: msg('{}: terminating: {}', pid, cmd) p.terminate() if stdout != output: stdout.close()
def _is_main_thread(): """Need to determine if the main thread for setting signals Returns: bool: if running in the main thread """ if hasattr(threading, 'main_thread'): # Python 3 return threading.current_thread() == threading.main_thread() # Python 2: See http://stackoverflow.com/a/23207116 return threading.current_thread().__class__ == threading._MainThread