Python Popen записывает и читает процесс io несколько раз
Вопрос касается Python
Цель моего приложения — компилировать и запускать некоторые программы. Отображается вывод программы, и пользователь также может писать на ввод программы. Вопрос звучит следующим образом:
Как общаться с Popen, чтобы он ждал записи на свой стандартный ввод и чтения своего стандартного вывода несколько раз?
В моем приложении с графическим интерфейсом у меня есть следующие элементы пользовательского интерфейса:
- Кнопка «Выполнить», которая запускает скомпилированный двоичный файл. оно использует
Popen
- Поле ввода текста, которое я хочу использовать для предоставления
к запущенному процессу - Текстовое поле только для чтения, которое отображает вывод программы. Я хотел бы обновлять его по мере продвижения программы.
Многие программы, которые только печатают данные, работают нормально. Например программа Hello World работает как положено. Но когда программа содержит чтение пользовательского ввода, у меня возникают проблемы.
Вот псевдокод целевой программы:
int x = 1 ;
println "enter an integer to echo";
read x ;
println x
Вызывает функцию C под капотом.
Если это поможет, существует версия программного кода, более специфичная для реализации. Это может или не может быть полезно для понимания вопроса. Пожалуйста, смотрите ARM-сборку внизу, если хотите.
Я компилирую исходный код в сборку ARM, а затем превращаю его в двоичный исполняемый файл. я использую
Вот как я называю это программно в Python:
import subprocess
import threading
def emulate(app, executable):
app.proc = subprocess.Popen(
["qemu-arm", "-L", "/usr/arm-linux-gnueabi/", executable],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_output, _ = woutput.proc.communicate()
app.write(stdout_output)
class App:
# self.entry = edit text entry object
# self.text = read only text field object
# self.proc = initialized with None, but assigned to Popen later
# ... something not important
def run(self, compile_command, target_dir, file_name):
# ... something not important
binary_executable = "/path/to/out"
trd = threading.Thread(target=emulate, args=(self, binary_executable))
trd.start()
def write(self, text):
# Writing to tkinter Text field
self.text.insert('end', text)
def _pass_entry_to_stdin_of_proccess(self, event):
''' Magic function, which is binded to user pressing Enter after
typing text to the text entry '''
self.proc.stdin.write(self.entry.get().encode('utf-8'))
self.entry.delete(0, END)
self.write(self.proc.stdout.read().decode('utf-8'))
Как вы могли заметить, я запускаю Popen в отдельном потоке. Это добавляет сложности, и, возможно, более разумное решение позволит избежать этого. Я могу не знать некоторых особенностей
В любом случае, идея состоит в том, чтобы позвонить
Окончательное значение stdout равно
b'enter an integer to echo\n0\n'
Получается сразу после нажатия кнопки "Выполнить". Это означает, что процесс Popen не ждет
. Вместо этого он как-то читается не дожидаясь ио. Затем этот ноль записывается в
с предыдущим сообщением. Призыв
заканчивается, и данные передаются в текст.
Резюме: Как организовать переключение между main и Popen процессами с правильным ожиданием?####
Файл сборки out.s, о котором упоминалось ранее:
.data
msg_0:
.word 5
.ascii "%.*s\0"
return
msg_1:
.word 24
.ascii "enter an integer to echo"
msg_2:
.word 1
.ascii "\0"
msg_3:
.word 3
.ascii "%d\0"
.text
.global main
main:
PUSH {lr}
SUB sp, sp, #4
LDR r0, =1
STR r0, [sp]
LDR r4, =msg_1
MOV r0, r4
BL p_print_string
BL p_print_ln
PUSH {r0}
MOV r0, sp
BL p_read_int
POP {r0}
STR r0, [sp]
MOV r4, r0
ADD r0, r4, #0
BL p_print_int
BL p_print_ln
LDR r0, =0
ADD sp, sp, #4
MOV r0, r0
POP {pc}
.ltorg
p_print_string:
PUSH {lr}
LDR r1, [r0]
ADD r2, r0, #4
LDR r0, =msg_0
ADD r0, r0, #4
BL printf
MOV r0, #0
BL fflush
POP {pc}
p_print_ln:
PUSH {lr}
LDR r0, =msg_2
ADD r0, r0, #4
BL puts
MOV r0, #0
BL fflush
POP {pc}
p_read_int:
PUSH {lr}
MOV r1, r0
LDR r0, =msg_3
ADD r0, r0, #4
BL scanf
POP {pc}
p_print_int:
PUSH {lr}
MOV r1, r0
LDR r0, =msg_3
ADD r0, r0, #4
BL printf
MOV r0, #0
BL fflush
POP {pc}