Leitura sem bloqueio em um subprocesso.PIPE em python

507

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 .readlinese nenhum dado estiver disponível):

p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
Mathieu Pagé
fonte
14
(Vindo do google?) Todos os PIPEs entrarão em conflito quando um dos buffer dos PIPEs for preenchido e não lido. por exemplo, stdout deadlock quando stderr é preenchido. Nunca passe um TUBO que você não pretende ler.
Nasser Al-Wohaibi
@ NasserAl-Wohaibi, isso significa que é melhor sempre criar arquivos, então?
Charlie Parker
algo que eu tenho curiosidade de entender é por isso que seu bloqueio, em primeiro lugar ... Estou perguntando porque eu vi o comentário:To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Charlie Parker
É, "por design", aguardando o recebimento de entradas.
Mathieu Pagé

Respostas:

403

fcntl, select, asyncprocNão vai ajudar neste caso.

Uma maneira confiável de ler um fluxo sem bloquear, independentemente do sistema operacional, é usar Queue.get_nowait():

import sys
from subprocess import PIPE, Popen
from threading  import Thread

try:
    from queue import Queue, Empty
except ImportError:
    from Queue import Queue, Empty  # python 2.x

ON_POSIX = 'posix' in sys.builtin_module_names

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()

# ... do other things here

# read line without blocking
try:  line = q.get_nowait() # or q.get(timeout=.1)
except Empty:
    print('no output yet')
else: # got line
    # ... do something with line
jfs
fonte
6
Sim, isso funciona para mim, eu removi muito embora. Inclui boas práticas, mas nem sempre é necessário. O Python 3.x 2.X compat e o close_fds podem ser omitidos, ainda funcionará. Mas esteja ciente do que tudo faz e não o copie cegamente, mesmo que funcione! (Na verdade, a solução mais simples é usar um fio e fazer um readline como Seb fez, qeues são apenas uma maneira fácil de obter os dados, há outros, tópicos são a resposta!)
Aki
3
Dentro do encadeamento, a chamada para out.readlinebloqueá-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)
Justin
3
@ Justin: 'out.readline' não bloqueia o thread principal, ele é executado em outro thread.
JFS
4
e se eu falhar em desligar o subprocesso, por exemplo. devido a exceções? o thread do stdout-reader não morre e o python trava, mesmo que o thread principal tenha saído, não é? como alguém poderia resolver isso? python 2.x não suporta matar os threads, o que é pior, não suporta interrompê-los. :( (obviamente um deve lidar com as exceções para garantir a subprocesso é desligado, mas apenas no caso de ele não vai, o que você pode fazer?)
n611x007
3
Eu criei alguns wrappers amigáveis desta no pacote shelljob pypi.python.org/pypi/shelljob
EDA-qa mort-ora-y
77

Eu 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, porque readline()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:

import fcntl
import os
import sys

# make stdin a non-blocking file
fd = sys.stdin.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

# user input handling thread
while mainThreadIsRunning:
      try: input = sys.stdin.readline()
      except: continue
      handleInput(input)

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 ...

Jesse
fonte
1
De acordo com os documentos, o fcntl () pode receber um descritor de arquivo ou um objeto com o método .fileno ().
Denilson Sá Maia
10
A resposta de Jesse não está correta. De acordo com Guido, o readline não funciona corretamente no modo sem bloqueio, e não antes do Python 3000. bugs.python.org/issue1175#msg56041 Se você deseja usar o fcntl para definir o arquivo no modo sem bloqueio, você precisa usar o os.read () de nível inferior e separar as linhas por conta própria. Misturar fcntl com chamadas de alto nível que executam buffer de linha está causando problemas.
anonnn
2
O uso de readline parece incorreto no Python 2. Veja a resposta de anonnn stackoverflow.com/questions/375427/…
Catalin Iacob
10
Por favor, não use loops ocupados. Use poll () com um tempo limite para aguardar os dados.
Ivo Danihelka
@Stefano como é buffer_sizedefinido?
cat
39

O Python 3.4 introduz uma nova API provisória para asynciomódulo IO assíncrono .

A abordagem é semelhante à twistedresposta baseada em @Bryan Ward - defina um protocolo e seus métodos serão chamados assim que os dados estiverem prontos:

#!/usr/bin/env python3
import asyncio
import os

class SubprocessProtocol(asyncio.SubprocessProtocol):
    def pipe_data_received(self, fd, data):
        if fd == 1: # got stdout data (bytes)
            print(data)

    def connection_lost(self, exc):
        loop.stop() # end loop.run_forever()

if os.name == 'nt':
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, 
        "myprogram.exe", "arg1", "arg2"))
    loop.run_forever()
finally:
    loop.close()

Consulte "Subprocesso" nos documentos .

