python - Establecer un tamaño de búfer más pequeño para sys.stdin?
buffering (5)
Estoy ejecutando memcached con el siguiente patrón de comando bash:
memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
para tratar de rastrear inigualable obtiene conjuntos para claves de toda la plataforma.
El script memtracer está debajo y funciona como se desee, con un pequeño problema. Viendo el tamaño del archivo de registro intermedio, memtracer.py no comienza a recibir entrada hasta que memkeywatchYMD.log tenga un tamaño de aproximadamente 15-18K. ¿Hay una mejor manera de leer en stdin o quizás una forma de reducir el tamaño del buffer a menos de 1k para tiempos de respuesta más rápidos?
#!/usr/bin/python
import sys
from collections import defaultdict
if __name__ == "__main__":
keys = defaultdict(int)
GET = 1
SET = 2
CLIENT = 1
SERVER = 2
#if <
for line in sys.stdin:
key = None
components = line.strip().split(" ")
#newConn = components[0][1:3]
direction = CLIENT if components[0].startswith("<") else SERVER
#if lastConn != newConn:
# lastConn = newConn
if direction == CLIENT:
command = SET if components[1] == "set" else GET
key = components[2]
if command == SET:
keys[key] -= 1
elif direction == SERVER:
command = components[1]
if command == "sending":
key = components[3]
keys[key] += 1
if key != None:
print "%s:%s" % ( key, keys[key], )
El sys.stdin.__iter__
sigue siendo un buffer de línea, uno puede tener un iterador que se comporta principalmente de forma idéntica (se detiene en EOF, mientras que stdin.__iter__
no) utilizando la forma de 2 argumentos de iter
para hacer un iterador de sys.stdin.readline
:
import sys
for line in iter(sys.stdin.readline, ''''):
sys.stdout.write(''> '' + line.upper())
O proporcione None
como centinela (pero tenga en cuenta que debe manejar la condición EOF por su cuenta).
Esto funcionó para mí en Python 3.4.3:
import os
import sys
unbuffered_stdin = os.fdopen(sys.stdin.fileno(), ''rb'', buffering=0)
La documentación para fdopen()
dice que es solo un alias para open()
.
open()
tiene un parámetro de buffering
opcional:
el almacenamiento en búfer es un número entero opcional utilizado para establecer la política de almacenamiento en búfer. Pase 0 para desactivar el almacenamiento en búfer (solo permitido en modo binario), 1 para seleccionar almacenamiento en línea (solo utilizable en modo texto) y un número entero> 1 para indicar el tamaño en bytes de un almacenamiento en bloque de tamaño fijo.
En otras palabras:
- El stdin completamente sin búfer requiere un modo binario y pasar cero como el tamaño del búfer.
- El almacenamiento en línea requiere un modo de texto .
- Cualquier otro tamaño de búfer parece funcionar en los modos binario y de texto (según la documentación).
La única forma en que podría hacerlo con Python 2.7 fue:
tty.setcbreak(sys.stdin.fileno())
desde la entrada de consola sin bloqueo de Python . Esto deshabilita por completo el almacenamiento en búfer y también suprime el eco.
EDITAR: Con respecto a la respuesta de Alex, la primera proposición (invocando python con -u
) no es posible en mi caso (ver limitación de shebang ).
La segunda proposición (duplicar fd con buffer más pequeño: os.fdopen(sys.stdin.fileno(), ''r'', 100)
no funciona cuando uso un buffer de 0 o 1, como lo es para una entrada interactiva y Necesito que cada personaje presionado sea procesado inmediatamente.
Puede eliminar completamente el almacenamiento en búfer de stdin / stdout utilizando el indicador -u
de python:
-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
see man page for details on internal buffering relating to ''-u''
y la página man aclara:
-u Force stdin, stdout and stderr to be totally unbuffered. On
systems where it matters, also put stdin, stdout and stderr in
binary mode. Note that there is internal buffering in xread-
lines(), readlines() and file-object iterators ("for line in
sys.stdin") which is not influenced by this option. To work
around this, you will want to use "sys.stdin.readline()" inside
a "while 1:" loop.
Más allá de esto, no se admite alterar el almacenamiento en búfer de un archivo existente, pero puede crear un nuevo objeto de archivo con el mismo descriptor de archivo subyacente que uno existente, y posiblemente un búfer diferente, utilizando os.fdopen . Es decir,
import os
import sys
newin = os.fdopen(sys.stdin.fileno(), ''r'', 100)
debería asociar newin
al nombre de un objeto de archivo que lea el mismo FD como entrada estándar, pero almacenado en búfer por solo unos 100 bytes a la vez (y puede continuar con sys.stdin = newin
para usar el nuevo objeto de archivo como entrada estándar de allí en adelante). Digo "debería" porque esta área solía tener varios errores y problemas en algunas plataformas (es una funcionalidad bastante difícil proporcionar multiplataforma con total generalidad) - No estoy seguro de cuál es su estado ahora, pero yo '' Definitivamente, recomiendo pruebas exhaustivas en todas las plataformas de interés para garantizar que todo funcione sin problemas. ( -u
, eliminar el almacenamiento en búfer por completo, debería funcionar con menos problemas en todas las plataformas, si eso puede cumplir sus requisitos).
Simplemente puede usar sys.stdin.readline()
lugar de sys.stdin.__iter__()
:
import sys
while True:
line = sys.stdin.readline()
if not line: break # EOF
sys.stdout.write(''> '' + line.upper())
Esto me da lecturas de buffer de línea usando Python 2.7.4 y Python 3.3.1 en Ubuntu 13.04.