python encoding utf-8 python-2.x

python - Peligros de sys.setdefaultencoding(''utf-8'')



python-2.x (5)

Ejemplo de palabra real # 1

No funciona en pruebas unitarias .

El corredor de prueba ( nose , py.test , ...) inicializa sys primero, y solo luego descubre e importa sus módulos. Para entonces, es demasiado tarde para cambiar la codificación predeterminada.

Por la misma virtud, no funciona si alguien ejecuta su código como módulo, ya que su inicialización es lo primero.

Y sí, mezclar str y unicode y confiar en la conversión implícita solo empuja el problema más adelante.

Hay una tendencia a desalentar la configuración de sys.setdefaultencoding(''utf-8'') en Python 2. ¿Alguien puede enumerar ejemplos reales de problemas con eso? Argumentos como it is harmful o it hides bugs no suenan muy convincentes.

ACTUALIZACIÓN : Tenga en cuenta que esta pregunta solo se trata de utf-8 , no se trata de cambiar la codificación predeterminada "en general".

Por favor, da algunos ejemplos con código si puedes.


El póster original solicitó un código que demuestre que el interruptor es dañino, excepto que "oculta" errores no relacionados con el interruptor.

Resumen de conclusiones

Basado en la experiencia y la evidencia que he recopilado, aquí están las conclusiones a las que he llegado.

  1. Establecer la codificación predeterminada en UTF-8 hoy en día es seguro , excepto para aplicaciones especializadas, que manejan archivos de sistemas no preparados para Unicode.

  2. El rechazo "oficial" del cambio se basa en razones que ya no son relevantes para una gran mayoría de usuarios finales (no proveedores de bibliotecas), por lo que debemos dejar de desalentar a los usuarios a configurarlo.

  3. Trabajar en un modelo que maneja Unicode correctamente de manera predeterminada es mucho más adecuado para aplicaciones para comunicaciones entre sistemas que trabajar manualmente con API unicode.

Efectivamente, la modificación de la codificación predeterminada con frecuencia evita una serie de dolores de cabeza del usuario en la gran mayoría de los casos de uso. Sí, hay situaciones en las que los programas que se ocupan de múltiples codificaciones se comportarán silenciosamente de forma silenciosa, pero dado que este interruptor se puede habilitar por partes, esto no es un problema en el código del usuario final .

Más importante aún, habilitar este indicador es una ventaja real es el código de los usuarios, ya que reduce la sobrecarga de tener que manejar manualmente las conversiones Unicode, satura el código y lo hace menos legible, pero también evita posibles errores cuando el programador no puede hacer esto. adecuadamente en todos los casos.

Dado que estas afirmaciones son prácticamente lo opuesto a la línea de comunicación oficial de Python, creo que la explicación de estas conclusiones está justificada.