Existe uma interface de alto nível asyncio.create_subprocess_exec()que retorna Processobjetos que permitem ler uma linha de forma assíncrona usando StreamReader.readline()coroutine (com sintaxe async/ awaitPython 3.5+ ):

#!/usr/bin/env python3.5
import asyncio
import locale
import sys
from asyncio.subprocess import PIPE
from contextlib import closing

async def readline_and_kill(*args):
    # start child process
    process = await asyncio.create_subprocess_exec(*args, stdout=PIPE)

    # read line (sequence of bytes ending with b'\n') asynchronously
    async for line in process.stdout:
        print("got line:", line.decode(locale.getpreferredencoding(False)))
        break
    process.kill()
    return await process.wait() # wait for the child process to exit


if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

with closing(loop):
    sys.exit(loop.run_until_complete(readline_and_kill(
        "myprogram.exe", "arg1", "arg2")))

readline_and_kill() executa as seguintes tarefas:

  • iniciar o subprocesso, redirecione seu stdout para um canal
  • ler uma linha do stdout do subprocesso de forma assíncrona
  • matar subprocesso
  • espere ele sair

Cada etapa pode ser limitada por segundos de tempo limite, se necessário.

jfs
fonte
Quando tento algo parecido com isto usando python 3.4 coroutines, só recebo saída quando o script inteiro é executado. Gostaria de ver uma linha de saída impressa, assim que o subprocesso imprimir uma linha. Aqui está o que eu tenho: pastebin.com/qPssFGep .
precisa saber é o seguinte
1
@ flutefreak7: problemas de buffer não estão relacionados à pergunta atual. Siga o link para possíveis soluções.
JFS
obrigado! Resolvi o problema para o meu script usando simplesmente print(text, flush=True)para que o texto impresso estivesse imediatamente disponível para o observador que chamava readline. 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.
precisa saber é o seguinte
É possível permitir que o subprocesso persista e execute mais operações de leitura / gravação. readline_and_kill, em seu segundo script, funciona da mesma subprocess.comunicatemaneira que encerra o processo após uma operação de leitura / gravação. Também vejo que você está usando um único canal stdout, que o subprocesso trata como não-bloqueador. Tentando usar os dois stdoute stderr acho que acabo bloqueando .
Carel
@Carel, o código na resposta funciona como pretendido, conforme descrito explicitamente na resposta. É possível implementar outro comportamento, se desejado. Ambos os tubos são igualmente anti-bloqueio, se usados, aqui está um exemplo de como ler os dois tubos simultaneamente .
JFS
19

Experimente o módulo asyncproc . Por exemplo:

import os
from asyncproc import Process
myProc = Process("myprogram.app")

while True:
    # check to see if process has ended
    poll = myProc.wait(os.WNOHANG)
    if poll != None:
        break
    # print any new output
    out = myProc.read()
    if out != "":
        print out

O módulo cuida de toda a segmentação, conforme sugerido por S.Lott.

Noé
fonte
1
Absolutamente brilhante. Muito mais fácil que o módulo de subprocesso bruto. Funciona perfeitamente para mim no Ubuntu.
Cerin
12
asyncproc não funciona em Windows e Windows não suportam os.WNOHANG :-(
Bryan Oakley
26
asyncproc é GPL, o que limita ainda mais a sua utilização :-(
Bryan Oakley
Obrigado. Uma pequena coisa: Parece que a substituição de guias com 8 espaços no asyncproc.py é o caminho a percorrer :)
benjaoming
Não parece que você pode obter o código de retorno do processo que você lançou através do módulo asyncproc; somente a saída que gerou.
grayaii
17

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 ProcessProtocolclasse e substitui o outReceived()método Torcido (dependendo do reator usado) geralmente é apenas um grande select()loop com retornos de chamada instalados para manipular dados de diferentes descritores de arquivo (geralmente soquetes de rede). Portanto, o outReceived()método é simplesmente instalar um retorno de chamada para manipular dados provenientes STDOUT. Um exemplo simples que demonstra esse comportamento é o seguinte:

from twisted.internet import protocol, reactor

class MyProcessProtocol(protocol.ProcessProtocol):

    def outReceived(self, data):
        print data

proc = MyProcessProtocol()
reactor.spawnProcess(proc, './myprogram', ['./myprogram', 'arg1', 'arg2', 'arg3'])
reactor.run()

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.

Bryan Ward
fonte
nada de bom. selectnão deve trabalhar em janelas com descritores de arquivos, de acordo com docs
n611x007
2
@ Naxa Eu não acho que select()ele está se referindo é o mesmo que você é. Estou assumindo isso porque Twistedfunciona em Windows ...
notbad.jpeg
1
"Torcido (dependendo do reator usado) geralmente é apenas um grande loop select ()" significa que existem vários reatores para escolher. O 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/...
clacke
14

Use selecione e leia (1).

import subprocess     #no new requirements
def readAllSoFar(proc, retVal=''): 
  while (select.select([proc.stdout],[],[],0)[0]!=[]):   
    retVal+=proc.stdout.read(1)
  return retVal
p = subprocess.Popen(['/bin/ls'], stdout=subprocess.PIPE)
while not p.poll():
  print (readAllSoFar(p))

Para readline () - como:

lines = ['']
while not p.poll():
  lines = readAllSoFar(p, lines[-1]).split('\n')
  for a in range(len(lines)-1):
    print a
lines = readAllSoFar(p, lines[-1]).split('\n')
for a in range(len(lines)-1):
  print a
Andy Jackson
fonte
6
nada de bom. selectnão deve trabalhar em janelas com descritores de arquivos, de acordo com docs
n611x007
AMD. Leia megabytes, ou possivelmente gigabytes, um caractere de cada vez ... essa é a pior idéia que já vi há muito tempo ... escusado será dizer que esse código não funciona, porque proc.stdout.read()não importa quão pequeno seja o argumento uma chamada bloqueada.
Wwxvw 03/07/19
OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
nmz787
8

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 () .

f = open('myprogram_output.txt','w')
p = subprocess.Popen('myprogram.exe', stdout=f)
p.wait()
f.close()


str = open('myprogram_output.txt','r').read()
monkut
fonte
parece que o encadeamento de recpie não sairia após o tempo limite e matá-lo depende de ser capaz de matar o subprocesso (por outro lado, não relacionado a esse respeito). .
N611x007
7

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:

easy_install tornado_subprocess

agora você pode fazer algo assim:

import tornado_subprocess
import tornado.ioloop

    def print_res( status, stdout, stderr ) :
    print status, stdout, stderr
    if status == 0:
        print "OK:"
        print stdout
    else:
        print "ERROR:"
        print stderr

t = tornado_subprocess.Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] )
t.start()
tornado.ioloop.IOLoop.instance().start()

