¿Cómo configurar la encoding sys.stdout en Python 3?

Establecer la encoding de salida predeterminada en Python 2 es un lenguaje conocido:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout) 

Esto envuelve el objeto sys.stdout en un escritor 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 encoding 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?

Desde Python 3.7 puede cambiar la encoding de flujos estándar con reconfigure() :

 sys.stdout.reconfigure(encoding='utf-8') 

También puede modificar cómo se manejan los errores de encoding agregando un parámetro de errors .

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 , use sys.stdout.buffer.write(b'abc') . El uso de las io.TextIOBase.detach() se puede convertir en binario de forma predeterminada. Esta función establece stdin y stdout en binario:

 def make_streams_binary(): sys.stdin = sys.stdin.detach() sys.stdout = sys.stdout.detach() 

Por lo tanto, el idioma correspondiente para Python 3.1 y versiones posteriores es:

 sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) 

Encontré este hilo mientras buscaba soluciones para el mismo error,

Una solución alternativa a las que ya se sugirió es configurar la variable de entorno PYTHONIOENCODING antes de que se inicie Python, para mi uso. Esto es menos problemático que cambiar sys.stdout después de que se sys.stdout Python:

 PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py 

Con la ventaja de no tener que ir y editar el código Python.

Otras respuestas parecen recomendar el uso de codecs , pero open Works 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" .

Establecer la encoding de salida predeterminada en Python 2 es un lenguaje conocido

¡Eek! ¿Es ese un idioma conocido en Python 2? Me parece un error peligroso.

Sin duda, estropeará cualquier secuencia de comandos que intente escribir binario en la salida estándar (que necesitará si es que una secuencia de comandos CGI devuelve una imagen, por ejemplo). Los bytes y caracteres son animales muy diferentes; no es una buena idea parchear con mono una interfaz que está especificada para aceptar bytes con una que solo lleva caracteres.

CGI y HTTP en general trabajan explícitamente con bytes. Solo deberías estar enviando bytes a sys.stdout. En Python 3, eso significa usar sys.stdout.buffer.write para enviar bytes directamente. La encoding del contenido de la página para que coincida con su parámetro charset debe manejarse en un nivel más alto en su aplicación (en los casos en los que está devolviendo contenido textual, en lugar de binario). Esto también significa que la print ya no sirve para CGI.

(Para agregar a la confusión, CGIHandler de wsgiref se ha roto en py3k hasta hace muy poco, lo que hace imposible implementar WSGI en CGI de esa manera. Con PEP 3333 y Python 3.2, esto finalmente es viable).

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 su lugar, esto funcionó bien para mí:

 default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') 

(Y, por supuesto, escribir en default_out lugar de stdout.)

sys.stdout está en modo de texto en Python 3. Por lo tanto, escribe unicode en él directamente, y ya no se necesita el idioma de Python 2.

Donde esto fallaría en Python 2:

 >>> import sys >>> sys.stdout.write(u"ûnicöde") Traceback (most recent call last): File "", line 1, in  UnicodeEncodeError: 'ascii' codec can't encode character u'\xfb' in position 0: ordinal not in range(128) 

Sin embargo, funciona simplemente excelente en Python 3:

 >>> import sys >>> sys.stdout.write("Ûnicöde") Ûnicöde7 

Ahora, si tu Python no sabe qué es realmente tu encoding stdouts, ese es un problema diferente, muy probablemente en la comstackción de Python.