Ejemplos de uso exitoso de una codificación predeterminada modificada en la naturaleza

  1. Dave Malcom de Fedora creía que siempre tiene la razón. Propuso, después de investigar los riesgos, cambiar la distribución amplia def.enc. = UTF-8 para todos los usuarios de Fedora.

    Sin embargo, el hecho difícil presentado por qué Python se rompería es solo el comportamiento de hashing que listed , que nunca es detectado por ningún otro oponente dentro de la comunidad central como una razón para preocuparse o incluso por la misma persona , cuando trabaja en tickets de usuario.

    Curriculum vitae de Fedora : Es cierto que el cambio en sí mismo se describió como "muy impopular" con los desarrolladores principales, y se le acusó de ser inconsistente con las versiones anteriores.

  2. Hay 3000 proyectos solos en openhub haciéndolo. Tienen una interfaz de búsqueda lenta, pero escaneando sobre ella, calculo que el 98% está usando UTF-8. Nada encontrado sobre sorpresas desagradables.

  3. Hay 18000 (!) Ramas maestras github con él cambiado.

    Si bien el cambio es " impopular " en la comunidad central, es bastante popular en la base de usuarios. Aunque esto podría pasarse por alto, ya que se sabe que los usuarios usan soluciones extravagantes, no creo que este sea un argumento relevante debido a mi siguiente punto.

  4. Debido a esto, solo hay 150 informes de errores en total en GitHub. A una tasa efectiva del 100%, el cambio parece ser positivo, no negativo.

    Para resumir los problemas existentes con los que la gente se ha encontrado, he examinado todos los tickets mencionados anteriormente.

    • Chaging def.enc. El UTF-8 generalmente se introduce pero no se elimina en el proceso de cierre del problema, con mayor frecuencia como una solución. Algunos más grandes lo excusan como una solución temporal , teniendo en cuenta la "mala prensa" que tiene, pero muchos más reporteros de errores están glad con la solución .

    • Unos pocos proyectos (1-5?) Modificaron su código haciendo las conversiones de tipo manualmente para que ya no necesitaran cambiar el valor predeterminado.

    • En dos casos, veo a alguien que dice eso con def.enc. configurado en UTF-8 conduce a una falta total de salida por entirely , sin explicar la configuración de prueba. No pude verificar el reclamo, probé uno y descubrí que lo contrario era cierto.

    • Uno claims su "sistema" podría depender de no cambiarlo, pero no sabemos por qué.

    • Uno (y solo uno) tenía una razón real para evitarlo: ipython usa un módulo de terceros o el corredor de prueba modificó su proceso de forma incontrolada (nunca se discute que un cambio de definición es defendido solo por sus proponentes en el momento de configuración del intérprete, es decir, cuando se ''posee'' el proceso).

  5. No encontré ninguna indicación de que los diferentes hashes de ''é'' y u''é ''causen problemas en el código del mundo real.

  6. Python no se "rompe"

    Después de cambiar la configuración a UTF-8, ninguna característica de Python cubierta por las pruebas unitarias funciona de manera diferente que sin el interruptor. Sin embargo, el interruptor en no se prueba en absoluto.

  7. Se aconseja en bugs.python.org a usuarios frustrados.

    Ejemplos here , aquí o here (a menudo conectados con la línea oficial de advertencia)

    El primero demuestra cuán establecido está el interruptor en Asia (compárese también con el argumento github).

  8. Ian Bicking published su apoyo para permitir siempre este comportamiento.

    Puedo hacer que mis sistemas y comunicaciones sean consistentemente UTF-8, las cosas mejorarán. Realmente no veo un inconveniente. Pero, ¿por qué Python lo hace TAN MALDITO? [...] Siento que alguien decidió que era más inteligente que yo, pero no estoy seguro de creerlo.

  9. Martijn Fassen, mientras refutaba a Ian, admitted que ASCII podría haberse equivocado en primer lugar.

    Creo que si, por ejemplo, Python 2.5, se envía con una codificación predeterminada de UTF-8, en realidad no rompería nada. Pero si lo hiciera para mi Python, tendría problemas pronto cuando le di mi código a otra persona.

  10. En Python3, no "practican lo que predican"

    Mientras se opone a cualquier def.enc. cambian tan drásticamente debido al código dependiente del entorno o a la implicidad, una discusión here gira en Python3 los problemas de Python3 con su paradigma ''sandwich unicode'' y los supuestos implícitos requeridos correspondientes.

    Además, crearon posibilidades para escribir código válido de Python3 como:

    >>> from 褐褑褒褓褔褕褖褗褘 import * >>> def 空手(合氣道): あいき(ど(合氣道)) >>> 空手(う힑힜(''👏 '') + 흾) 💔

  11. DiveIntoPython lo recomienda .

  12. En este hilo , el propio Guido advises un usuario final profesional que use un entorno específico del proceso con el interruptor configurado para "crear un entorno Python personalizado para cada proyecto".

    La razón fundamental por la que los diseñadores de la biblioteca estándar 2.x de Python no quieren que pueda establecer la codificación predeterminada en su aplicación, es que la biblioteca estándar está escrita con el supuesto de que la codificación predeterminada es fija, y no hay garantías sobre el funcionamiento correcto de la biblioteca estándar se puede hacer cuando lo cambia. No hay pruebas para esta situación. Nadie sabe qué fallará cuando. Y usted (o, lo que es peor, sus usuarios) volverá a nosotros con quejas si la biblioteca estándar de repente comienza a hacer cosas que no esperaba.

  13. Jython ofrece cambiarlo sobre la marcha, incluso en módulos.

  14. PyPy no admitió la recarga (sys), pero la devolvió a solicitud del usuario en un solo día sin hacer preguntas. Compare con la actitud de CPython " lo está haciendo mal ", claiming sin pruebas que es la "raíz del mal".

