UPD: Если вы не можете дождаться завершения дочернего процесса, то не обязательно блокировать программу для получения всего его выхлопа:
import subprocess
if __name__ == '__main__':
cmd = 'py "(time.sleep(1) or print(i) for i in itertools.count())"'
cmd = 'ping ya.ru'
cmd = 'ffmpeg'
with subprocess.Popen([cmd], stdout=subprocess.PIPE, text=True, shell=True) as p:
for line in p.stdout:
print(line.strip())
Так вы можете получать выхлоп построчно или вовсе посимвольно:
while 1:
print(p.stdout.read(1), end='')
Да, это по-прежнему блокирующий вызов, но вы можете читать строки в отдельном треде и по таймауту его закрывать, например.
UPD2:
Ну так что, автор, разобрался, или разжевывать надо решение?
import subprocess
import threading
import logging
import sys
log = logging.getLogger(__name__)
def check_process_output(process: subprocess.Popen, timeout: float, substring: str):
def fetch_output():
try:
while process.poll() is None:
line = p.stdout.readline()
log.debug('FETCH: %s', line.strip())
data.append(line)
except ValueError as e:
pass
data = []
t = threading.Thread(target=fetch_output)
t.start()
log.debug(f'Wait %f s', timeout)
t.join(timeout)
result = substring in ''.join(data)
log.debug('Output is%s contained substring %r', '' if result else ' NOT', substring)
return result
if __name__ == '__main__':
logging.basicConfig(stream=sys.stderr, level='DEBUG')
cmd = 'py "(time.sleep(1) or print(i) for i in itertools.count())"'
cmd = 'ping ya.ru -c 8'
# cmd = 'ffmpeg'
with (subprocess.Popen([cmd], stdout=subprocess.PIPE, text=True, shell=True) as p):
print('CHECK RESULT:', check_process_output(p, timeout=5, substring='ya.ru'))
Можно сделать потомка от Popen, который будет вычитывать в треде свой stdout и на каждую строчку дёргать коллбэк, а также будет валиться по таймауту, если за какое-то время ни один коллбэк не вернул True.
А можно просто добавить методы readline и read прямо в класс и с таймаутами в аргументах.