python stdout buffered

python - Deshabilitar el búfer de salida



stdout buffered (15)

(He publicado un comentario, pero se perdió de alguna manera. Entonces, otra vez :)

  1. Como noté, CPython (al menos en Linux) se comporta de manera diferente dependiendo de a dónde vaya la salida. Si va a un tty, la salida se vacía después de cada '' /n''
    Si va a una tubería / proceso, entonces está en búfer y puede usar las soluciones basadas en flush() o la opción -u recomendada anteriormente.

  2. Ligeramente relacionado con el búfer de salida:
    Si iteras sobre las líneas en la entrada con

    for line in sys.stdin:
    ...

luego, la implementación para CPython recopilará la entrada por un tiempo y luego ejecutará el cuerpo del bucle para un grupo de líneas de entrada. Si su secuencia de comandos está a punto de escribir la salida para cada línea de entrada, esto podría parecer un búfer de salida, pero en realidad es un proceso por lotes, y por lo tanto, ninguna de las técnicas de flush() , etc. ayudará. Curiosamente, no tienes este comportamiento en pypy . Para evitar esto, puedes usar

while True: line=sys.stdin.readline()
...

¿El búfer de salida está habilitado de forma predeterminada en el intérprete de Python para sys.stdout ?

Si la respuesta es positiva, ¿cuáles son todas las formas de desactivarla?

Sugerencias hasta ahora:

  1. Utilice el interruptor de línea de comando -u
  2. Envuelva sys.stdout en un objeto que se vacía después de cada escritura
  3. Set PYTHONUNBUFFERED env var
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)

¿Hay alguna otra forma de establecer un indicador global en sys / sys.stdout programación durante la ejecución?


De Magnus Lycka responda en una lista de correo :

Puede omitir el almacenamiento en búfer para todo un proceso de Python utilizando "python -u" (o #! / Usr / bin / env python -u, etc.) o configurando la variable de entorno PYTHONUNBUFFERED.

También puede reemplazar sys.stdout con alguna otra secuencia como envoltura que se vacía después de cada llamada.

class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) import sys sys.stdout = Unbuffered(sys.stdout) print ''Hello''


Es posible anular solo el método de sys.stdout de sys.stdout con uno que llame a flush . La implementación del método sugerido está abajo.

def write_flush(args, w=stdout.write): w(args) stdout.flush()

El valor predeterminado del argumento w mantendrá la referencia del método de write original. Después de que se define write_flush , la write original puede ser anulada.

stdout.write = write_flush

El código supone que stdout se importa de esta manera from sys import stdout .


Esto se relaciona con la respuesta de Cristóvão D. Sousa, pero todavía no pude comentar.

Una forma directa de usar el argumento de palabra clave al flush de Python 3 para tener siempre una salida sin búfer es:

import functools print = functools.partial(print, flush=True)

después, la impresión siempre vaciará la salida directamente (excepto que flush=False está dado).

Tenga en cuenta que (a) esto responde la pregunta solo parcialmente, ya que no redirige toda la salida. Pero supongo que print es la forma más común de crear una salida en stdout / stderr en python, por lo que estas 2 líneas cubren la mayoría de los casos de uso.

Tenga en cuenta (b) que solo funciona en el módulo / script donde lo definió. Esto puede ser bueno cuando se escribe un módulo, ya que no se sys.stdout con sys.stdout .

Python 2 no proporciona el argumento de flush , pero puede emular una función de print tipo Python 3 como se describe aquí https://.com/a/27991478/3734258 .


Los siguientes trabajos en Python 2.6, 2.7 y 3.2:

import os import sys buf_arg = 0 if sys.version_info[0] == 3: os.environ[''PYTHONUNBUFFERED''] = ''1'' buf_arg = 1 sys.stdout = os.fdopen(sys.stdout.fileno(), ''a+'', buf_arg) sys.stderr = os.fdopen(sys.stderr.fileno(), ''a+'', buf_arg)



Puede crear un archivo sin almacenamiento y asignar este archivo a sys.stdout.

import sys myFile= open( "a.log", "w", 0 ) sys.stdout= myFile

No puedes cambiar mágicamente la salida estándar del sistema; ya que se suministra a su programa de python por el sistema operativo.


Sí lo es.

Puede deshabilitarlo en la línea de comandos con el interruptor "-u".

Alternativamente, puede llamar a .flush () en sys.stdout en cada escritura (o envolverlo con un objeto que haga esto automáticamente)


Sí, está habilitado por defecto. Puedes deshabilitarlo usando la opción -u en la línea de comandos cuando llames a python.


También puede ejecutar Python con la utilidad stdbuf :

stdbuf -oL python <script>


También puede usar fcntl para cambiar las marcas de archivo sobre la marcha.

fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL) fl |= os.O_SYNC # or os.O_DSYNC (if you don''t care the file timestamp updates) fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)


Una forma de obtener una salida sin búfer sería usar sys.stderr lugar de sys.stdout o simplemente llamar a sys.stdout.flush() para forzar explícitamente que se produzca una escritura.

Podrías redirigir fácilmente todo lo impreso haciendo:

import sys; sys.stdout = sys.stderr print "Hello World!"

O para redirigir solo para una declaración de print particular:

print >>sys.stderr, "Hello World!"

Para restablecer stdout puedes simplemente hacer:

sys.stdout = sys.__stdout__


Variante que funciona sin fallar (al menos en win32; python 2.7, ipython 0.12) y luego llamada (varias veces):

def DisOutBuffering(): if sys.stdout.name == ''<stdout>'': sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0) if sys.stderr.name == ''<stderr>'': sys.stderr = os.fdopen(sys.stderr.fileno(), ''w'', 0)


# reopen stdout file descriptor with write mode # and 0 as the buffer size (unbuffered) sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0)

Créditos: "Sebastian", en algún lugar de la lista de correo de Python.


def disable_stdout_buffering(): # Appending to gc.garbage is a way to stop an object from being # destroyed. If the old sys.stdout is ever collected, it will # close() stdout, which is not good. gc.garbage.append(sys.stdout) sys.stdout = os.fdopen(sys.stdout.fileno(), ''w'', 0) # Then this will give output in the correct order: disable_stdout_buffering() print "hello" subprocess.call(["echo", "bye"])

Sin guardar el antiguo sys.stdout, disable_stdout_buffering () no es idempotente, y varias llamadas darán como resultado un error como este:

Traceback (most recent call last): File "test/buffering.py", line 17, in <module> print "hello" IOError: [Errno 9] Bad file descriptor close failed: [Errno 9] Bad file descriptor

Otra posibilidad es:

def disable_stdout_buffering(): fileno = sys.stdout.fileno() temp_fd = os.dup(fileno) sys.stdout.close() os.dup2(temp_fd, fileno) os.close(temp_fd) sys.stdout = os.fdopen(fileno, "w", 0)

(Anexar a gc.garbage no es una buena idea porque es donde se ponen los ciclos que no se pueden ver, y es posible que desee verificarlos).