Al finalizar esta lista, confirmo que uno podría construir un módulo que falla debido a una configuración de intérprete modificada, haciendo algo como esto:

def is_clean_ascii(s): """ [Stupid] type agnostic checker if only ASCII chars are contained in s""" try: unicode(str(s)) # we end here also for NON ascii if the def.enc. was changed return True except Exception, ex: return False if is_clean_ascii(mystr): <code relying on mystr to be ASCII>

No creo que este sea un argumento válido porque la persona que escribió este módulo de aceptación de tipo dual obviamente era consciente de las cadenas ASCII frente a las no ASCII y estaría al tanto de la codificación y decodificación.

Creo que esta evidencia es una indicación más que suficiente de que cambiar esta configuración no conduce a ningún problema en las bases de código del mundo real la gran mayoría de las veces.


En primer lugar: muchos oponentes de cambiar el valor predeterminado por defecto argumentan que es tonto porque incluso cambia las comparaciones ascii

Creo que es justo aclarar que, de acuerdo con la pregunta original, no veo a nadie que defienda nada más que desviarse de Ascii a UTF-8 .

El ejemplo setdefaultencoding (''utf-16'') parece ser siempre presentado por aquellos que se oponen a cambiarlo ;-)

Con m = {''a'': 1, ''é'': 2} y el archivo ''out.py'':

# coding: utf-8 print u''é''

Entonces:

+---------------+-----------------------+-----------------+ | DEF.ENC | OPERATION | RESULT (printed)| +---------------+-----------------------+-----------------+ | ANY | u''abc'' == ''abc'' | True | | (i.e.Ascii | str(u''abc'') | ''abc'' | | or UTF-8) | ''%s %s'' % (''a'', u''a'') | u''a a'' | | | python out.py | é | | | u''a'' in m | True | | | len(u''a''), len(a) | (1, 1) | | | len(u''é''), len(''é'') | (1, 2) [*] | | | u''é'' in m | False (!) | +---------------+-----------------------+-----------------+ | UTF-8 | u''abé'' == ''abé'' | True [*] | | | str(u''é'') | ''é'' | | | ''%s %s'' % (''é'', u''é'') | u''é é'' | | | python out.py | more | ''é'' | +---------------+-----------------------+-----------------+ | Ascii | u''abé'' == ''abé'' | False, Warning | | | str(u''é'') | Encoding Crash | | | ''%s %s'' % (''é'', u''é'') | Decoding Crash | | | python out.py | more | Encoding Crash | +---------------+-----------------------+-----------------+

[*]: El resultado asume el mismo é. Ver más abajo sobre eso.

Al observar esas operaciones, cambiar la codificación predeterminada en su programa podría no verse tan mal, lo que le dará resultados "más cercanos" a tener datos solo de Ascii.

Con respecto al comportamiento hash (in) y len (), obtienes lo mismo que en Ascii (más sobre los resultados a continuación). Esas operaciones también muestran que existen diferencias significativas entre las cadenas de bytes y unicode, lo que podría causar errores lógicos si usted las ignora.

