Locale.getpreferredencoding() - почему этот reset string.letters?

>>> import string
>>> import locale
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> locale.getpreferredencoding()
'UTF-8'
>>> string.letters
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

Любые обходные пути для этого?

Платформа: Linux Python2.6.7 и Python2.7.3 кажутся затронутыми, отлично работает в Python3 (с ascii_letters)

Ответы

Ответ 1

Примечание. Что было сделано для решения этой проблемы, это передать encoding='UTF-8' в вызов open. Если вы столкнулись с этой проблемой и ищете исправление, это работает. Остальная часть сообщения - это акцент на том, почему.


Что произойдет

Как сказал Лукас, в документах указывается:

В некоторых системах необходимо вызвать setlocale() для получения пользовательских настроек

Изначально string.letters имеет значение lowercase + uppercase:

lowercase = 'abcdefghijklmnopqrstuvwxyz'
uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
letters = lowercase + uppercase

Однако, когда вы вызываете getpreferredencoding(), модуль _locale переопределяет его, вызывая PyDict_SetItemString(string, "letters", ulo); после его создания внутри fixup_ulcase(void) со следующим:

/* create letters string */
n = 0;
for (c = 0; c < 256; c++) {
    if (isalpha(c))
        ul[n++] = c;
}
ulo = PyString_FromStringAndSize((const char *)ul, n);
if (!ulo)
    return;
if (string)
    PyDict_SetItemString(string, "letters", ulo);
Py_DECREF(ulo);

В свою очередь, это называется в PyLocale_setlocale, который действительно setlocale, который вызывается getpreferredencoding - здесь кодом http://hg.python.org/cpython/file/07a6fca7ff42/Lib/locale.py#l612:

  def getpreferredencoding(do_setlocale = True):
        """Return the charset that the user is likely using,
        according to the system configuration."""
        if do_setlocale:
            oldloc = setlocale(LC_CTYPE)
            try:
                setlocale(LC_CTYPE, "")
            except Error:
                pass
            result = nl_langinfo(CODESET)
            setlocale(LC_CTYPE, oldloc)
            return result
        else:
            return nl_langinfo(CODESET)

Как этого избежать?

Попробуйте getpreferredencoding(False)

Почему это не происходит в Windows?

В Windows используется другой код для получения локали, как вы можете видеть здесь.

В Python 3

В Python 3 getdefaultlocale не принимает логическую переменную setlocale и не вызывает сам setlocale, поскольку вы можете видеть здесь.