Uso de subproceso con select y pty se bloquea al capturar resultados
Estoy tratando de escribir un programa de python que pueda interactuar con otros programas. Eso significa enviar stdin y recibir datos de stdout. No puedo usar pexpect (aunque definitivamente inspiró algo del diseño). El proceso que estoy usando ahora es este:
Adjuntar una pty a la salida estándar del subprocesoRecorrer hasta que el subproceso salga marcandosubprocess.poll
Cuando haya datos disponibles en la salida estándar, escriba esos datos inmediatamente en la salida estándar actual.¡Terminar!He estado creando un prototipo de un código (a continuación) que funciona pero parece tener un defecto que me está molestando. Una vez que el proceso hijo se ha completado, el proceso padre se bloquea si no especifico un tiempo de espera al usarselect.select
. Realmente preferiría no establecer un tiempo de espera. Simplemente parece un poco sucio. Sin embargo, todas las otras formas en las que he tratado de solucionar el problema no parecen funcionar. Pexpect parece superarlo usandoos.execv
ypty.fork
en lugar desubprocess.Popen
ypty.openpty
Una solución que no prefiero. ¿Estoy haciendo algo mal con la forma en que verifico la vida del subproceso? ¿Es mi enfoque incorrecto?
El código que estoy usando está abajo. Estoy usando esto en un Mac OS X 10.6.8, pero también necesito que funcione en Ubuntu 12.04.
Este es el subproceso del corredor.runner.py
:
import subprocess
import select
import pty
import os
import sys
def main():
master, slave = pty.openpty()
process = subprocess.Popen(['python', 'outputter.py'],
stdin=subprocess.PIPE,
stdout=slave, stderr=slave, close_fds=True)
while process.poll() is None:
# Just FYI timeout is the last argument to select.select
rlist, wlist, xlist = select.select([master], [], [])
for f in rlist:
output = os.read(f, 1000) # This is used because it doesn't block
sys.stdout.write(output)
sys.stdout.flush()
print "**ALL COMPLETED**"
if __name__ == '__main__':
main()
Este es el código de subproceso.outputter.py
. Las partes aleatorias extrañas son solo para simular un programa que genera datos a intervalos aleatorios. Puedes eliminarlo si lo deseas. No debería importar:
import time
import sys
import random
def main():
lines = ['hello', 'there', 'what', 'are', 'you', 'doing']
for line in lines:
sys.stdout.write(line + random.choice(['', '\n']))
sys.stdout.flush()
time.sleep(random.choice([1,2,3,4,5])/20.0)
sys.stdout.write("\ndone\n")
sys.stdout.flush()
if __name__ == '__main__':
main()
Gracias por cualquier ayuda que todos puedan proporcionar!
Nota extra
pty se usa porque quiero asegurar que stdout no esté en búfer.