Como ya se señaló: es una opción de todo el proceso , por lo que solo tiene una oportunidad para elegirla, que es la razón por la cual los desarrolladores de la biblioteca nunca deberían hacerlo, sino poner sus componentes internos en orden para que no tengan que depender de lo implícito de Python conversiones También necesitan documentar claramente lo que esperan y devolver y negar la entrada para la que no escribieron la biblioteca (como la función normalizar, ver más abajo).

=> Escribir programas con esa configuración activa hace que sea arriesgado que otros usen los módulos de su programa en su código, al menos sin filtrar la entrada.

Nota: Algunos oponentes afirman que def.enc. es incluso una opción de todo el sistema (a través de sitecustomize.py) pero lo último en tiempos de contenedorización de software (docker) cada proceso puede iniciarse en su entorno perfecto sin gastos generales.

Con respecto al comportamiento hash y len ():

Te dice que incluso con un def.enc modificado. aún no puede ignorar los tipos de cadenas que procesa en su programa. u '''' y '''' son diferentes secuencias de bytes en la memoria, no siempre sino en general.

Entonces, cuando realice las pruebas, asegúrese de que su programa se comporte correctamente también con datos que no sean Ascii.

Algunos dicen que el hecho de que los valores hash pueden volverse desiguales cuando cambian los valores de los datos, aunque debido a las conversiones implícitas las operaciones ''=='' permanecen iguales, es un argumento en contra de cambiar def.enc.

Personalmente, no comparto eso, ya que el comportamiento de hash sigue siendo el mismo que sin cambiarlo. Todavía no he visto un ejemplo convincente de comportamiento no deseado debido a esa configuración en un proceso que ''poseo''.

En general, con respecto a setdefaultencoding ("utf-8"): la respuesta con respecto a si es tonto o no debería ser más equilibrado.

Depende. Si bien evita bloqueos, por ejemplo, en las operaciones str () en una declaración de registro, el precio es una mayor posibilidad de resultados inesperados más adelante, ya que los tipos incorrectos hacen que el código sea más largo y su funcionamiento correcto depende de un determinado tipo.

En ningún caso debería ser la alternativa a aprender la diferencia entre cadenas de bytes y cadenas unicode para su propio código.

Por último, establecer la codificación predeterminada fuera de Ascii no hace que su vida sea más fácil para operaciones de texto comunes como len (), segmentación y comparaciones, en caso de que suponga que (byte) stringyfying todo con UTF-8 en resuelve problemas aquí.

Lamentablemente no lo hace, en general.

Los resultados ''=='' y len () son un problema mucho más complejo de lo que uno podría pensar, pero incluso con el mismo tipo en ambos lados.

Sin def. cambiado, "==" falla siempre para no Ascii, como se muestra en la tabla. Con él funciona, a veces:

Unicode estandarizó alrededor de un millón de símbolos del mundo y les dio un número, pero desafortunadamente NO hay una biyección 1: 1 entre los glifos que se muestran a un usuario en los dispositivos de salida y los símbolos a partir de los cuales se generan.

Para motivarlo, investigue esto : Tener dos archivos, j1, j2 escritos con el mismo programa usando la misma codificación, que contiene la entrada del usuario:

>>> u1, u2 = open(''j1'').read(), open(''j2'').read() >>> print sys.version.split()[0], u1, u2, u1 == u2

Resultado: 2.7.9 José José Falso (!)

Usando print como una función en Py2, ve la razón: desafortunadamente, hay DOS formas de codificar el mismo carácter, la ''e'' acentuada:

>>> print (sys.version.split()[0], u1, u2, u1 == u2) (''2.7.9'', ''Jos/xc3/xa9'', ''Jose/xcc/x81'', False)

Qué estúpido códec podría decir, pero no es culpa del códec. Es un problema en Unicode como tal.

Entonces, incluso en Py3:

>>> u1, u2 = open(''j1'').read(), open(''j2'').read() >>> print sys.version.split()[0], u1, u2, u1 == u2

Resultado: 3.4.2 José José Falso (!)

