Юникод
Ещё с ранних времён Python 2 юникод был частью всех стандартных библиотек Python. Это позволяет разработчикам писать приложения, которые работают с символами, не входящими в ASCII, простым способом. Но работа с юникодом требует базовых знаний об этой теме, особенно при работе с библиотеками, которые его не поддерживают.
Werkzeug использует юникод во всех частях, где предполагаются текстовые данные, даже если стандарт HTTP не поддерживает юникод. В принципе, все входящие данные декодируются из указанного набора символов (по умолчанию utf-8), чтобы вы больше не работали с байтовыми строками. Исходящие данные в формате юникод затем кодируются обратно в целевой набор символов.
Юникод в Python
В Python 2 есть два основных типа строк: str и unicode. str может содержать закодированные данные юникода, но они всегда представлены в байтах, в то время как тип unicode не содержит байты, а код символов. Что это значит? Представьте немецкий диакритический знак ö. В ASCII вы не можете представить этот символ, но в наборах символов latin-1 и utf-8 вы можете его представить, но они выглядят по-разному при кодировании:
>>> u'ö'.encode('latin1')
'\xf6'
>>> u'ö'.encode('utf-8')
'\xc3\xb6'
Таким образом, ö может выглядеть совершенно по-разному в зависимости от кодировки, что затрудняет с ним работу. Решение — использовать тип unicode (как мы сделали выше, обратите внимание на префикс u перед строкой). Тип unicode не хранит байты для ö, а информацию о том, что это LATIN SMALL LETTER O WITH DIAERESIS.
Выполнение len(u'ö') всегда даст нам ожидаемое «1», но len('ö') может давать разные результаты в зависимости от кодировки 'ö'.
Юникод в HTTP
Проблема с юникодом заключается в том, что HTTP не знает, что такое юникод. HTTP ограничен байтами, но это не большая проблема, так как Werkzeug автоматически декодирует и кодирует все входящие и исходящие данные. В принципе, это означает, что данные, отправленные из браузера в веб-приложение, по умолчанию декодируются из байтовой строки utf-8 в строку unicode. Данные, отправленные приложением обратно в браузер, которые ещё не являются байтовой строкой, затем кодируются обратно в utf-8.
Обычно всё «работает», и нам не нужно об этом беспокоиться, но есть ситуации, когда это поведение проблематично. Например, уровень ввода-вывода Python 2 не понимает юникод. Это означает, что всякий раз, когда вы работаете с данными из файловой системы, вы должны правильно их декодировать. Правильный способ загрузки текстового файла из файловой системы выглядит так:
f = file('/path/to/the_file.txt', 'r')
try:
text = f.decode('utf-8') # assuming the file is utf-8 encoded
finally:
f.close()
Также существует модуль codecs, который предоставляет функцию open, которая автоматически декодирует из заданной кодировки.
Обработка ошибок
Функции, выполняющие внутреннюю кодировку или декодирование, принимают ключевое слово errors, которое передаётся в str.decode() и str.encode(). По умолчанию используется 'replace', чтобы ошибки легко было обнаружить. Может быть полезно установить его на 'strict', чтобы поймать ошибку и сообщить клиенту о некорректных данных.
Объекты запроса и ответа
Поскольку объекты запроса и ответа обычно являются центральными объектами приложений, работающих с Werkzeug, вы можете изменить кодировку по умолчанию, с которой работает Werkzeug, путём наследования этих двух классов. Например, вы можете легко установить кодировку приложения в utf-7 и строгую обработку ошибок:
from werkzeug.wrappers import BaseRequest, BaseResponse
class Request(BaseRequest):
charset = 'utf-7'
encoding_errors = 'strict'
class Response(BaseResponse):
charset = 'utf-7'
Помните, что обработка ошибок настраивается только для всех декодирований, но не кодирования. Если Werkzeug обнаружит ошибку кодирования, он поднимет UnicodeEncodeError. Вам следует позаботиться о том, чтобы не создавать данные, отсутствующие в целевой кодировке (неактуально для всех кодировок юникод, таких как utf-8).
Файловая система
Журнал изменений
Изменено в версии 0.11.
До версии 0.11 Werkzeug использовал функциональность стандартной библиотеки Python для определения кодировки файловой системы. Однако несколько сообщений об ошибках в Werkzeug показали, что значение sys.getfilesystemencoding() нельзя доверять на традиционных системах UNIX. Обычно проблемы возникают из-за неправильно настроенных систем, где переменные окружения LANG и им подобные не заданы. В таких случаях Python по умолчанию устанавливает кодировку файловой системы в ASCII, что очень консервативное значение, которое обычно является неправильным и создаёт больше проблем, чем решает.
Поэтому Werkzeug заставит кодировку файловой системы принять значение UTF-8 и выдаст предупреждение всякий раз, когда обнаружит, что работает под BSD или Linux, и sys.getfilesystemencoding() возвращает кодировку ASCII.
См. также werkzeug.filesystem.
© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://werkzeug.palletsprojects.com/en/1.0.x/unicode/