python - microsoft - visual studio installer
Las líneas de lectura del subproceso Python() se cuelgan (4)
La tarea que intento lograr es transmitir un archivo ruby e imprimir el resultado. ( NOTA : no quiero imprimir todo de una vez)
main.py
from subprocess import Popen, PIPE, STDOUT
import pty
import os
file_path = ''/Users/luciano/Desktop/ruby_sleep.rb''
command = '' ''.join(["ruby", file_path])
master, slave = pty.openpty()
proc = Popen(command, bufsize=0, shell=True, stdout=slave, stderr=slave, close_fds=True)
stdout = os.fdopen(master, ''r'', 0)
while proc.poll() is None:
data = stdout.readline()
if data != "":
print(data)
else:
break
print("This is never reached!")
ruby_sleep.rb
puts "hello"
sleep 2
puts "goodbye!"
Problema
Transmitir el archivo funciona bien. La salida de saludo / despedida se imprime con la demora de 2 segundos. Exactamente como el script debería funcionar. El problema es que readline () se cuelga al final y nunca se cierra. Nunca alcanzo la última impresión.
Sé que hay muchas preguntas como esta aquí un stackoverflow pero ninguna de ellas me hizo resolver el problema. No estoy tan metido en todo el subproceso así que por favor dame una respuesta más práctica / concreta.
Saludos
editar
Repara el código no deseado. (nada que ver con el error real)
No estoy seguro de lo que está mal con su código, pero lo siguiente parece funcionar para mí:
#!/usr/bin/python
from subprocess import Popen, PIPE
import threading
p = Popen(''ls'', stdout=PIPE)
class ReaderThread(threading.Thread):
def __init__(self, stream):
threading.Thread.__init__(self)
self.stream = stream
def run(self):
while True:
line = self.stream.readline()
if len(line) == 0:
break
print line,
reader = ReaderThread(p.stdout)
reader.start()
# Wait until subprocess is done
p.wait()
# Wait until we''ve processed all output
reader.join()
print "Done!"
Tenga en cuenta que no tengo instalado Ruby y, por lo tanto, no puedo verificar su problema real. Funciona bien con ls
, sin embargo.
Prueba esto:
proc = Popen(command, bufsize=0, shell=True, stdout=PIPE, close_fds=True)
for line in proc.stdout:
print line
print("This is most certainly reached!")
Como otros han notado, readline()
se bloqueará al leer datos. Incluso lo hará cuando el proceso de su hijo haya muerto. No estoy seguro de por qué esto no ocurre al ejecutar ls
como en la otra respuesta, pero tal vez el intérprete de ruby detecta que está escribiendo en un PIPE y por lo tanto no se cierra automáticamente.
Básicamente, lo que estás viendo aquí es una condición de carrera entre tu proc.poll()
y tu readline()
. Como la entrada en la manejador de archivos master
nunca se cierra, si el proceso intenta hacer una línea de readline()
después de que el proceso de ruby haya terminado de salir, nunca habrá nada que leer, pero la tubería nunca se cerrará. El código solo funcionará si el proceso de shell se cierra antes de que su código pruebe otra readline ().
Aquí está la línea de tiempo:
readline()
print-output
poll()
readline()
print-output (last line of real output)
poll() (returns false since process is not done)
readline() (waits for more output)
(process is done, but output pipe still open and no poll ever happens for it).
Una solución fácil es simplemente usar el módulo de subproceso como sugiere en los documentos, no junto con openpty:
http://docs.python.org/library/subprocess.html
Aquí hay un problema muy similar para un estudio posterior:
Usar subproceso con select y pty cuelga cuando se captura la salida
Supongo que usa pty
por los motivos descritos en P: ¿Por qué no utiliza una pipa (popen ())? (todas las otras respuestas hasta el momento ignoran su "NOTA: no quiero imprimir todo de una vez" ).
pty
es Linux solo como se dice en los documentos :
Como el manejo de pseudo-terminales depende en gran medida de la plataforma, hay un código para hacerlo solo para Linux. (Se supone que el código de Linux funciona en otras plataformas, pero aún no se ha probado).
No está claro qué tan bien funciona en otros sistemas operativos.
Podrías probar pexpect
:
import sys
import pexpect
pexpect.run("ruby ruby_sleep.rb", logfile=sys.stdout)
O stdbuf
para habilitar el almacenamiento de líneas en modo no interactivo:
from subprocess import Popen, PIPE, STDOUT
proc = Popen([''stdbuf'', ''-oL'', ''ruby'', ''ruby_sleep.rb''],
bufsize=1, stdout=PIPE, stderr=STDOUT, close_fds=True)
for line in iter(proc.stdout.readline, b''''):
print line,
proc.stdout.close()
proc.wait()
O usando pty
de stdlib basado en la respuesta de @Antti Haapala :
#!/usr/bin/env python
import errno
import os
import pty
from subprocess import Popen, STDOUT
master_fd, slave_fd = pty.openpty() # provide tty to enable
# line-buffering on ruby''s side
proc = Popen([''ruby'', ''ruby_sleep.rb''],
stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, close_fds=True)
os.close(slave_fd)
try:
while 1:
try:
data = os.read(master_fd, 512)
except OSError as e:
if e.errno != errno.EIO:
raise
break # EIO means EOF on some systems
else:
if not data: # EOF
break
print(''got '' + repr(data))
finally:
os.close(master_fd)
if proc.poll() is None:
proc.kill()
proc.wait()
print("This is reached!")
Los tres ejemplos de código imprimen ''hola'' inmediatamente (tan pronto como se ve el primer EOL).
deje aquí el ejemplo del código anterior más complicado porque puede ser referenciado y discutido en otras publicaciones en SO
O usando pty
basado en la respuesta de @Antti Haapala :
import os
import pty
import select
from subprocess import Popen, STDOUT
master_fd, slave_fd = pty.openpty() # provide tty to enable
# line-buffering on ruby''s side
proc = Popen([''ruby'', ''ruby_sleep.rb''],
stdout=slave_fd, stderr=STDOUT, close_fds=True)
timeout = .04 # seconds
while 1:
ready, _, _ = select.select([master_fd], [], [], timeout)
if ready:
data = os.read(master_fd, 512)
if not data:
break
print("got " + repr(data))
elif proc.poll() is not None: # select timeout
assert not select.select([master_fd], [], [], 0)[0] # detect race condition
break # proc exited
os.close(slave_fd) # can''t do it sooner: it leads to errno.EIO error
os.close(master_fd)
proc.wait()
print("This is reached!")