¿Cómo establecer la codificación sys.stdout en Python 3?
unicode python-3.x (6)
Establecer la codificación de salida predeterminada en Python 2 es una expresión idiomática bien conocida
¡Eek! ¿Es un idioma bien conocido en Python 2? Me parece un error peligroso.
Ciertamente estropeará cualquier script que intente escribir binario a stdout (que necesitará si es un script CGI que devuelve una imagen, por ejemplo). Bytes y caracteres son animales bastante diferentes; no es una buena idea montar una interfaz que se haya especificado para aceptar bytes con una que solo tome caracteres.
CGI y HTTP en general trabajan explícitamente con bytes. Solo debe enviar bytes a sys.stdout. En Python 3 eso significa usar sys.stdout.buffer.write
para enviar bytes directamente. El contenido de la página de codificación para que coincida con su parámetro de juego de charset
debe manejarse en un nivel más alto en su aplicación (en los casos en que devuelve contenido de texto en lugar de binario). Esto también significa que print
ya no es bueno para CGI.
(Para aumentar la confusión, el CGIHandler de wsgiref se ha roto en py3k hasta hace muy poco, por lo que es imposible implementar WSGI en CGI de esa manera. Con PEP 3333 y Python 3.2 esto finalmente es factible).
Establecer la codificación de salida predeterminada en Python 2 es una expresión idiomática bien conocida:
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
Esto envuelve el objeto sys.stdout
en una sys.stdout
de códec que codifica la salida en UTF-8.
Sin embargo, esta técnica no funciona en Python 3 porque sys.stdout.write()
espera un str
, pero el resultado de la codificación es bytes
, y se produce un error cuando los codecs
intentan escribir los bytes codificados en el sys.stdout
original.
¿Cuál es la forma correcta de hacer esto en Python 3?
El uso de detach()
hace que el intérprete imprima una advertencia cuando intenta cerrar la salida estándar justo antes de que salga:
Exception ignored in: <_io.TextIOWrapper mode=''w'' encoding=''UTF-8''>
ValueError: underlying buffer has been detached
En cambio, esto funcionó bien para mí:
default_out = io.TextIOWrapper(sys.stdout.buffer, encoding=''utf-8'')
(Y, por supuesto, escribiendo en default_out
lugar de stdout).
Encontré este hilo mientras buscaba soluciones para el mismo error,
Una solución alternativa a las ya sugeridas es establecer la variable de entorno PYTHONIOENCODING
antes de que inicie Python, para mi uso: esto es menos problemático que intercambiar sys.stdout
después de inicializar Python:
PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py
Con la ventaja de no tener que ir y editar el código de Python.
Otras respuestas parecen recomendar el uso de codecs
, pero open
obras open
para mí:
import sys
sys.stdout = open(sys.stdout.fileno(), mode=''w'', encoding=''utf8'', buffering=1)
print("日本語")
# Also works with other methods of writing to stdout:
sys.stdout.write("日本語/n")
sys.stdout.buffer.write("日本語/n".encode())
Esto funciona incluso cuando lo ejecuto con PYTHONIOENCODING="ascii"
.
Python 3.1 agregó io.TextIOBase.detach()
, con una nota en la documentación para sys.stdout
:
Las transmisiones estándar están en modo de texto por defecto. Para escribir o leer datos binarios a estos, use el búfer binario subyacente. Por ejemplo, para escribir bytes en
stdout
, usesys.stdout.buffer.write(b''abc'')
. El uso de lasio.TextIOBase.detach()
se puede hacer de forma binaria por defecto. Esta función establecestdin
ystdout
en binario:
def make_streams_binary(): sys.stdin = sys.stdin.detach() sys.stdout = sys.stdout.detach()
Por lo tanto, la expresión correspondiente para Python 3.1 y posterior es:
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
sys.stdout está en modo texto en Python 3. Por lo tanto, usted escribe unicode directamente, y la expresión idiomática de Python 2 ya no es necesaria.
Donde esto fallaría en Python 2:
>>> import sys
>>> sys.stdout.write(u"ûnicöde")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: ''ascii'' codec can''t encode character u''/xfb'' in position 0: ordinal not in range(128)
Sin embargo, funciona muy bien en Python 3:
>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7
Ahora bien, si su Python no sabe cuál es realmente su codificación de stdouts, ese es un problema diferente, muy probablemente en la versión de Python.