=> Independiente de Py2 y Py3, en realidad independiente de cualquier lenguaje informático que utilice: para escribir software de calidad, probablemente tenga que "normalizar" todas las entradas del usuario. El estándar Unicode estandarizó la normalización. En Python 2 y 3 la función unicodedata.normalize es tu amiga.


Porque no siempre desea que sus cadenas se descodifiquen automáticamente en Unicode, o de hecho, sus objetos Unicode se codifiquen automáticamente en bytes. Como está pidiendo un ejemplo concreto, aquí hay uno:

Tome una aplicación web WSGI; está creando una respuesta agregando el producto de un proceso externo a una lista, en un bucle, y ese proceso externo le proporciona bytes codificados UTF-8:

results = [] content_length = 0 for somevar in some_iterable: output = some_process_that_produces_utf8(somevar) content_length += len(output) results.append(output) headers = { ''Content-Length'': str(content_length), ''Content-Type'': ''text/html; charset=utf8'', } start_response(200, headers) return results

Eso es genial y está bien y funciona. Pero luego aparece su compañero de trabajo y agrega una nueva característica; ahora también está proporcionando etiquetas, y estas están localizadas:

results = [] content_length = 0 for somevar in some_iterable: label = translations.get_label(somevar) output = some_process_that_produces_utf8(somevar) content_length += len(label) + len(output) + 1 results.append(label + ''/n'') results.append(output) headers = { ''Content-Length'': str(content_length), ''Content-Type'': ''text/html; charset=utf8'', } start_response(200, headers) return results

Lo probaste en inglés y todo sigue funcionando, ¡genial!

Sin embargo, la biblioteca translations.get_label() realidad devuelve valores Unicode y cuando cambia la configuración regional, las etiquetas contienen caracteres que no son ASCII.

La biblioteca WSGI escribe esos resultados en el socket, y todos los valores Unicode se codifican automáticamente por usted, ya que establece setdefaultencoding() en UTF-8, pero la longitud que calculó es completamente incorrecta . Será demasiado corto ya que UTF-8 codifica todo fuera del rango ASCII con más de un byte.

Todo esto ignora la posibilidad de que realmente esté trabajando con datos en un códec diferente; podría estar escribiendo Latin-1 + Unicode, y ahora tiene un encabezado de longitud incorrecta y una mezcla de codificaciones de datos.

Si no hubiera utilizado sys.setdefaultencoding() se habría producido una excepción y sabía que tenía un error, pero ahora sus clientes se quejan de respuestas incompletas; faltan bytes al final de la página y no sabes muy bien cómo sucedió eso.

Tenga en cuenta que este escenario ni siquiera involucra bibliotecas de terceros que pueden o no depender de que el valor predeterminado sea ASCII. La sys.setdefaultencoding() es global y se aplica a todo el código que se ejecuta en el intérprete. ¿Qué tan seguro está de que no hay problemas en esas bibliotecas que impliquen codificación o decodificación implícita?

Que Python 2 codifique y descodifique implícitamente entre los tipos str y unicode puede ser útil y seguro cuando se trata solo de datos ASCII. Pero realmente necesita saber cuándo está mezclando accidentalmente datos Unicode y cadena de bytes, en lugar de enyesarlos con un pincel global y esperar lo mejor.


Una cosa que debemos saber es

Python 2 usa sys.getdefaultencoding() para decodificar / codificar entre str y unicode

así que si cambiamos la codificación predeterminada, habrá todo tipo de problemas incompatibles. p.ej:

# coding: utf-8 import sys print "你好" == u"你好" # False reload(sys) sys.setdefaultencoding("utf-8") print "你好" == u"你好" # True

Más ejemplos:

Dicho esto, recuerdo que hay algunos blogs que sugieren usar unicode siempre que sea posible, y solo una cadena de bits cuando se trata de E / S. Creo que si sigues esta convención, la vida será mucho más fácil. Se pueden encontrar más soluciones: