Estou usando o módulo subprocesso para iniciar um subprocesso e conectar ao seu fluxo de saída (stdout). Quero poder executar leituras sem bloqueio no seu stdout. Existe uma maneira de tornar o .readline sem bloqueio ou verificar se há dados no fluxo antes de eu chamar .readline
? Eu gostaria que isso fosse portátil ou, pelo menos, funcionasse no Windows e Linux.
aqui está como eu faço isso por enquanto (está bloqueando .readline
se nenhum dado estiver disponível):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
fonte
fonte
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Respostas:
fcntl
,select
,asyncproc
Não vai ajudar neste caso.Uma maneira confiável de ler um fluxo sem bloquear, independentemente do sistema operacional, é usar
Queue.get_nowait()
:fonte
out.readline
bloqueá-lo e o encadeamento principal, e eu tenho que esperar até a linha de leitura retornar antes que todo o resto continue. Existe uma maneira fácil de contornar isso? (Estou lendo várias linhas do meu processo, que é também um outro arquivo .py que está fazendo DB e coisas)shelljob
pypi.python.org/pypi/shelljobEu sempre tive um problema semelhante; Os programas Python que escrevo com frequência precisam ter a capacidade de executar algumas funcionalidades principais e, ao mesmo tempo, aceitar a entrada do usuário na linha de comando (stdin). Simplesmente colocar a funcionalidade de manipulação de entrada do usuário em outro encadeamento não resolve o problema porque
readline()
bloqueia e não tem tempo limite. Se a funcionalidade principal estiver concluída e não houver mais necessidade de esperar por mais entradas do usuário, normalmente quero que meu programa saia, mas não pode, porquereadline()
ainda está bloqueando o outro thread que está aguardando uma linha. Uma solução que encontrei para esse problema é tornar o stdin um arquivo sem bloqueio usando o módulo fcntl:Na minha opinião, isso é um pouco mais limpo do que usar os módulos de seleção ou sinal para resolver esse problema, mas, novamente, ele só funciona no UNIX ...
fonte
buffer_size
definido?O Python 3.4 introduz uma nova API provisória para
asyncio
módulo IO assíncrono .A abordagem é semelhante à
twisted
resposta baseada em @Bryan Ward - defina um protocolo e seus métodos serão chamados assim que os dados estiverem prontos:Consulte "Subprocesso" nos documentos .
Existe uma interface de alto nível
asyncio.create_subprocess_exec()
que retornaProcess
objetos que permitem ler uma linha de forma assíncrona usandoStreamReader.readline()
coroutine (com sintaxeasync
/await
Python 3.5+ ):readline_and_kill()
executa as seguintes tarefas:Cada etapa pode ser limitada por segundos de tempo limite, se necessário.
fonte
print(text, flush=True)
para que o texto impresso estivesse imediatamente disponível para o observador que chamavareadline
. Quando o testei com o executável baseado em Fortran, eu realmente quero envolver / assistir, ele não armazena em buffer sua saída e, portanto, se comporta conforme o esperado.readline_and_kill
, em seu segundo script, funciona da mesmasubprocess.comunicate
maneira que encerra o processo após uma operação de leitura / gravação. Também vejo que você está usando um único canalstdout
, que o subprocesso trata como não-bloqueador. Tentando usar os doisstdout
estderr
acho que acabo bloqueando .Experimente o módulo asyncproc . Por exemplo:
O módulo cuida de toda a segmentação, conforme sugerido por S.Lott.
fonte
Você pode fazer isso com muita facilidade no Twisted . Dependendo da sua base de código existente, isso pode não ser tão fácil de usar, mas se você estiver criando um aplicativo distorcido, coisas como essa se tornam quase triviais. Você cria uma
ProcessProtocol
classe e substitui ooutReceived()
método Torcido (dependendo do reator usado) geralmente é apenas um grandeselect()
loop com retornos de chamada instalados para manipular dados de diferentes descritores de arquivo (geralmente soquetes de rede). Portanto, ooutReceived()
método é simplesmente instalar um retorno de chamada para manipular dados provenientesSTDOUT
. Um exemplo simples que demonstra esse comportamento é o seguinte:A documentação Twisted tem algumas boas informações sobre isso.
Se você criar todo o aplicativo em torno do Twisted, a comunicação assíncrona com outros processos, locais ou remotos, será realmente elegante como esta. Por outro lado, se o seu programa não for construído sobre o Twisted, isso não será realmente útil. Espero que isso possa ser útil para outros leitores, mesmo que não seja aplicável a seu aplicativo em particular.
fonte
select
não deve trabalhar em janelas com descritores de arquivos, de acordo com docsselect()
ele está se referindo é o mesmo que você é. Estou assumindo isso porqueTwisted
funciona em Windows ...asyncio
stdlib .select()
um é o mais portátil em unixes e Unix-gostos, mas também existem dois reatores disponível para Windows: twistedmatrix.com/documents/current/core/howto/...Use selecione e leia (1).
Para readline () - como:
fonte
select
não deve trabalhar em janelas com descritores de arquivos, de acordo com docsproc.stdout.read()
não importa quão pequeno seja o argumento uma chamada bloqueada.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Uma solução é fazer outro processo para executar sua leitura do processo ou fazer um encadeamento do processo com um tempo limite.
Aqui está a versão encadeada de uma função de tempo limite:
http://code.activestate.com/recipes/473878/
No entanto, você precisa ler o stdout quando ele estiver chegando? Outra solução pode ser despejar a saída em um arquivo e aguardar o processo terminar usando p.wait () .
fonte
Disclaimer: isso funciona apenas para tornado
Você pode fazer isso definindo o fd como não bloqueante e, em seguida, use o ioloop para registrar retornos de chamada. Eu empacotei isso em um ovo chamado tornado_subprocess e você pode instalá-lo via PyPI:
agora você pode fazer algo assim:
você também pode usá-lo com um RequestHandler
fonte
threading.Thread
para criar novos processos sem bloqueio? Usei-o naon_message
instância do tornado websocket e ele funcionou bem.select
, com descritores de arquivo, não funciona )select
chamada. Eu não tentei isso no Windows, mas você provavelmente teria problemas, pois a lib está usando ofcntl
módulo. Então, resumindo: não, isso provavelmente não funcionará no Windows.As soluções existentes não funcionaram para mim (detalhes abaixo). O que finalmente funcionou foi implementar o readline usando o read (1) (com base nesta resposta ). Este último não bloqueia:
Por que as soluções existentes não funcionaram:
fonte
q.get_nowait()
da minha resposta não deve bloquear, nunca, que é o ponto de usá-lo. 2. O encadeamento que executa a linha de leitura (enqueue_output()
função ) sai no EOF, por exemplo, incluindo o caso em que o processo de produção de saída é interrompido. Se você acredita que não é assim; forneça um exemplo de código mínimo completo que mostre o contrário (talvez como uma nova pergunta ).dcmpid = myprocess
.Aqui está o meu código, usado para capturar todas as saídas do subprocesso o mais rápido possível, incluindo linhas parciais. Bombeia ao mesmo tempo e stdout e stderr na ordem quase correta.
Testado e funcionado corretamente no Python 2.7 linux e windows.
fonte
Eu adiciono esse problema para ler alguns subprocessos.Popen stdout. Aqui está minha solução de leitura sem bloqueio:
fonte
msvcrt.kbhit()
vezEsta versão da leitura sem bloqueio não requer módulos especiais e funcionará imediatamente na maioria das distribuições Linux.
fonte
Aqui está uma solução simples baseada em threads que:
select
).stdout
estderr
assincronamente.asyncio
(o que pode entrar em conflito com outras bibliotecas).printer.py
reader.py
fonte
Adicionando esta resposta aqui, pois fornece a capacidade de definir pipes sem bloqueio no Windows e Unix.
Todos os
ctypes
detalhes são graças à resposta da @ techtonik .Existe uma versão ligeiramente modificada para ser usada nos sistemas Unix e Windows.
Dessa forma, você pode usar a mesma função e exceção para o código Unix e Windows.
Para evitar a leitura de dados incompletos, acabei escrevendo meu próprio gerador de linha de leitura (que retorna a sequência de bytes para cada linha).
É um gerador para que você possa, por exemplo ...
fonte
readline()
não funciona com pipes não-bloqueadores (como o set usingfcntl
) no Python 2 - você acha que não está mais correto? (minha resposta contém o link (fcntl
) que fornece as mesmas informações, mas parece excluído agora). (2) Veja comomultiprocessing.connection.Pipe
usaSetNamedPipeHandleState
Eu tenho o problema do questionador original, mas não desejei chamar threads. Misturei a solução de Jesse com uma leitura direta () do canal e meu próprio manipulador de buffer para leituras de linha (no entanto, meu subprocesso - ping - sempre escrevia linhas completas <tamanho da página do sistema). Evito a espera ocupada lendo apenas em um relógio io registrado no gobject. Hoje em dia, eu normalmente executo código em um gobject MainLoop para evitar threads.
O observador é
E o programa principal configura um ping e depois chama o loop gobject mail.
Qualquer outro trabalho é anexado aos retornos de chamada no gobject.
fonte
As coisas estão muito melhores no Python moderno.
Aqui está um programa filho simples, "hello.py":
E um programa para interagir com ele:
Isso imprime:
Observe que o padrão real, que também é encontrado em quase todas as respostas anteriores, aqui e em perguntas relacionadas, é definir o descritor de arquivo stdout da criança para não bloquear e, em seguida, pesquisar em algum tipo de loop de seleção. Hoje em dia, é claro, esse loop é fornecido pelo asyncio.
fonte
O select módulo de ajuda a determinar onde está a próxima entrada útil.
No entanto, você quase sempre fica mais feliz com threads separados. Um faz um bloqueio e lê o stdin, outro faz onde quer que você não queira bloquear.
fonte
por que incomodar discussão e fila? ao contrário de readline (), BufferedReader.read1 () não bloqueará a espera por \ r \ n, ele retornará o mais rápido possível, se houver alguma saída.
fonte
read1
bloqueará se os primeiros blocos de leitura subjacentes, o que acontece quando o canal ainda está aberto, mas nenhuma entrada está disponível.No meu caso, eu precisava de um módulo de registro que captasse a saída dos aplicativos em segundo plano e a aumentasse (adicionando carimbos de data / hora, cores etc.).
Acabei com um thread de segundo plano que faz a E / S real. O código a seguir é apenas para plataformas POSIX. Tirei as peças não essenciais.
Se alguém vai usar esse animal por longos períodos, considere gerenciar descritores abertos. No meu caso, não foi um grande problema.
fonte
Meu problema é um pouco diferente, pois eu queria coletar stdout e stderr de um processo em execução, mas, em última análise, o mesmo, pois eu queria renderizar a saída em um widget conforme ele fosse gerado.
Eu não queria recorrer a muitas das soluções alternativas propostas usando Filas ou Threads adicionais, pois elas não seriam necessárias para executar uma tarefa tão comum como executar outro script e coletar sua saída.
Depois de ler as soluções propostas e os documentos python, resolvi meu problema com a implementação abaixo. Sim, só funciona para POSIX, pois estou usando a
select
chamada de função.Concordo que os documentos são confusos e a implementação é estranha para uma tarefa de script tão comum. Acredito que versões mais antigas do python têm diferentes padrões
Popen
e explicações diferentes, o que gerou muita confusão. Isso parece funcionar bem para o Python 2.7.12 e 3.5.2.A chave era definir o
bufsize=1
buffer de linha e depoisuniversal_newlines=True
processar como um arquivo de texto em vez de um binário que parece se tornar o padrão na configuraçãobufsize=1
.ERRO, DEBUG e VERBOSE são simplesmente macros que imprimem a saída no terminal.
Esta solução é IMHO 99,99% eficaz, pois ainda usa a
readline
função de bloqueio , por isso assumimos que o subprocesso é bom e gera linhas completas.Congratulo-me com o feedback para melhorar a solução, pois ainda sou novo no Python.
fonte
Eu criei uma biblioteca baseada na solução de JF Sebastian . Você pode usar isso.
https://github.com/cenkalti/what
fonte
Trabalhando com a resposta de JF Sebastian, e várias outras fontes, montei um gerenciador de subprocessos simples. Ele fornece a leitura de leitura sem bloqueio, além de executar vários processos em paralelo. Ele não usa nenhuma chamada específica do SO (que eu saiba) e, portanto, deve funcionar em qualquer lugar.
Está disponível no pypi, apenas
pip install shelljob
. Consulte a página do projeto para obter exemplos e documentos completos.fonte
EDIT: Esta implementação ainda bloqueia. Use a resposta do JFSebastian .
Tentei a resposta principal , mas o risco adicional e a manutenção do código do thread eram preocupantes.Olhando através do módulo io (e sendo limitado a 2.6), encontrei o BufferedReader. Esta é a minha solução sem threads e sem bloqueio.fonte
for line in iter(p.stdout.readline, ""): # do stuff with the line
? É sem thread (thread único) e bloqueia quando o seu código é bloqueado.Recentemente, me deparei com o mesmo problema: preciso ler uma linha de cada vez do fluxo (execução final no subprocesso) no modo sem bloqueio. Eu queria evitar os próximos problemas: para não queimar a CPU, não leia o fluxo por um byte como o readline fez), etc
Aqui está minha implementação https://gist.github.com/grubberr/5501e1a9760c3eab5e0a , não suporta janelas (pesquisa), não lida com EOF, mas funciona bem para mim
fonte
timeout
como em sua solução) e.readline()
lê mais do que um byte de cada vez (bufsize=1
meios linha tamponada (relevante apenas para escrita)). Que outros problemas você encontrou? Respostas somente de link não são muito úteis.Este é um exemplo para executar o comando interativo no subprocesso, e o stdout é interativo usando o pseudo terminal. Você pode consultar: https://stackoverflow.com/a/43012138/3555925
fonte
Essa solução usa o
select
módulo para "ler todos os dados disponíveis" de um fluxo de E / S. Essa função bloqueia inicialmente até que os dados estejam disponíveis, mas depois lê apenas os dados disponíveis e não bloqueia mais.Dado o fato de usar o
select
módulo, isso funciona apenas no Unix.O código é totalmente compatível com PEP8.
fonte
Também enfrentei o problema descrito por Jesse e o resolvi usando "select", como Bradley , Andy e outros, mas no modo de bloqueio, para evitar um loop ocupado. Ele usa um Pipe fictício como um stdin falso. Os blocos selecionados e aguardam o stdin ou o pipe estar pronto. Quando uma tecla é pressionada, stdin desbloqueia a seleção e o valor da chave pode ser recuperado com read (1). Quando um encadeamento diferente grava no tubo, o tubo desbloqueia a seleção e isso pode ser tomado como uma indicação de que a necessidade de stdin acabou. Aqui está um código de referência:
fonte
Tente wexpect , que é a alternativa do pexpect para janelas .
fonte
Nos sistemas tipo Unix e Python 3.5+, existe
os.set_blocking
exatamente o que diz.Isso gera:
Com
os.set_blocking
comentado é:fonte
Aqui está um módulo que suporta leituras sem bloqueio e gravações em segundo plano em python:
https://pypi.python.org/pypi/python-nonblock
Fornece uma função,
nonblock_read que lerá os dados do fluxo, se disponível, retornará uma sequência vazia (ou Nenhum se o fluxo estiver fechado do outro lado e todos os dados possíveis tiverem sido lidos)
Você também pode considerar o módulo python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
que adiciona ao módulo de subprocesso. Portanto, no objeto retornado de "subprocess.Popen" é adicionado um método adicional, runInBackground. Isso inicia um thread e retorna um objeto que será automaticamente preenchido conforme as coisas são gravadas em stdout / stderr, sem bloquear o thread principal.
Aproveitar!
fonte