Des pièges utilisant unicode_literals dans Python 2.6?
-
03-07-2019 - |
Question
Nous avons déjà mis notre base de code sous Python 2.6. Afin de préparer Python 3.0, nous avons commencé à ajouter:
from __future__ import unicode_literals
dans nos fichiers .py
(tels que nous les modifions). Je me demande si quelqu'un d'autre a fait cela et a rencontré des pièges non évidents (peut-être après avoir passé beaucoup de temps à déboguer).
La solution
La principale source de problèmes que j'ai rencontrés lors de l'utilisation de chaînes unicode provient du mélange de chaînes encodées en utf-8 avec des chaînes unicode.
Par exemple, considérez les scripts suivants.
deux.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
Le résultat de l'exécution de python one.py
est:
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)
Dans cet exemple, two.name
est une chaîne codée en utf-8 (non unicode) car elle n'a pas importé unicode_literals
et one.name
est une chaîne unicode. Lorsque vous mélangez les deux, python essaie de décoder la chaîne codée (en supposant que ce soit ascii) et la convertit en unicode et échoue. Cela fonctionnerait si vous imprimiez nom + deux.nom.decode ('utf-8')
.
La même chose peut se produire si vous encodez une chaîne et essayez de la mélanger plus tard. Par exemple, cela fonctionne:
# encoding: utf-8
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
html = html.encode('utf-8')
print 'DEBUG: %s' % html
Sortie:
DEBUG: <html><body>helló wörld</body></html>
Mais après avoir ajouté le import unicode_literals
, cela ne signifie PAS:
# 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
Sortie:
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)
Il échoue car 'DEBUG:% s'
est une chaîne unicode et que, par conséquent, python tente de décoder html
. Voici deux manières de corriger l'impression: print str ('DEBUG:% s')% html
ou print 'DEBUG:% s'% html.decode ('utf-8 ')
.
J'espère que cela vous aidera à comprendre les pièges potentiels liés à l'utilisation de chaînes Unicode.
Autres conseils
Également dans la version 2.6 (avant python 2.6.5 RC1 +), les littéraux unicode ne fonctionnent pas bien avec les arguments de mots clés ( issue4978 ):
Le code suivant, par exemple, fonctionne sans unicode_literals, mais échoue avec TypeError: les mots clés doivent être chaîne
si unicode_literals est utilisé.
>>> 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
J'ai constaté que si vous ajoutez la directive unicode_literals
, vous devez également ajouter un élément tel que:
# -*- coding: utf-8
à la première ou à la deuxième ligne de votre fichier .py. Sinon, des lignes telles que:
foo = "barré"
entraîne une erreur telle que:
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
Tenez également compte du fait que unicode_literal
affectera eval ()
mais pas repr ()
(comportement asymétrique qui imho est un bogue). eval (repr (b '\ xa4'))
ne sera pas égal à b '\ xa4'
(comme avec Python 3).
Idéalement, le code suivant serait un invariant, qui devrait toujours fonctionner, pour toutes les combinaisons de unicode_literals
et de Python {2.7, 3.x} usage:
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+
La deuxième assertion fonctionne, car repr ('\ xa4')
est évalué à u '\ xa4'
dans Python 2.7.
Il y en a plus.
Il existe des bibliothèques et des fonctions intégrées qui attendent des chaînes qui ne tolèrent pas l’unicode.
Deux exemples:
intégré:
myenum = type('Enum', (), enum)
(légèrement esotic) ne fonctionne pas avec unicode_literals: type () attend une chaîne.
bibliothèque:
from wx.lib.pubsub import pub
pub.sendMessage("LOG MESSAGE", msg="no go for unicode literals")
ne fonctionne pas: la bibliothèque wx pubsub attend un type de message sous forme de chaîne.
Le premier est ésotérique et facile à corriger avec
myenum = type(b'Enum', (), enum)
mais ce dernier est dévastateur si votre code est plein d’appels à pub.sendMessage () (quel mien est).
Dang, hein?!?
Click soulève des exceptions unicode partout si un module comportant de __future__ import unicode_literals
est importé et que vous utilisez click.echo
. C'est un cauchemar & # 8230;