Передача буферизованного стандартного вывода из подпроцесса в веб-сокет - PullRequest
0 голосов
/ 04 мая 2020

Как бы вы перенаправили стандартный вывод из подпроцесса в веб-сокет, не дожидаясь символа новой строки? В настоящее время приведенный ниже код отправляет стандартный вывод только с новой строки.

Код, прикрепленный к сценарию, выполняемому подпроцессом. Не выводится ли вывод оттуда правильно?

send_data.py:

import asyncio
import websockets
import subprocess
import sys
import os

async def foo(websocket, path):
        print ("socket open")
        await websocket.send("successfully connected")

        with subprocess.Popen(['sudo','python3', '-u','inline_print.py'],stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True) as p:
                for line in p.stdout:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()
                for line in p.stderr:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()


start_server = websockets.serve(foo, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

inline_print.py:

from time import sleep
import sys

loading = 'LOADING...LOADING...LOADING...LOADING...LOADING...'
for i in range(50):
    print(loading[i], sep='', end=' ', flush=True)
    sleep(0.1)

, если end=' ' изменяется на end='\n', тогда вывод из send_data.py происходит в режиме реального времени.

js клиент:

var ws = new WebSocket('ws://localhost:8765/');

ws.onmessage = function(event) {
  console.log(event.data);
};

Я подтверждаю, что этот вопрос похож на эти:

catch-stdout-in-realtime-from-subprocess

how-do-i-get-i-get-real-time-back-from-sub-process-popen-in- python -2-5

перехват-вывод-подпроцесс-пока-он работает

пока ни одно из решений работать без символа перевода строки из подпроцесса.

1 Ответ

1 голос
/ 04 мая 2020

Если вы напишите

      for line in p.stdout:

, то вы (вроде) неявно скажете, что хотите дождаться полной строки

, вам пришлось использовать read(num_bytes), а не readline()

Ниже приведен один пример для иллюстрации:

sub.py : (пример подпроцесса)

import sys, time
for v in range(20):
    print(".", end="")
    sys.stdout.flush()
    if v % 4 == 0:
        print()
    if v % 3 != 0:
        time.sleep(0.5)

rdunbuf.py : (пример чтения stddout без буферизации)

contextlib, time, subprocess

def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            last = stream.read(80) # read up to 80 chars
            # stop when end of stream reached
            if not last:
                if proc.poll() is not None:
                    break
            else:
                yield last

# open subprocess without buffering and without universal_newlines=True
proc = subprocess.Popen(["./sub.py"], stdout=subprocess.PIPE, bufsize=0)

for l in unbuffered(proc):
    print(l)
print("end")

Обратите также внимание, что ваш код может блокироваться, если он выдает много сообщений об ошибках перед выдачей нормального вывода, так как вы сначала пытаетесь прочитать все нормальные выходные данные и только тогда данные из stderr.

Вы должны читать все данные, которые ваш подпроцесс производит, как прежде, чем любые конвейерные буферы блокируют независимо, будь то stdout или stderr. Вы можете использовать select.select() (https://docs.python.org/3.8/library/select.html#select .select ), чтобы решить, нужно ли вам читать с stdout или с stderr

...