Como implementar um REPL python que bem lida com saída assíncrona?
-
22-07-2019 - |
Pergunta
Eu tenho um aplicativo baseado em Python que pode aceitar alguns comandos em uma simples leitura eval-print-loop. Estou usando raw_input('> ')
para obter a entrada. Em sistemas baseados em Unix, eu também import readline
para tornar as coisas se comportam um pouco melhor. Tudo bem que isso está funcionando.
O problema é que existem eventos assíncronos chegando, e eu gostaria de imprimir a saída assim que eles acontecem. Infelizmente, isso torna as coisas olhar feio. A ">" string não aparecer novamente após a saída, e se o usuário estiver no meio de digitação algo, ele corta o seu texto no meio. Provavelmente deve redesenhar texto-in-progress do usuário depois de imprimir algo.
Isto parece que deve ser um problema resolvido. Qual é a maneira correta de fazer isso?
Observe também que alguns dos meus usuários são baseados em Windows.
TIA
Editar: A resposta aceitos trabalhos em plataformas UNIXy (quando o módulo readline estiver disponível), mas se alguém sabe como fazer este trabalho no Windows, isso seria muito apreciado
Solução
Talvez algo como isso irá fazer o truque:
#!/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()
Eu não acho que stdin é thread-safe, então você pode acabar perdendo caracteres para o segmento de interrupção (que o usuário terá que digitar novamente no final do interrupt
). I exagerada a quantidade de tempo interrupt
com a chamada time.sleep
. A chamada readline.get_line_buffer
não vai exibir os caracteres que se perdem, assim tudo acaba bem.
Note que stdout em si não é thread-safe, então se você tem múltiplos interrompendo threads de execução, isso ainda pode acabar procurando bruta.
Outras dicas
Por que você está escrevendo seu próprio REPL usando raw_input()
? Você já olhou para a classe cmd.Cmd
? Editar: Eu só descobri a biblioteca sclapp , que também pode ser útil.
Nota: a classe cmd.Cmd
(e sclapp) pode ou não pode apoiar diretamente seu objetivo original; você pode ter que subclasse-lo e modificá-lo conforme necessário para fornecer esse recurso.
executar este:
python -m twisted.conch.stdio
Você vai ter uma agradável, colorido, assíncrona REPL, sem o uso de fios . Enquanto você digita no prompt, o ciclo de eventos está em execução.
olhar para o módulo de código, que permite criar objetos para interpretar código python também (plug descarado) https: // github.com/iridium172/PyTerm permite criar programas de linha de comando interativas que lidam com a entrada do teclado crua (como ^ C vai levantar uma KeyboardInterrupt).
É uma espécie de um não-resposta, mas eu ficaria em IPython de código para ver como eles estão fazendo isso.
Eu acho que você tem 2 opções básicas:
- Sincronizar sua saída (bloco ou seja, até que ele volta)
- Separar sua entrada e sua saída (asyncronous), talvez em duas colunas separadas.