você também pode usá-lo com um RequestHandler

class MyHandler(tornado.web.RequestHandler):
    def on_done(self, status, stdout, stderr):
        self.write( stdout )
        self.finish()

    @tornado.web.asynchronous
    def get(self):
        t = tornado_subprocess.Subprocess( self.on_done, timeout=30, args=[ "cat", "/etc/passwd" ] )
        t.start()
Vukasin Toroman
fonte
Obrigado pelo bom recurso! Apenas para esclarecer, por que não podemos simplesmente usar threading.Threadpara criar novos processos sem bloqueio? Usei-o na on_messageinstância do tornado websocket e ele funcionou bem.
VisioN 26/11/12
1
rosquear é principalmente desencorajado em tornado. eles são adequados para funções pequenas e de curta duração. Você pode ler sobre ele aqui: stackoverflow.com/questions/7846323/tornado-web-and-threads github.com/facebook/tornado/wiki/Threading-and-concurrency
Vukasin Toroman
@VukasinToroman você realmente me salvou aqui com isso. muito obrigado para o módulo tornado_subprocess :)
James Gentes
isso funciona no windows? (observe que select, com descritores de arquivo, não funciona )
n611x007 9/09/13
Esta lib não usa a selectchamada. Eu não tentei isso no Windows, mas você provavelmente teria problemas, pois a lib está usando o fcntlmódulo. Então, resumindo: não, isso provavelmente não funcionará no Windows.
Vukasin Toroman
6

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:

from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
    nextline = None
    buf = ''
    while True:
        #--- extract line using read(1)
        out = myprocess.stdout.read(1)
        if out == '' and myprocess.poll() != None: break
        if out != '':
            buf += out
            if out == '\n':
                nextline = buf
                buf = ''
        if not nextline: continue
        line = nextline
        nextline = None

        #--- do whatever you want with line here
        print 'Line is:', line
    myprocess.stdout.close()

myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread
p1.daemon = True
p1.start()

#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
    myprocess.kill()
    myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
    p1.join()

Por que as soluções existentes não funcionaram:

  1. As soluções que requerem linha de leitura (incluindo as baseadas na fila) sempre são bloqueadas. É difícil (impossível?) Eliminar o thread que executa o readline. Ele é eliminado apenas quando o processo que o criou é finalizado, mas não quando o processo de produção de saída é finalizado.
  2. Misturar fcntl de baixo nível com chamadas de linha de leitura de alto nível pode não funcionar corretamente, como apontou anonnn.
  3. O uso de select.poll () é legal, mas não funciona no Windows, de acordo com a documentação do python.
  4. O uso de bibliotecas de terceiros parece um exagero para esta tarefa e adiciona dependências adicionais.
