python - how - plotly library
BrokenPipeError en Python (3)
De acuerdo con la documentación de Python, esto se produce cuando:
tratando de escribir en una tubería mientras que el otro extremo se ha cerrado
Esto se debe al hecho de que la herramienta principal lee de stdout
y luego la cierra rápidamente .
Como puede ver, puede sys.stdout.flush()
simplemente agregando un sys.stdout.flush()
después de cada print()
. Tenga en cuenta que esto a veces no funciona en Python 3.
Alternativamente, puede canalizarlo a awk
esta manera para obtener el mismo resultado que head -3
:
python3 0to3.py | awk ''NR >= 4 {exit} 1''
Espero que esto haya ayudado, ¡buena suerte!
Pregunta: ¿Hay alguna manera de usar flush=True
para la función print()
sin obtener el BrokenPipeError
?
Tengo un script pipe.py
:
for i in range(4000):
print(i)
Lo llamo así desde una línea de comandos de Unix:
python3 pipe.py | head -n3000
Y vuelve:
0
1
2
Este guión también lo hace
import sys
for i in range(4000):
print(i)
sys.stdout.flush()
Sin embargo, cuando ejecuto este script y lo head -n3000
a la head -n3000
:
for i in range(4000):
print(i, flush=True)
Entonces obtengo este error:
print(i, flush=True)
BrokenPipeError: [Errno 32] Broken pipe
Exception BrokenPipeError: BrokenPipeError(32, ''Broken pipe'') in <_io.TextIOWrapper name=''<stdout>'' mode=''w'' encoding=''UTF-8''> ignored
También probé la solución a continuación, pero todavía obtengo el BrokenPipeError
:
import sys
for i in range(4000):
try:
print(i, flush=True)
except BrokenPipeError:
sys.exit()
Como puede ver en la salida que publicó, la última excepción se plantea en la fase de destrucción: es por eso que ha ignored
al final
Exception BrokenPipeError: BrokenPipeError(32, ''Broken pipe'') in <_io.TextIOWrapper name=''<stdout>'' mode=''w'' encoding=''UTF-8''> ignored
Un ejemplo simple para entender qué pasa en ese contexto es el siguiente:
>> class A():
... def __del__(self):
... raise Exception("It will be ignored!!!")
...
>>> a = A()
>>> del a
Exception Exception: Exception(''It will be ignored!!!'',) in <bound method A.__del__ of <__builtin__.A instance at 0x7ff1d5c06d88>> ignored
>>> a = A()
>>> import sys
>>> sys.stderr.close()
>>> del a
Cada excepción que se desencadena mientras se destruye el objeto generará un error estándar que explica la excepción producida e ignorada (esto es porque Python le informará que algo no se puede manejar correctamente en la fase de destrucción). De todos modos, ese tipo de excepciones no se pueden almacenar en caché y, por lo tanto, puedes eliminar las llamadas que pueden generarlo o cerrar stderr
.
Regresa a la pregunta. Esa excepción no es un problema real (como decir que se ignora), pero si no quieres imprimirla, debes anular la función a la que se puede llamar cuando se destruirá el objeto o cerrar stderr
como @SergeBallesta sugirió correctamente: en ti caso de que pueda apagar la función de write
y flush
y no se activará ninguna excepción en el contexto de destrucción
Ese es un ejemplo de cómo puedes hacerlo:
import sys
def _void_f(*args,**kwargs):
pass
for i in range(4000):
try:
print(i,flush=True)
except (BrokenPipeError, IOError):
sys.stdout.write = _void_f
sys.stdout.flush = _void_f
sys.exit()
El BrokenPipeError
es normal como dicho fantasma porque el proceso de lectura (cabeza) termina y cierra su extremo del tubo mientras el proceso de escritura (python) aún intenta escribir.
Es una condición anormal, y los scripts de Python reciben un BrokenPipeError
; más exactamente, el intérprete de Python recibe una señal SIGPIPE del sistema que capta y levanta el BrokenPipeError
para permitir que el script procese el error.
Y efectivamente puede procesar el error, porque en su último ejemplo, solo ve un mensaje que dice que la excepción fue ignorada - vale, no es cierto, pero parece estar relacionado con este problema abierto en Python: los desarrolladores de Python piensan que es importante advertir al usuario de la condición anormal.
Lo que realmente sucede es que AFAIK, el intérprete de Python siempre lo señala en stderr, incluso si captas la excepción. Pero solo tiene que cerrar stderr antes de salir para deshacerse del mensaje.
Cambié ligeramente tu script a:
- capte el error como lo hizo en su último ejemplo
- atrapa IOError (que obtengo en Python34 en Windows64) o BrokenPipeError (en Python 33 en FreeBSD 9.0) - y muestra un mensaje para ese
- mostrar un mensaje personalizado hecho en stderr (stdout está cerrado debido a la tubería rota)
- cerrar stderr antes de salir para deshacerse del mensaje
Aquí está el script que utilicé:
import sys
try:
for i in range(4000):
print(i, flush=True)
except (BrokenPipeError, IOError):
print (''BrokenPipeError caught'', file = sys.stderr)
print (''Done'', file=sys.stderr)
sys.stderr.close()
y aquí el resultado de python3.3 pipe.py | head -10
python3.3 pipe.py | head -10
:
0
1
2
3
4
5
6
7
8
9
BrokenPipeError caught
Done
Si no quieres los mensajes extraños simplemente usa:
import sys
try:
for i in range(4000):
print(i, flush=True)
except (BrokenPipeError, IOError):
pass
sys.stderr.close()