Estoy usando el módulo de subproceso para iniciar un subproceso y conectarme a su flujo de salida (stdout). Quiero poder ejecutar lecturas sin bloqueo en su stdout. ¿Hay alguna manera de hacer que .readline no bloquee o verificar si hay datos en la transmisión antes de invocar .readline
? Me gustaría que esto sea portátil o que al menos funcione en Windows y Linux.
así es como lo hago por ahora (está bloqueando .readline
si no hay datos disponibles):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
fuente
fuente
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Respuestas:
fcntl
,select
,asyncproc
No ayudará en este caso.Una forma confiable de leer una secuencia sin bloquear independientemente del sistema operativo es usar
Queue.get_nowait()
:fuente
out.readline
bloquear el hilo y el hilo principal, y tengo que esperar hasta que readline regrese antes de que todo lo demás continúe. ¿Alguna forma fácil de evitar eso? (Estoy leyendo varias líneas de mi proceso, que también es otro archivo .py que está haciendo DB y cosas)shelljob
pypi.python.org/pypi/shelljobA menudo he tenido un problema similar; Los programas de Python que escribo con frecuencia necesitan tener la capacidad de ejecutar alguna funcionalidad primaria al mismo tiempo que aceptan la entrada del usuario desde la línea de comandos (stdin). Simplemente poner la funcionalidad de manejo de entrada del usuario en otro hilo no resuelve el problema porque
readline()
bloquea y no tiene tiempo de espera. Si la funcionalidad principal está completa y ya no hay necesidad de esperar más entradas del usuario, normalmente quiero que mi programa salga, pero no puede porquereadline()
todavía está bloqueando en el otro hilo esperando una línea. Una solución que he encontrado para este problema es hacer que stdin sea un archivo sin bloqueo usando el módulo fcntl:En mi opinión, esto es un poco más limpio que usar los módulos de selección o señal para resolver este problema, pero de nuevo solo funciona en UNIX ...
fuente
buffer_size
define?Python 3.4 presenta una nueva API provisional para el
asyncio
módulo de E / S asíncrono .El enfoque es similar a la
twisted
respuesta basada en @Bryan Ward : defina un protocolo y sus métodos se invocan tan pronto como los datos estén listos:Consulte "Subproceso" en los documentos .
Hay una interfaz de alto nivel
asyncio.create_subprocess_exec()
que devuelveProcess
objetos que permiten leer una línea de forma asincrónica usando laStreamReader.readline()
rutina (conasync
/await
Python 3.5+ sintaxis ):readline_and_kill()
realiza las siguientes tareas:Cada paso podría estar limitado por segundos de tiempo de espera si es necesario.
fuente
print(text, flush=True)
para que el texto impreso estuviera inmediatamente disponible para la llamada del observadorreadline
. Cuando lo probé con el ejecutable basado en Fortran que realmente quiero envolver / mirar, no almacena su salida en el búfer, por lo que se comporta como se esperaba.readline_and_kill
, en su segundo script, funciona de manera muy similarsubprocess.comunicate
a que termina el proceso después de una operación de lectura / escritura. También veo que está utilizando una sola tuberíastdout
, que el subproceso maneja como no bloqueante. Intento usar ambosstdout
ystderr
encuentro que termino bloqueando .Pruebe el módulo asyncproc . Por ejemplo:
El módulo se encarga de todos los subprocesos según lo sugerido por S.Lott.
fuente
Puedes hacer esto muy fácilmente en Twisted . Dependiendo de su base de código existente, esto podría no ser tan fácil de usar, pero si está creando una aplicación retorcida, cosas como esta se vuelven casi triviales. Crea una
ProcessProtocol
clase y anula eloutReceived()
método. Retorcido (dependiendo del reactor utilizado) generalmente es solo un granselect()
bucle con devoluciones de llamada instaladas para manejar datos de diferentes descriptores de archivos (a menudo tomas de red). Entonces, eloutReceived()
método es simplemente instalar una devolución de llamada para manejar los datos provenientes deSTDOUT
. Un ejemplo simple que demuestra este comportamiento es el siguiente:La documentación de Twisted tiene buena información al respecto.
Si construye toda su aplicación alrededor de Twisted, hace que la comunicación asincrónica con otros procesos, locales o remotos, sea realmente elegante como esta. Por otro lado, si su programa no está construido sobre Twisted, esto realmente no será tan útil. Esperemos que esto pueda ser útil para otros lectores, incluso si no es aplicable para su aplicación en particular.
fuente
select
no debería funcionar en ventanas con descriptores de archivos, de acuerdo con los documentosselect()
que se refiera al mismo que tú. Asumo esto porqueTwisted
las obras en las ventanas ...asyncio
stdlib .select()
uno es el más portátil en Unixes y Me gusta, pero también hay dos reactores disponibles para Windows: twistedmatrix.com/documents/current/core/howto/…Use select & read (1).
Para readline () - como:
fuente
select
no debería funcionar en ventanas con descriptores de archivos, de acuerdo con los documentosproc.stdout.read()
no importa cuán pequeño sea el argumento Una llamada de bloqueo.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Una solución es hacer otro proceso para realizar su lectura del proceso, o hacer un hilo del proceso con un tiempo de espera.
Aquí está la versión roscada de una función de tiempo de espera:
http://code.activestate.com/recipes/473878/
Sin embargo, ¿necesita leer el stdout a medida que entra? Otra solución puede ser volcar la salida en un archivo y esperar a que el proceso termine de usar p.wait () .
fuente
Descargo de responsabilidad: esto solo funciona para tornado
Puede hacer esto configurando el fd para que no se bloquee y luego use ioloop para registrar devoluciones de llamada. He empaquetado esto en un huevo llamado tornado_subprocess y puedes instalarlo a través de PyPI:
ahora puedes hacer algo como esto:
también puedes usarlo con un RequestHandler
fuente
threading.Thread
para crear nuevos procesos sin bloqueo? Lo utilicé en laon_message
instancia de Websocket de Tornado, y funcionó bien.select
, con los descriptores de archivo, no )select
llamada. No lo he intentado en Windows, pero probablemente te encuentres en problemas ya que lib está usando elfcntl
módulo. En resumen: no, esto probablemente no funcionará en Windows.Las soluciones existentes no me funcionaron (detalles a continuación). Lo que finalmente funcionó fue implementar readline usando read (1) (basado en esta respuesta ). Este último no bloquea:
Por qué las soluciones existentes no funcionaron:
fuente
q.get_nowait()
de mi respuesta no debe bloquear, nunca, ese es el punto de usarlo. 2. El subproceso que ejecuta readline (enqueue_output()
función ) sale en EOF, por ejemplo, incluido el caso en que se mata el proceso de producción de salida. Si crees que no es así; proporcione un ejemplo de código mínimo completo que muestre lo contrario (tal vez como una nueva pregunta ).dcmpid = myprocess
.Aquí está mi código, utilizado para capturar cada salida del subproceso lo antes posible, incluidas las líneas parciales. Bombea al mismo tiempo y stdout y stderr en el orden casi correcto.
Probado y funcionado correctamente en Python 2.7 Linux y Windows.
fuente
Agrego este problema para leer algunos subprocesos. Abrir stdout. Aquí está mi solución de lectura sin bloqueo:
fuente
msvcrt.kbhit()
en su lugarEsta versión de lectura sin bloqueo no requiere módulos especiales y funcionará de forma inmediata en la mayoría de las distribuciones de Linux.
fuente
Aquí hay una solución simple basada en hilos que:
select
).stdout
ystderr
asincrónicamente.asyncio
(lo que puede entrar en conflicto con otras bibliotecas).impresora.py
reader.py
fuente
Agregando esta respuesta aquí, ya que proporciona la capacidad de establecer tuberías sin bloqueo en Windows y Unix.
Todos los
ctypes
detalles son gracias a la respuesta de @ techtonik .Hay una versión ligeramente modificada para ser utilizada tanto en sistemas Unix como Windows.
De esta manera, puede usar la misma función y excepción para el código Unix y Windows.
Para evitar leer datos incompletos, terminé escribiendo mi propio generador de línea de lectura (que devuelve la cadena de bytes para cada línea).
Es un generador para que pueda, por ejemplo ...
fuente
readline()
no funciona con tuberías sin bloqueo (como el uso de conjuntosfcntl
) en Python 2: ¿cree que ya no es correcto? (mi respuesta contiene el enlace (fcntl
) que proporciona la misma información pero parece eliminado ahora). (2) Vea cómo semultiprocessing.connection.Pipe
utilizaSetNamedPipeHandleState
Tengo el problema del interrogador original, pero no quería invocar hilos. Mezclé la solución de Jesse con una lectura directa () desde la tubería, y mi propio controlador de búfer para lecturas de línea (sin embargo, mi subproceso - ping - siempre escribía líneas completas <un tamaño de página del sistema). Evito la espera ocupada solo leyendo en un reloj io registrado por un objeto. En estos días, generalmente ejecuto código dentro de un objeto principal MainLoop para evitar subprocesos.
El observador es
Y el programa principal configura un ping y luego llama al bucle de correo gobject.
Cualquier otro trabajo se adjunta a devoluciones de llamada en gobject.
fuente
Las cosas están mucho mejor en Python moderno.
Aquí hay un programa secundario simple, "hello.py":
Y un programa para interactuar con él:
Eso se imprime:
Tenga en cuenta que el patrón real, que también se encuentra en casi todas las respuestas anteriores, tanto aquí como en preguntas relacionadas, es establecer el descriptor de archivo stdout del niño en no bloqueante y luego sondearlo en algún tipo de ciclo de selección. En estos días, por supuesto, ese bucle lo proporciona asyncio.
fuente
El módulo de selección le ayuda a determinar dónde está la siguiente entrada útil.
Sin embargo, casi siempre eres más feliz con hilos separados. Uno hace un bloqueo de lectura del stdin, otro lo hace donde sea que no desee que se bloquee.
fuente
¿Por qué molestar hilo y cola? a diferencia de readline (), BufferedReader.read1 () no bloqueará la espera de \ r \ n, devuelve lo antes posible si hay alguna salida entrante.
fuente
read1
se bloqueará si la primera lectura subyacente se bloquea, lo que sucede cuando la tubería aún está abierta pero no hay ninguna entrada disponible.En mi caso, necesitaba un módulo de registro que capture la salida de las aplicaciones en segundo plano y la aumente (agregando marcas de tiempo, colores, etc.).
Terminé con un hilo de fondo que hace la E / S real. El siguiente código es solo para plataformas POSIX. Me quité las partes no esenciales.
Si alguien va a usar esta bestia durante largos períodos, considere administrar descriptores abiertos. En mi caso no fue un gran problema.
fuente
Mi problema es un poco diferente, ya que quería recopilar stdout y stderr de un proceso en ejecución, pero en última instancia es el mismo ya que quería renderizar la salida en un widget a medida que se genera.
No quería recurrir a muchas de las soluciones alternativas propuestas usando Colas o Subprocesos adicionales, ya que no deberían ser necesarias para realizar una tarea tan común como ejecutar otro script y recopilar su salida.
Después de leer las soluciones propuestas y los documentos de Python, resolví mi problema con la implementación a continuación. Sí, solo funciona para POSIX ya que estoy usando la
select
llamada a la función.Estoy de acuerdo en que los documentos son confusos y la implementación es incómoda para una tarea de script tan común. Creo que las versiones anteriores de Python tienen diferentes valores predeterminados
Popen
y explicaciones diferentes, por lo que crearon mucha confusión. Esto parece funcionar bien tanto para Python 2.7.12 como para 3.5.2.La clave era establecer el
bufsize=1
almacenamiento en línea del búfer y luegouniversal_newlines=True
procesarlo como un archivo de texto en lugar de un binario que parece convertirse en el predeterminado al configurarlobufsize=1
.ERROR, DEBUG y VERBOSE son simplemente macros que imprimen la salida al terminal.
Esta solución es IMHO 99.99% efectiva ya que todavía usa la
readline
función de bloqueo , por lo que asumimos que el subproceso es bueno y genera líneas completas.Agradezco sus comentarios para mejorar la solución, ya que todavía soy nuevo en Python.
fuente
He creado una biblioteca basada en la solución de JF Sebastian . Puedes usarlo.
https://github.com/cenkalti/what
fuente
Trabajando a partir de la respuesta de JF Sebastian, y varias otras fuentes, he reunido un simple administrador de subprocesos. Proporciona la solicitud de lectura sin bloqueo, además de ejecutar varios procesos en paralelo. No utiliza ninguna llamada específica del sistema operativo (que yo sepa) y, por lo tanto, debería funcionar en cualquier lugar.
Está disponible en pypi, así que solo
pip install shelljob
. Consulte la página del proyecto para ver ejemplos y documentos completos.fuente
EDITAR: Esta implementación todavía bloquea. Utilice la respuesta de JFSebastian en su lugar.
Intenté la respuesta principal , pero el riesgo adicional y el mantenimiento del código de hilo era preocupante.Mirando a través del módulo io (y estando limitado a 2.6), encontré BufferedReader. Esta es mi solución sin hilos y sin bloqueo.fuente
for line in iter(p.stdout.readline, ""): # do stuff with the line
? No tiene subprocesos (subproceso único) y se bloquea cuando el código se bloquea.Recientemente me topé con el mismo problema que necesito leer una línea a la vez desde la transmisión (ejecución de cola en subproceso) en modo sin bloqueo. Quería evitar los siguientes problemas: no quemar la CPU, no leer la transmisión por un byte ( como lo hizo readline), etc.
Aquí está mi implementación https://gist.github.com/grubberr/5501e1a9760c3eab5e0a no es compatible con Windows (encuesta), no maneje EOF, pero funciona bien para mí
fuente
timeout
como en su solución) y.readline()
lee más de un byte a la vez (bufsize=1
mediante línea de -buffered (sólo es relevante para la escritura)). ¿Qué otros problemas has encontrado? Las respuestas de solo enlace no son muy útiles.Este es un ejemplo para ejecutar un comando interactivo en un subproceso, y el stdout es interactivo mediante el uso de pseudo terminal. Puede consultar: https://stackoverflow.com/a/43012138/3555925
fuente
Esta solución utiliza el
select
módulo para "leer los datos disponibles" de una secuencia de E / S. Esta función bloquea inicialmente hasta que los datos estén disponibles, pero luego lee solo los datos que están disponibles y no bloquea más.Dado que usa el
select
módulo, esto solo funciona en Unix.El código es totalmente compatible con PEP8.
fuente
También enfrenté el problema descrito por Jesse y lo resolví usando "select" como lo hicieron Bradley , Andy y otros, pero en modo de bloqueo para evitar un bucle ocupado. Utiliza una pipa ficticia como un stdin falso. Seleccione bloques y espere a que esté listo el stdin o la tubería. Cuando se presiona una tecla, stdin desbloquea la selección y el valor de la tecla se puede recuperar con read (1). Cuando un hilo diferente escribe en la tubería, la tubería desbloquea la selección y se puede tomar como una indicación de que la necesidad de stdin ha terminado. Aquí hay un código de referencia:
fuente
Pruebe wexpect , que es la alternativa de Windows de pexpect .
fuente
En los sistemas similares a Unix y Python 3.5+ hay
os.set_blocking
quien hace exactamente lo que dice.Esto produce:
Con
os.set_blocking
comentado es:fuente
Aquí hay un módulo que admite lecturas sin bloqueo y escrituras en segundo plano en python:
https://pypi.python.org/pypi/python-nonblock
Proporciona una función
nonblock_read que leerá los datos de la secuencia, si está disponible, de lo contrario devolverá una cadena vacía (o Ninguno si la secuencia está cerrada en el otro lado y se han leído todos los datos posibles)
También puede considerar el módulo python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
que se agrega al módulo de subproceso. Entonces, en el objeto devuelto por "subprocess.Popen" se agrega un método adicional, runInBackground. Esto inicia un hilo y devuelve un objeto que se completará automáticamente a medida que las cosas se escriben en stdout / stderr, sin bloquear su hilo principal.
¡Disfrutar!
fuente