Vikram Pudi
fonte
1
1. 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 ).
jfs
1
@sebastian Passei uma hora ou mais tentando criar um exemplo mínimo. No final, devo concordar que sua resposta lida com todos os casos. Eu acho que não funcionou mais cedo para mim, porque quando eu estava tentando matar o processo de produção de saída, ele já estava morto e deu um erro difícil de depurar. A hora foi bem gasta, porque, apesar de apresentar um exemplo mínimo, eu poderia encontrar uma solução mais simples.
Vikram Pudi
Você também poderia postar a solução mais simples? :) (se é diferente de Sebastian)
n611x007
@ danger89: eu acho dcmpid = myprocess.
ViFI
Na condição após read () chamando (logo após while True): fora nunca será string vazia porque você leu pelo menos corda / bytes com o tempo de 1.
sergzach
6

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.

#!/usr/bin/python
#
# Runner with stdout/stderr catcher
#
from sys import argv
from subprocess import Popen, PIPE
import os, io
from threading import Thread
import Queue
def __main__():
    if (len(argv) > 1) and (argv[-1] == "-sub-"):
        import time, sys
        print "Application runned!"
        time.sleep(2)
        print "Slept 2 second"
        time.sleep(1)
        print "Slept 1 additional second",
        time.sleep(2)
        sys.stderr.write("Stderr output after 5 seconds")
        print "Eol on stdin"
        sys.stderr.write("Eol on stderr\n")
        time.sleep(1)
        print "Wow, we have end of work!",
    else:
        os.environ["PYTHONUNBUFFERED"]="1"
        try:
            p = Popen( argv + ["-sub-"],
                       bufsize=0, # line-buffered
                       stdin=PIPE, stdout=PIPE, stderr=PIPE )
        except WindowsError, W:
            if W.winerror==193:
                p = Popen( argv + ["-sub-"],
                           shell=True, # Try to run via shell
                           bufsize=0, # line-buffered
                           stdin=PIPE, stdout=PIPE, stderr=PIPE )
            else:
                raise
        inp = Queue.Queue()
        sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
        serr = io.open(p.stderr.fileno(), 'rb', closefd=False)
        def Pump(stream, category):
            queue = Queue.Queue()
            def rdr():
                while True:
                    buf = stream.read1(8192)
                    if len(buf)>0:
                        queue.put( buf )
                    else:
                        queue.put( None )
                        return
            def clct():
                active = True
                while active:
                    r = queue.get()
                    try:
                        while True:
                            r1 = queue.get(timeout=0.005)
                            if r1 is None:
                                active = False
                                break
                            else:
                                r += r1
                    except Queue.Empty:
                        pass
                    inp.put( (category, r) )
            for tgt in [rdr, clct]:
                th = Thread(target=tgt)
                th.setDaemon(True)
                th.start()
        Pump(sout, 'stdout')
        Pump(serr, 'stderr')

        while p.poll() is None:
            # App still working
            try:
                chan,line = inp.get(timeout = 1.0)
                if chan=='stdout':
                    print "STDOUT>>", line, "<?<"
                elif chan=='stderr':
                    print " ERROR==", line, "=?="
            except Queue.Empty:
                pass
        print "Finish"

if __name__ == '__main__':
    __main__()
datacompboy
fonte
Uma das poucas respostas que permitem ler coisas que não terminam necessariamente com uma nova linha.
Totaam
5

Eu adiciono esse problema para ler alguns subprocessos.Popen stdout. Aqui está minha solução de leitura sem bloqueio:

import fcntl

def non_block_read(output):
    fd = output.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    try:
        return output.read()
    except:
        return ""

# Use example
from subprocess import *
sb = Popen("echo test && sleep 1000", shell=True, stdout=PIPE)
sb.kill()

# sb.stdout.read() # <-- This will block
non_block_read(sb.stdout)
'test\n'
Sebastien Claeys
fonte
5
O fcntl não funciona no Windows, de acordo com os documentos .
N611x007
@anatolytechtonik usar msvcrt.kbhit()vez
gato
4

Esta versão da leitura sem bloqueio não requer módulos especiais e funcionará imediatamente na maioria das distribuições Linux.

import os
import sys
import time
import fcntl
import subprocess

def async_read(fd):
    # set non-blocking flag while preserving old flags
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    # read char until EOF hit
    while True:
        try:
            ch = os.read(fd.fileno(), 1)
            # EOF
            if not ch: break                                                                                                                                                              
            sys.stdout.write(ch)
        except OSError:
            # waiting for data be available on fd
            pass

def shell(args, async=True):
    # merge stderr and stdout
    proc = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if async: async_read(proc.stdout)
    sout, serr = proc.communicate()
    return (sout, serr)

if __name__ == '__main__':
    cmd = 'ping 8.8.8.8'
    sout, serr = shell(cmd.split())
Tom Lime
fonte
3

Aqui está uma solução simples baseada em threads que:

  • funciona em Linux e Windows (sem depender de select ).
  • lê ambos stdoutestderr assincronamente.
  • não depende de pesquisa ativa com tempo de espera arbitrário (compatível com a CPU).
  • não usa asyncio(o que pode entrar em conflito com outras bibliotecas).
  • é executado até o processo filho terminar.

