Ошибка тайм-аута подпроцесса
Я хочу использовать тайм-аут для подпроцесса
from subprocess32 import check_output
output = check_output("sleep 30", shell=True, timeout=1)
К сожалению, хотя это вызывает ошибку тайм-аута, это происходит через 30 секунд. Кажется, что check_output не может прерывать команду оболочки.
Что я могу сделать на стороне Python, чтобы остановить это? Я подозреваю, что subprocess32 не может завершить процесс по тайм-ауту.
2 ответа
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 против.check_output process.kill()
может не уничтожить все дерево процессов, см. Как завершить подпроцесс python, запущенный с shell=True
Это приводит к поведению, которое вы наблюдали: TimeoutExpired
случается через секунду, оболочка убивается, но check_output()
возвращается только через 30 секунд после внука 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
Обновление для Python 3.6.
Это все еще происходит, но я проверил много комбинаций check_output
, communicate
а также run
методы и теперь у меня есть четкое знание о том, где находится ошибка и как ее легко избежать на Python 3.5 и Python 3.6.
Мой вывод: это происходит, когда вы смешиваете использование shell=True
и любой PIPE
на stdout
, stderr
или же stdin
параметры (используется в Popen
а также run
методы).
Быть осторожен: check_output
использования PIPE
внутри Если вы посмотрите на код внутри на Python 3.6, это в основном вызов run
с stdout=PIPE
: https://github.com/python/cpython/blob/ae011e00189d9083dd84c357718264e24fe77314/Lib/subprocess.py
Итак, для решения проблемы @innisfree на Python 3.5 или 3.6 просто сделайте это:
check_output(['sleep', '30'], timeout=1)
А для других случаев просто избегайте смешивания shell=True
а также PIPE
имея в виду, что check_output
использования PIPE
,