Deshabilitar el búfer de salida

¿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 manera de establecer un indicador global en sys / sys.stdout progtwigció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' 

Preferiría poner mi respuesta en ¿Cómo vaciar la salida de la impresión de Python? ¿O en la función de impresión de Python que vacía el búfer cuando se llama? , pero como fueron marcados como duplicados de este (lo que no estoy de acuerdo), lo contestaré aquí.

Como Python 3.3 print () admite el argumento de palabra clave “flush” ( consulte la documentación ):

 print('Hello World!', flush=True) 
 # 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.

Edición de terceros

No admitido en versiones recientes de Python 3

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)

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

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

Una forma sencilla 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 aparece).

Tenga en cuenta, (a) que esto responde a la pregunta solo parcialmente, ya que no redirige toda la salida. Pero supongo que print es la forma más común de crear 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://stackoverflow.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) 

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

También puede ejecutar Python con la utilidad stdbuf :

stdbuf -oL python

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) 

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

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

(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 recostackrá 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()

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 .

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 se puede cambiar mágicamente la salida estándar del sistema; ya que se suministra a su progtwig de python por el sistema operativo.

Una forma de obtener un resultado sin búfer sería utilizar 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 statement de print particular:

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

Para restablecer stdout puedes simplemente hacer:

 sys.stdout = sys.__stdout__ 

En Python 3, puedes parchear la función de impresión, para enviar siempre flush = True:

 _orig_print = print def print(*args, **kwargs): _orig_print(*args, flush=True, **kwargs)