printer.py

import time
import sys

sys.stdout.write("Hello\n")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("World!\n")
sys.stdout.flush()
time.sleep(1)
sys.stderr.write("That's an error\n")
sys.stderr.flush()
time.sleep(2)
sys.stdout.write("Actually, I'm fine\n")
sys.stdout.flush()
time.sleep(1)

reader.py

import queue
import subprocess
import sys
import threading


def enqueue_stream(stream, queue, type):
    for line in iter(stream.readline, b''):
        queue.put(str(type) + line.decode('utf-8'))
    stream.close()


def enqueue_process(process, queue):
    process.wait()
    queue.put('x')


p = subprocess.Popen('python printer.py', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
q = queue.Queue()
to = threading.Thread(target=enqueue_stream, args=(p.stdout, q, 1))
te = threading.Thread(target=enqueue_stream, args=(p.stderr, q, 2))
tp = threading.Thread(target=enqueue_process, args=(p, q))
te.start()
to.start()
tp.start()

while True:
    line = q.get()
    if line[0] == 'x':
        break
    if line[0] == '2':  # stderr
        sys.stdout.write("\033[0;31m")  # ANSI red color
    sys.stdout.write(line[1:])
    if line[0] == '2':
        sys.stdout.write("\033[0m")  # reset ANSI code
    sys.stdout.flush()

tp.join()
to.join()
te.join()
Olivier Michel
fonte
2

Adicionando esta resposta aqui, pois fornece a capacidade de definir pipes sem bloqueio no Windows e Unix.

Todos os ctypesdetalhes são graças à resposta da @ techtonik .

Existe uma versão ligeiramente modificada para ser usada nos sistemas Unix e Windows.

  • Compatível com Python3 (apenas pequenas alterações são necessárias) .
  • Inclui a versão posix e define a exceção a ser usada para qualquer um.

Dessa forma, você pode usar a mesma função e exceção para o código Unix e Windows.

# pipe_non_blocking.py (module)
"""
Example use:

    p = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            )

    pipe_non_blocking_set(p.stdout.fileno())

    try:
        data = os.read(p.stdout.fileno(), 1)
    except PortableBlockingIOError as ex:
        if not pipe_non_blocking_is_error_blocking(ex):
            raise ex
"""


__all__ = (
    "pipe_non_blocking_set",
    "pipe_non_blocking_is_error_blocking",
    "PortableBlockingIOError",
    )

import os


if os.name == "nt":
    def pipe_non_blocking_set(fd):
        # Constant could define globally but avoid polluting the name-space
        # thanks to: /programming/34504970
        import msvcrt

        from ctypes import windll, byref, wintypes, WinError, POINTER
        from ctypes.wintypes import HANDLE, DWORD, BOOL

        LPDWORD = POINTER(DWORD)

        PIPE_NOWAIT = wintypes.DWORD(0x00000001)

        def pipe_no_wait(pipefd):
            SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
            SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
            SetNamedPipeHandleState.restype = BOOL

            h = msvcrt.get_osfhandle(pipefd)

            res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
            if res == 0:
                print(WinError())
                return False
            return True

        return pipe_no_wait(fd)

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        from ctypes import GetLastError
        ERROR_NO_DATA = 232

        return (GetLastError() == ERROR_NO_DATA)

    PortableBlockingIOError = OSError
else:
    def pipe_non_blocking_set(fd):
        import fcntl
        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        return True

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        return True

    PortableBlockingIOError = BlockingIOError

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 ...

def non_blocking_readlines(f, chunk=1024):
    """
    Iterate over lines, yielding b'' when nothings left
    or when new data is not yet available.

    stdout_iter = iter(non_blocking_readlines(process.stdout))

    line = next(stdout_iter)  # will be a line or b''.
    """
    import os

    from .pipe_non_blocking import (
            pipe_non_blocking_set,
            pipe_non_blocking_is_error_blocking,
            PortableBlockingIOError,
            )

    fd = f.fileno()
    pipe_non_blocking_set(fd)

    blocks = []

    while True:
        try:
            data = os.read(fd, chunk)
            if not data:
                # case were reading finishes with no trailing newline
                yield b''.join(blocks)
                blocks.clear()
        except PortableBlockingIOError as ex:
            if not pipe_non_blocking_is_error_blocking(ex):
                raise ex

            yield b''
            continue

        while True:
            n = data.find(b'\n')
            if n == -1:
                break

            yield b''.join(blocks) + data[:n + 1]
            data = data[n + 1:]
            blocks.clear()
        blocks.append(data)
ideasman42
fonte
(1) esse comentário indica que readline()não funciona com pipes não-bloqueadores (como o set using fcntl) 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 como multiprocessing.connection.PipeusaSetNamedPipeHandleState
jfs
Eu só testei isso no Python3. Mas vi essas informações também e espero que permaneçam válidas. Também escrevi meu próprio código para usar o local de readline, atualizei minha resposta para incluí-lo.
ideasman42
2

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.

def set_up_ping(ip, w):
# run the sub-process
# watch the resultant pipe
p = subprocess.Popen(['/bin/ping', ip], stdout=subprocess.PIPE)
# make stdout a non-blocking file
fl = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
stdout_gid = gobject.io_add_watch(p.stdout, gobject.IO_IN, w)
return stdout_gid # for shutting down

O observador é

def watch(f, *other):
print 'reading',f.read()
return True

E o programa principal configura um ping e depois chama o loop gobject mail.

def main():
set_up_ping('192.168.1.8', watch)
# discard gid as unused here
gobject.MainLoop().run()

Qualquer outro trabalho é anexado aos retornos de chamada no gobject.

Dave Kitchen
fonte
2

As coisas estão muito melhores no Python moderno.

Aqui está um programa filho simples, "hello.py":

#!/usr/bin/env python3

while True:
    i = input()
    if i == "quit":
        break
    print(f"hello {i}")

E um programa para interagir com ele:

import asyncio


async def main():
    proc = await asyncio.subprocess.create_subprocess_exec(
        "./hello.py", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE
    )
    proc.stdin.write(b"bob\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"alice\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"quit\n")
    await proc.wait()


asyncio.run(main())

Isso imprime:

b'hello bob\n'
b'hello alice\n'

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.

user240515
fonte
1

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.

S.Lott
fonte
11
Penso que esta resposta é inútil por dois motivos: (a) O módulo de seleção não funcionará em pipes no Windows (como o link fornecido indica claramente), o que anula as intenções do OP de ter uma solução portátil. (b) Threads assíncronos não permitem um diálogo síncrono entre o processo pai e o filho. E se o processo pai quiser despachar a próxima ação de acordo com a próxima linha lida pelo filho ?!
ThomasH
4
A seleção também não é útil, pois as leituras do Python serão bloqueadas mesmo após a seleção, porque não possui semântica C padrão e não retornará dados parciais.
precisa saber é o seguinte
Uma série separada para leitura da saída da criança resolveu meu problema, que era semelhante a isso. Se você precisar de interação síncrona, acho que não poderá usar esta solução (a menos que saiba qual saída esperar). Eu teria aceito esta resposta
Emiliano
1

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.

#!/usr/bin/python
from subprocess import Popen, PIPE, STDOUT
import io

def __main__():
    try:
        p = Popen( ["ping", "-n", "3", "127.0.0.1"], stdin=PIPE, stdout=PIPE, stderr=STDOUT )
    except: print("Popen failed"); quit()
    sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
    while True:
        buf = sout.read1(1024)
        if len(buf) == 0: break
        print buf,

if __name__ == '__main__':
    __main__()
mfmain
fonte
Voltará o mais rápido possível se não houver nada chegando? Caso contrário, está bloqueando.
Mathieu Pagé
@ MathieuPagé está certo. read1bloqueará se os primeiros blocos de leitura subjacentes, o que acontece quando o canal ainda está aberto, mas nenhuma entrada está disponível.
Jack O'Connor
1

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.

# -*- python -*-
import fcntl
import threading
import sys, os, errno
import subprocess

class Logger(threading.Thread):
    def __init__(self, *modules):
        threading.Thread.__init__(self)
        try:
            from select import epoll, EPOLLIN
            self.__poll = epoll()
            self.__evt = EPOLLIN
            self.__to = -1
        except:
            from select import poll, POLLIN
            print 'epoll is not available'
            self.__poll = poll()
            self.__evt = POLLIN
            self.__to = 100
        self.__fds = {}
        self.daemon = True
        self.start()

    def run(self):
        while True:
            events = self.__poll.poll(self.__to)
            for fd, ev in events:
                if (ev&self.__evt) != self.__evt:
                    continue
                try:
                    self.__fds[fd].run()
                except Exception, e:
                    print e

    def add(self, fd, log):
        assert not self.__fds.has_key(fd)
        self.__fds[fd] = log
        self.__poll.register(fd, self.__evt)

class log:
    logger = Logger()

    def __init__(self, name):
        self.__name = name
        self.__piped = False

    def fileno(self):
        if self.__piped:
            return self.write
        self.read, self.write = os.pipe()
        fl = fcntl.fcntl(self.read, fcntl.F_GETFL)
        fcntl.fcntl(self.read, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        self.fdRead = os.fdopen(self.read)
        self.logger.add(self.read, self)
        self.__piped = True
        return self.write

    def __run(self, line):
        self.chat(line, nl=False)

    def run(self):
        while True:
            try: line = self.fdRead.readline()
            except IOError, exc:
                if exc.errno == errno.EAGAIN:
                    return
                raise
            self.__run(line)

    def chat(self, line, nl=True):
        if nl: nl = '\n'
        else: nl = ''
        sys.stdout.write('[%s] %s%s' % (self.__name, line, nl))

def system(command, param=[], cwd=None, env=None, input=None, output=None):
    args = [command] + param
    p = subprocess.Popen(args, cwd=cwd, stdout=output, stderr=output, stdin=input, env=env, bufsize=0)
    p.wait()

ls = log('ls')
ls.chat('go')
system("ls", ['-l', '/'], output=ls)

date = log('date')
date.chat('go')
system("date", output=date)
Dmytro
fonte
1

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 selectchamada 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 Popene 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=1buffer de linha e depois universal_newlines=Trueprocessar como um arquivo de texto em vez de um binário que parece se tornar o padrão na configuração bufsize=1.

class workerThread(QThread):
   def __init__(self, cmd):
      QThread.__init__(self)
      self.cmd = cmd
      self.result = None           ## return code
      self.error = None            ## flag indicates an error
      self.errorstr = ""           ## info message about the error

   def __del__(self):
      self.wait()
      DEBUG("Thread removed")

   def run(self):
      cmd_list = self.cmd.split(" ")   
      try:
         cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None
                                        , universal_newlines=True
                                        , stderr=subprocess.PIPE
                                        , stdout=subprocess.PIPE)
      except OSError:
         self.error = 1
         self.errorstr = "Failed to execute " + self.cmd
         ERROR(self.errorstr)
      finally:
         VERBOSE("task started...")
      import select
      while True:
         try:
            r,w,x = select.select([cmd.stdout, cmd.stderr],[],[])
            if cmd.stderr in r:
               line = cmd.stderr.readline()
               if line != "":
                  line = line.strip()
                  self.emit(SIGNAL("update_error(QString)"), line)
            if cmd.stdout in r:
               line = cmd.stdout.readline()
               if line == "":
                  break
               line = line.strip()
               self.emit(SIGNAL("update_output(QString)"), line)
         except IOError:
            pass
      cmd.wait()
      self.result = cmd.returncode
      if self.result < 0:
         self.error = 1
         self.errorstr = "Task terminated by signal " + str(self.result)
         ERROR(self.errorstr)
         return
      if self.result:
         self.error = 1
         self.errorstr = "exit code " + str(self.result)
         ERROR(self.errorstr)
         return
      return

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 readlinefunçã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.

Brooke Wallace
fonte
Nesse caso específico, você pode definir stderr = subprocess.STDOUT no construtor Popen e obter toda a saída de cmd.stdout.readline ().
Aaron
Belo exemplo claro. Estava com problemas com o select.select (), mas isso resolveu o problema para mim.
maharvey67
0

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.

edA-qa mort-ora-y
fonte
0

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.

import io
from subprocess import PIPE, Popen

p = Popen(['myprogram.exe'], stdout=PIPE)

SLEEP_DELAY = 0.001

# Create an io.BufferedReader on the file descriptor for stdout
with io.open(p.stdout.fileno(), 'rb', closefd=False) as buffer:
  while p.poll() == None:
      time.sleep(SLEEP_DELAY)
      while '\n' in bufferedStdout.peek(bufferedStdout.buffer_size):
          line = buffer.readline()
          # do stuff with the line

  # Handle any remaining output after the process has ended
  while buffer.peek():
    line = buffer.readline()
    # do stuff with the line
brincar
fonte
você já tentou for line in iter(p.stdout.readline, ""): # do stuff with the line? É sem thread (thread único) e bloqueia quando o seu código é bloqueado.
JFS
@ jf-sebastian Sim, acabei revertendo para a sua resposta. Minha implementação ainda está bloqueada ocasionalmente. Vou editar minha resposta para avisar outras pessoas para não seguirem esse caminho.
romc
0

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

grubberr
fonte
a resposta baseada em threads que não queimar cpu (você pode especificar arbitrária timeoutcomo em sua solução) e .readline()mais do que um byte de cada vez ( bufsize=1meios linha tamponada (relevante apenas para escrita)). Que outros problemas você encontrou? Respostas somente de link não são muito úteis.
JFS
0

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

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import select
import termios
import tty
import pty
from subprocess import Popen

command = 'bash'
# command = 'docker run -it --rm centos /bin/bash'.split()

# save original tty setting then set it to raw mode
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())

