Проблема кодирования с python3 и нажмите пакет

когда lib click обнаруживает, что среда выполнения python3, но кодировка ASCII, то он резко завершает программу 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.

Я нашел причину этой проблемы в моем случае, когда я подключаюсь к моему хосту Linux с моего Mac, терминала.приложение устанавливает локаль сеанса SSH в локаль моего Mac (es_ES.UTF-8) однако мой хост Linux не установил такую локаль (только en_US.utf-8).

я применил первоначальный обходной путь, чтобы исправить это (но это было многие вопросы, см. принятый ответ):

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

EDIT: для лучшего патча см. Мой принятый ответ.

все Мои хосты linux установили ' en_US.utf-8 ' в качестве локали (Fedora использует его по умолчанию).

мой вопрос: есть ли лучший (более надежный) способ выбрать/заставить локаль в скрипте python3 ? например, установка одного из доступных локалей в системе.

может быть, есть другой подход чтобы исправить эту проблему, но я ее не нашел.

2 ответов


Ну, мой первоначальный обходной путь имел много недостатков, я должен пройти click проверка библиотеки о кодировке, но сама кодировка не была исправлена, поэтому я получаю исключения, когда входные параметры или выходные данные имели символы, отличные от ascii.

мне пришлось реализовать более сложный метод с 3 шагами: установить локаль, правильную кодировку в std in / out и перекодировать параметры командной строки, кроме того, я добавил "дружественный" выход, если первая попытка установить локаль не работает как ожидалось:

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() 

этот патч решает почти все проблемы, однако он имеет оговорку, метод shutils.get_terminal_size() поднимает ValueError потому что sys.__stdout__ была отсоединена, click lib использует этот метод для печати справки, чтобы исправить это, мне пришлось применить патч обезьяны на 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

С этими изменениями все мои скрипты теперь работают нормально, когда в среде настроена неправильная локаль, но система поддерживает en_US.utf-8 (это локаль Fedora по умолчанию).

если вы найдете любую проблему на этом подходе или имеете лучшее решение, Пожалуйста, добавьте новый ответ.

редактировать: есть открытая проблема (улучшение),http://bugs.python.org/issue15216, что позволит легко изменить кодировку в созданном (не используемом) потоке (sys.ЗППП.)* Но нацелен на python 3.7, поэтому нам придется подождать некоторое время.

редактировать (2017-12-08): я видел, что есть PEP 538 для py3.7, это изменит все поведение управления кодировкой python3 во время запуска, я думаю, что новый подход исправит исходную проблему:https://www.python.org/dev/peps/pep-0538/

IMHO изменения, направленные на python 3.7 для проблем кодирования, должны были быть спланированы много лет назад, но лучше поздно, чем никогда, я думаю.


Это старая нить, однако этот ответ может помочь другим в будущем или мне. Если это *nux

env | grep LC_ALL

если он установлен, выполните следующие действия. Вот и все.

unset LC_ALL