Как реализовать python REPL, который прекрасно обрабатывает асинхронный вывод?
У меня есть приложение на основе Python, которое может принимать несколько команд в простом цикле чтения-eval-print. Я использую raw_input('> ')
для ввода. В системах на базе Unix я также import readline
, чтобы все выглядело немного лучше. Все это прекрасно работает.
Проблема в том, что происходят асинхронные события, и я хотел бы печатать выходные данные, как только они произойдут. К сожалению, это заставляет вещи выглядеть уродливыми. Строка " > " не появляется снова после выхода, и если пользователь находится на полпути, набрав что-то, он прерывает их текст пополам. Вероятно, он должен перерисовать текст пользователя в процессе после печати.
Кажется, что это должна быть проблема. Каков правильный способ сделать это?
Также обратите внимание, что некоторые из моих пользователей основаны на Windows.
ТИА
Изменить: принятый ответ работает на платформах Unixy (когда доступен модуль readline), но если кто-то знает, как сделать эту работу под Windows, это будет очень полезно!
Ответы
Ответ 1
Возможно, что-то подобное сделает трюк:
#!/usr/bin/env python2.6
from __future__ import print_function
import readline
import threading
PROMPT = '> '
def interrupt():
print() # Don't want to end up on the same line the user is typing on.
print('Interrupting cow -- moo!')
print(PROMPT, readline.get_line_buffer(), sep='', end='')
def cli():
while True:
cli = str(raw_input(PROMPT))
if __name__ == '__main__':
threading.Thread(target=cli).start()
threading.Timer(2, interrupt).start()
Я не думаю, что stdin является потокобезопасным, поэтому вы можете потерять символы прерывистой нити (которую пользователь должен будет перепечатать в конце interrupt
). Я преувеличил количество interrupt
времени при вызове time.sleep
. Вызов readline.get_line_buffer
не отображает символы, которые теряются, поэтому все получается хорошо.
Обратите внимание, что сам stdout не является потокобезопасным, поэтому, если у вас есть несколько прерывистых потоков выполнения, это все равно может выглядеть грубым.
Ответ 2
Почему вы пишете свой собственный REPL с помощью raw_input()
? Вы посмотрели класс cmd.Cmd
? Изменить: Я просто нашел библиотеку sclapp, которая также может быть полезна.
Примечание: класс cmd.Cmd
(и sclapp) может или не может напрямую поддерживать вашу исходную цель; вам может потребоваться подкласс и изменить его по мере необходимости, чтобы обеспечить эту функцию.
Ответ 3
запустите это:
python -m twisted.conch.stdio
Вы получите красивый, цветной, async REPL, без использования потоков. Когда вы вводите приглашение, цикл событий запускается.
Ответ 4
просмотрите модуль кода, он также позволяет создавать объекты для интерпретации кода python (бесстыдный плагин) https://github.com/iridium172/PyTerm позволяет создавать интерактивную команду линейные программы, которые обрабатывают исходный ввод с клавиатуры (например, ^ C поднимет KeyboardInterrupt).
Ответ 5
Это не ответ, но я бы посмотрел IPython, чтобы узнать, как они это делают.
Ответ 6
Я думаю, у вас есть 2 основных варианта:
- Синхронизируйте свой вывод (т.е. блокируйте, пока он не вернется)
- Отделите свой вход и ваш (асинхронный) вывод, возможно, в двух отдельных столбцах.