# open pseudo-terminal to interact with subprocess
master_fd, slave_fd = pty.openpty()

# use os.setsid() make it run in a new process group, or bash job control will not be enabled
p = Popen(command,
          preexec_fn=os.setsid,
          stdin=slave_fd,
          stdout=slave_fd,
          stderr=slave_fd,
          universal_newlines=True)

while p.poll() is None:
    r, w, e = select.select([sys.stdin, master_fd], [], [])
    if sys.stdin in r:
        d = os.read(sys.stdin.fileno(), 10240)
        os.write(master_fd, d)
    elif master_fd in r:
        o = os.read(master_fd, 10240)
        if o:
            os.write(sys.stdout.fileno(), o)

# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
Liao
fonte
0

Essa solução usa o selectmó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 selectmódulo, isso funciona apenas no Unix.

O código é totalmente compatível com PEP8.

import select


def read_available(input_stream, max_bytes=None):
    """
    Blocks until any data is available, then all available data is then read and returned.
    This function returns an empty string when end of stream is reached.

    Args:
        input_stream: The stream to read from.
        max_bytes (int|None): The maximum number of bytes to read. This function may return fewer bytes than this.

    Returns:
        str
    """
    # Prepare local variables
    input_streams = [input_stream]
    empty_list = []
    read_buffer = ""

    # Initially block for input using 'select'
    if len(select.select(input_streams, empty_list, empty_list)[0]) > 0:

        # Poll read-readiness using 'select'
        def select_func():
            return len(select.select(input_streams, empty_list, empty_list, 0)[0]) > 0

        # Create while function based on parameters
        if max_bytes is not None:
            def while_func():
                return (len(read_buffer) < max_bytes) and select_func()
        else:
            while_func = select_func

        while True:
            # Read single byte at a time
            read_data = input_stream.read(1)
            if len(read_data) == 0:
                # End of stream
                break
            # Append byte to string buffer
            read_buffer += read_data
            # Check if more data is available
            if not while_func():
                break

    # Return read buffer
    return read_buffer
