Ответ 1
check_output()
с тайм-аутом в основном:
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
try:
output, unused_err = process.communicate(inputdata, timeout=timeout)
except TimeoutExpired:
process.kill()
output, unused_err = process.communicate()
raise TimeoutExpired(process.args, timeout, output=output)
Есть две проблемы:
- [второй]
.communicate()
может ожидать процессы потомков, а не только для непосредственного дочернего элемента, см. подпроцесс Python.check_call vs .check_output -
process.kill()
может не убить все дерево процессов, см. Как завершить подпроцесс python, запущенный с помощью оболочки = True
Это приводит к поведению, которое вы наблюдали: TimeoutExpired
происходит через секунду, оболочка убита, но check_output()
возвращается только через 30 секунд после завершения процесса grandchild sleep
.
Чтобы обойти проблемы, уничтожьте все дерево процессов (все подпроцессы, принадлежащие к одной группе):
#!/usr/bin/env python3
import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer
start = timer()
with Popen('sleep 30', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
try:
output = process.communicate(timeout=1)[0]
except TimeoutExpired:
os.killpg(process.pid, signal.SIGINT) # send signal to the process group
output = process.communicate()[0]
print('Elapsed seconds: {:.2f}'.format(timer() - start))
Выход
Elapsed seconds: 1.00