Непрерывная связь между родительским и дочерним подпроцессами в Python (Windows)? - PullRequest
3 голосов
/ 14 марта 2020

У меня есть этот скрипт:

import subprocess

p = subprocess.Popen(["myProgram.exe"],
                     stdin=subprocess.PIPE,
                     stdout=subprocess.PIPE)
while True:
    out, _ = p.communicate(input().encode())
    print(out.decode())

, который прекрасно работает до второго ввода, где я получаю:

ValueError: Cannot send input after starting communication

Есть ли способ, чтобы несколько сообщений отправлялись между родителем и дочерний процесс в Windows?

[EDIT]
У меня нет доступа к исходному коду myProgram.exe
Это интерактивное приложение командной строки, возвращающее результаты запросов
Выполнение >> myProgram.exe < in.txt > out.txt отлично работает с in.txt:

query1;
query2;
query3;

1 Ответ

1 голос
/ 14 марта 2020

Взаимодействие с другим запущенным процессом через stdin / stdout

Для имитации случая использования, когда скрипт Python запускает интерактивный процесс командной строки и отправляет / получает текст через stdin / stdout , у нас есть основной скрипт, который запускает другой Python процесс, выполняющий простой интерактивный l oop.

. Это также может быть применено к случаям, когда Python скрипт должен запустить другой процесс и просто прочитать его выводится без каких-либо интерактивных действий.

основной скрипт

import subprocess
import threading
import queue
import time

if __name__ == '__main__':

    def enqueue_output(outp, q):
        for line in iter(outp.readline, ''):
            q.put(line)
        outp.close()

    q = queue.Queue()

    p = subprocess.Popen(["/usr/bin/python", "/test/interact.py"],
                         stdin    = subprocess.PIPE,
                         stdout   = subprocess.PIPE,
                         # stderr   = subprocess.STDOUT,
                         bufsize  = 1,
                         encoding ='utf-8')

    th = threading.Thread(target=enqueue_output, args=(p.stdout, q))
    th.daemon = True

    th.start()

    for i in range(4):

        print("dir()", file=p.stdin)

        print(f"Iteration ({i}) Parent received: {q.get()}", end='')
        # p.stdin.write("dir()\n")
        # while q.empty():
        #     time.sleep(0)
        # print(f"Parent: {q.get_nowait()}")

взаимодействующий скрипт

if __name__ == '__main__':

    for i in range(2):

        cmd = raw_input()

        print("Iteration (%i) cmd=%s" % (i, cmd))

        result = eval(cmd)

        print("Iteration (%i) result=%s" % (i, str(result)))

output

Iteration (0) Parent received: Iteration (0) cmd=dir()
Iteration (1) Parent received: Iteration (0) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i']
Iteration (2) Parent received: Iteration (1) cmd=dir()
Iteration (3) Parent received: Iteration (1) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i', 'result']

Эти вопросы и ответы были использованы для имитации неблокирующих операций чтения из целевого процесса: { ссылка }

This метод обеспечивает способ проверки вывода без блокировки в основном потоке; q.empty() скажет вам, если нет данных. Вы также можете поиграть с блокировкой вызовов, используя q.get() или с тайм-аутом q.get(2) - параметр - количество секунд. Это может быть значение с плавающей запятой меньше нуля.

Текстовое взаимодействие между процессами может выполняться без потока и очереди, но эта реализация дает больше возможностей для получения данных, возвращающихся.

Параметры Popen(), bufsize=1 и encoding='utf-8' позволяют использовать <stdout>.readline() из основного сценария и устанавливают кодировку в ascii-совместимый код c, понимаемый обоими процессами (1 - это не размер буфер, это символьное значение c, указывающее буферизацию строки).

При такой конфигурации оба процесса могут просто использовать print() для отправки текста друг другу. Эта конфигурация должна быть совместимой со многими интерактивными текстовыми инструментами командной строки.

...