Bradley Odell
fonte
0

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:

import sys
import os
from select import select

# -------------------------------------------------------------------------    
# Set the pipe (fake stdin) to simulate a final key stroke
# which will unblock the select statement
readEnd, writeEnd = os.pipe()
readFile = os.fdopen(readEnd)
writeFile = os.fdopen(writeEnd, "w")

# -------------------------------------------------------------------------
def getKey():

    # Wait for stdin or pipe (fake stdin) to be ready
    dr,dw,de = select([sys.__stdin__, readFile], [], [])

    # If stdin is the one ready then read it and return value
    if sys.__stdin__ in dr:
        return sys.__stdin__.read(1)   # For Windows use ----> getch() from module msvcrt

    # Must finish
    else:
        return None

# -------------------------------------------------------------------------
def breakStdinRead():
    writeFile.write(' ')
    writeFile.flush()

# -------------------------------------------------------------------------
# MAIN CODE

# Get key stroke
key = getKey()

# Keyboard input
if key:
    # ... do your stuff with the key value

# Faked keystroke
else:
    # ... use of stdin finished

# -------------------------------------------------------------------------
# OTHER THREAD CODE

breakStdinRead()
gonzaedu61
fonte
NOTA: Para fazer isso funcionar no Windows, o tubo deve ser substituído por um soquete. Ainda não tentei, mas deve funcionar de acordo com a documentação.
precisa saber é o seguinte
0

