2
"""A Python implementation of the *nix utility for use on all platforms."""
3
from argparse import ArgumentParser
4
from itertools import chain
10
from utility import until_timeout
13
# Generate a list of all signals that can be used with Popen.send_signal on
15
if sys.platform == 'win32':
17
'TERM': signal.SIGTERM,
18
# CTRL_C_EVENT is also supposed to work, but experience shows
20
'CTRL_BREAK': signal.CTRL_BREAK_EVENT,
23
# Blech. No equivalent of errno.errorcode for signals.
25
(x[3:], getattr(signal, x)) for x in dir(signal) if
26
x.startswith('SIG') and x not in ('SIG_DFL', 'SIG_IGN'))
29
def parse_args(argv=None):
30
parser = ArgumentParser()
31
parser.add_argument('duration', type=float)
34
'--signal', default='TERM', choices=sorted(signals.keys()))
35
return parser.parse_known_args(argv)
38
def run_command(duration, timeout_signal, command):
39
"""Run a subprocess. If a timeout elapses, send specified signal.
41
:param duration: Timeout in seconds.
42
:param timeout_signal: Signal to send to the subprocess on timeout.
43
:param command: Subprocess to run (Popen args).
44
:return: exit status of the subprocess, 124 if the subprocess was
47
if sys.platform == 'win32':
49
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
52
proc = subprocess.Popen(command, creationflags=creationflags)
53
for remaining in chain([None], until_timeout(duration)):
55
if result is not None:
59
proc.send_signal(timeout_signal)
65
args, command = parse_args(args)
66
return run_command(args.duration, signals[args.signal], command)
69
if __name__ == '__main__':