Любые ошибки, использующие unicode_literals в Python 2.6?
Мы уже запустили нашу кодовую базу под Python 2.6. Чтобы подготовиться к Python 3.0, мы начали добавлять:
из __future__ импорт unicode_literals
в наш .py
файлы (как мы их модифицируем). Мне интересно, если кто-то еще делал это и сталкивался с какими-то неочевидными ошибками (возможно, после того, как потратил много времени на отладку).
6 ответов
Основным источником проблем, с которыми я сталкивался при работе со строками Unicode, является смешивание строк в кодировке UTF-8 со строками Unicode.
Например, рассмотрим следующие сценарии.
two.py
# encoding: utf-8
name = 'helló wörld from two'
one.py
# encoding: utf-8
from __future__ import unicode_literals
import two
name = 'helló wörld from one'
print name + two.name
Выход работает python one.py
является:
Traceback (most recent call last):
File "one.py", line 5, in <module>
print name + two.name
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)
В этом примере two.name
является строкой в кодировке utf-8 (не unicode), так как она не импортировала unicode_literals
, а также one.name
является строкой Unicode. Когда вы смешиваете оба, python пытается декодировать закодированную строку (предполагая, что это ascii) и преобразовывает ее в unicode, и терпит неудачу. Это бы сработало, если бы ты сделал print name + two.name.decode('utf-8')
,
То же самое может случиться, если вы закодируете строку и попытаетесь смешать их позже. Например, это работает:
# encoding: utf-8
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
html = html.encode('utf-8')
print 'DEBUG: %s' % html
Выход:
DEBUG: <html><body>helló wörld</body></html>
Но после добавления import unicode_literals
Это не:
# encoding: utf-8
from __future__ import unicode_literals
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
html = html.encode('utf-8')
print 'DEBUG: %s' % html
Выход:
Traceback (most recent call last):
File "test.py", line 6, in <module>
print 'DEBUG: %s' % html
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 16: ordinal not in range(128)
Это не удается, потому что 'DEBUG: %s'
является строкой Unicode, и поэтому Python пытается декодировать html
, Несколько способов исправить печать либо делают print str('DEBUG: %s') % html
или же print 'DEBUG: %s' % html.decode('utf-8')
,
Я надеюсь, что это поможет вам понять потенциальные ошибки при использовании Unicode-строк.
Также в 2.6 (до python 2.6.5 RC1+) литералы юникода не очень хорошо работают с аргументами ключевых слов ( проблема 4978):
Следующий код, например, работает без unicode_literals, но завершается с ошибкой TypeError: keywords must be string
если используется unicode_literals.
>>> def foo(a=None): pass
...
>>> foo(**{'a':1})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
Я обнаружил, что если вы добавите unicode_literals
Директива вы также должны добавить что-то вроде:
# -*- coding: utf-8
в первую или вторую строку вашего.py файла. В противном случае такие строки, как:
foo = "barré"
привести к ошибке, такой как:
Ошибка синтаксиса: не-ASCII символ '\xc3' в файле mumble.py в строке 198, но кодировка не объявлена; см. http://www.python.org/peps/pep-0263.html для деталей
Также примите во внимание, что unicode_literal
повлияет eval()
но нет repr()
(асимметричное поведение, которое imho является ошибкой), т.е. eval(repr(b'\xa4'))
не будет равным b'\xa4'
(как это было бы с Python 3).
В идеале следующий код должен быть инвариантом, который всегда должен работать для всех комбинаций unicode_literals
и использование Python {2.7, 3.x}:
from __future__ import unicode_literals
bstr = b'\xa4'
assert eval(repr(bstr)) == bstr # fails in Python 2.7, holds in 3.1+
ustr = '\xa4'
assert eval(repr(ustr)) == ustr # holds in Python 2.7 and 3.1+
Второе утверждение работает, так как repr('\xa4')
оценивает u'\xa4'
в Python 2.7.
Есть еще.
Есть библиотеки и встроенные функции, которые ожидают строки, которые не переносят юникод.
Два примера:
встроенная:
myenum = type('Enum', (), enum)
(немного эротично) не работает с unicode_literals: type() ожидает строку.
библиотека:
from wx.lib.pubsub import pub
pub.sendMessage("LOG MESSAGE", msg="no go for unicode literals")
не работает: библиотека wx pubsub ожидает тип строкового сообщения.
Первый эзотерический и легко фиксируется
myenum = type(b'Enum', (), enum)
но последнее разрушительно, если ваш код полон вызовов pub.sendMessage() (который у меня).
Черт возьми, а?!?
Click будет вызывать исключения Unicode везде, если какой-либо модуль, который имеет from __future__ import unicode_literals
импортируется, где вы используете click.echo
, Это кошмар...