Tente wexpect , que é a alternativa do pexpect para janelas .

import wexpect

p = wexpect.spawn('myprogram.exe')
p.stdout.readline('.')               // regex pattern of any character
output_str = p.after()
betontalpfa
fonte
0

Nos sistemas tipo Unix e Python 3.5+, existe os.set_blockingexatamente o que diz.

import os
import time
import subprocess

cmd = 'python3', '-c', 'import time; [(print(i), time.sleep(1)) for i in range(5)]'
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
os.set_blocking(p.stdout.fileno(), False)
start = time.time()
while True:
    # first iteration always produces empty byte string in non-blocking mode
    for i in range(2):    
        line = p.stdout.readline()
        print(i, line)
        time.sleep(0.5)
    if time.time() > start + 5:
        break
p.terminate()

Isso gera:

1 b''
2 b'0\n'
1 b''
2 b'1\n'
1 b''
2 b'2\n'
1 b''
2 b'3\n'
1 b''
2 b'4\n'

Com os.set_blockingcomentado é:

0 b'0\n'
1 b'1\n'
0 b'2\n'
1 b'3\n'
0 b'4\n'
1 b''
saaj
fonte
-2

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!

Tim Savannah
fonte
Gostaria de experimentar este módulo não - bloqueado , mas relativamente novo em alguns dos procedimentos do Linux. Exatamente como instalo essas rotinas? Estou executando o Raspbian Jessie, uma versão do Debian Linux para o Raspberry Pi. Tentei 'sudo apt-get install nonblock' e python-nonblock e ambos lançaram um erro - não encontrado. Fiz o download do arquivo zip deste site pypi.python.org/pypi/python-nonblock , mas não sei o que fazer com ele. Obrigado .... RDK
RDK