python-3.x – Problema de codificación con python3 y haga clic en paquete

Cuando la versión click detecta que el tiempo de ejecución es python3 pero la codificación es ASCII, finaliza abruptamente el programa python:

RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Either switch to Python 2 or consult http://click.pocoo.org/python3/ for mitigation steps.

Encontré la causa de este problema en mi caso, cuando me conecto a mi host de Linux desde mi Mac, Terminal.app configura la configuración regional de la sesión SSH a mi locale de Mac (es_ES.UTF-8) Sin embargo, mi host de Linux no se ha instalado dicha configuración regional (solo en_US.utf-8).

Apliqué una solución inicial para solucionarlo (pero tenía muchos problemas, vea la respuesta aceptada):

import locale, codecs
# locale.getpreferredencoding() == 'ANSI_X3.4-1968'
if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
    os.environ['LANG'] = 'en_US.utf-8'

EDITAR: Para un mejor parche ver mi respuesta aceptada.

Todos los hosts de mi linux han instalado ‘en_US.utf-8’ como configuración regional (Fedora lo usa como predeterminado).

Mi pregunta es: ¿Existe una forma mejor (más robusta) de elegir / forzar la configuración regional en un script de python3? Por ejemplo, configurando uno de los locales disponibles en el sistema.

Tal vez haya un enfoque diferente para solucionar este problema pero no lo encontré.

Mejor respuesta
Bueno, mi solución inicial inicial tuvo muchas fallas, tuve que pasar la verificación de la biblioteca de clics sobre la codificación, pero la codificación en sí no fue fija, por lo que recibí excepciones cuando los parámetros de entrada o salida tenían caracteres no ascii.

Tuve que implementar un método más complejo, con 3 pasos: establecer la configuración regional, la codificación correcta en entrada / salida estándar y volver a codificar los parámetros de la línea de comandos, además de haber agregado una salida “amigable” si el primer intento intenta establecer la configuración regional no funciona como se esperaba

def prevent_ascii_env():
    """
    To avoid issues reading unicode chars from stdin or writing to stdout, we need to ensure that the 
    python3 runtime is correctly configured, if not, we try to force to utf-8, 
    but It isn't possible then we exit with a more friendly message that the original one.
    """
    import locale, codecs, os, sys
    # locale.getpreferredencoding() == 'ANSI_X3.4-1968'
    if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
        os.environ['LANG'] = 'en_US.utf-8'
        if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
            print("The current locale is not correctly configured in your system")
            print("Please set the LANG env variable to the proper value before to call this script")
            sys.exit(-1)
        #Once we have the proper locale.getpreferredencoding() We can change current stdin/out streams
        _, encoding = locale.getdefaultlocale()
        import io
        sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding=encoding, errors="replace", line_buffering=True)
        sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding=encoding, errors="replace", line_buffering=True)
        sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding=encoding, errors="replace", line_buffering=True)
        # And finally we need to re-encode the input parameters
        for i, p in enumerate(sys.argv):
            sys.argv[i] = os.fsencode(p).decode() 

Este parche resuelve casi todos los problemas; sin embargo, tiene una advertencia: el método shutils.get_terminal_size () genera un ValueError porque el sys .__ stdout__ se ha separado, click lib usa ese método para imprimir la ayuda, para solucionarlo tuve que aplicar una parche de mono en click lib

def wrapper_get_terminal_size():
    """
    Replace the original function termui.get_terminal_size (click lib) by a new one 
    that uses a fallback if ValueError exception has been raised
    """
    from click import termui, formatting

    old_get_term_size = termui.get_terminal_size
    def _wrapped_get_terminal_size():
        try:
            return old_get_term_size()
        except ValueError:
            import os
            sz = os.get_terminal_size()
            return sz.columns, sz.lines
    termui.get_terminal_size = _wrapped_get_terminal_size
    formatting.get_terminal_size = _wrapped_get_terminal_size

Con estos cambios, todos mis scripts funcionan bien ahora cuando el entorno tiene una configuración regional incorrecta pero el sistema admite en_US.utf-8 (es la configuración regional predeterminada de Fedora).

Si encuentra algún problema con este enfoque o tiene una solución mejor, agregue una nueva respuesta.

EDITAR: Hay un problema abierto (mejora), http://bugs.python.org/issue15216, que permitirá cambiar la codificación en un flujo creado (no utilizado) fácilmente (sys.std *). Pero está orientado a Python 3.7 Por lo tanto, tendremos que esperar un rato.

EDITAR (2017-12-08): He visto que hay un PEP 538 para py3.7, que cambiará todo el comportamiento de la administración de codificación de python3 durante el inicio, creo que el nuevo enfoque solucionará el problema original: https://www.python.org/dev/peps/pep-0538/

En mi humilde opinión, los cambios dirigidos a Python 3.7 para problemas de codificación deberían haberse planeado hace años, pero creo que más tarde que nunca.

Por favor indique la dirección original:python-3.x – Problema de codificación con python3 y haga clic en paquete - Código de registro