Любые ошибки с использованием unicode_literals в Python 2.6?
Мы уже получили базу кода под Python 2.6. Чтобы подготовиться к Python 3.0, мы начали добавлять:
from __future__ import unicode_literals
в наши .py
файлы (по мере их изменения). Мне интересно, если кто-то еще это делает и столкнулся с любыми неочевидными ошибками (возможно, потратив много времени на отладку).
Ответы
Ответ 1
Основным источником проблем, с которыми я работал с строками unicode, является то, что вы смешиваете строки с кодировкой utf-8 с юникодными.
Например, рассмотрим следующие сценарии.
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
- это строка в Юникоде. Когда вы смешиваете оба, 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')
.
Надеюсь, это поможет вам понять потенциальные ошибки при использовании строк в Юникоде.
Ответ 2
Также в 2.6 (до python 2.6.5 RC1 +) литералы в unicode не играют хорошими с аргументами ключевого слова (issue4978):
Следующий код, например, работает без 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
Ответ 3
Я обнаружил, что если вы добавите директиву unicode_literals
, вы также должны добавить что-то вроде:
# -*- coding: utf-8
для первой или второй строки .pdf файл. В противном случае строки, такие как:
foo = "barré"
приведет к ошибке, например:
SyntaxError: Non-ASCII character '\xc3' in file mumble.py on line 198,
but no encoding declared; see http://www.python.org/peps/pep-0263.html
for details
Ответ 4
Также учтите, что 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.
Ответ 5
Есть больше.
Есть библиотеки и встроенные функции, которые ожидают строки, которые не переносят юникод.
Два примера:
встроенный:
myenum = type('Enum', (), enum)
(слегка esotic) не работает с 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() (который мой).
Положите его, eh?!?
Ответ 6
Click будет вызывать исключения юникода повсюду, если какой-либо модуль с from __future__ import unicode_literals
будет импортирован туда, где вы используете click.echo
. Это кошмар…