Жодна з відповідей тут не відповідала всім моїм потребам.
- Немає потоків для stdout (немає черг тощо)
- Не блокуючи, як мені потрібно перевірити, чи не відбувається інше
- Використовуйте PIPE так, як мені потрібно було робити декілька речей, наприклад, потоковий вихід, записувати у файл журналу та повертати рядкову копію результату.
Невеликий фон: я використовую ThreadPoolExecutor для управління пулом ниток, кожен запускаючи підпроцес і виконуючи їх одночасність. (У Python2.7, але це також має працювати в новіших 3.x). Я не хочу використовувати нитки тільки для збору результатів, оскільки я хочу, щоб якомога більше доступних для інших речей (пул з 20 процесів використовував би 40 потоків просто для запуску; 1 для потокового процесу та 1 для stdout ... і більше, якщо ви хочете, що більш жорсткий, я думаю)
Я знімаю багато винятків, і таке тут, тому це засноване на коді, який працює у виробництві. Сподіваюся, я не зіпсував це в копії та вставці. Також відгуки дуже вітаємо!
import time
import fcntl
import subprocess
import time
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
"""A little inline function to handle the stdout business. """
# fcntl makes readline non-blocking so it raises an IOError when empty
try:
for s in iter(proc_stream.readline, ''): # replace '' with b'' for Python 3
my_buffer.append(s)
if echo_streams:
sys.stdout.write(s)
if log_file:
log_file.write(s)
except IOError:
pass
# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
handle_stdout(proc_stdout, stdout_parts)
# ...Check for other things here...
# For example, check a multiprocessor.Value('b') to proc.kill()
time.sleep(0.01)
# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)
stdout_str = "".join(stdout_parts) # Just to demo
Я впевнений, що тут додаються накладні витрати, але це не викликає занепокоєння в моєму випадку. Функціонально він робить те, що мені потрібно. Єдине, що я не вирішив, це те, що це ідеально підходить для повідомлень журналу, але я бачу, що деякі print
повідомлення з’являються пізніше і все відразу.