Notre base de code fonctionne déjà sous Python 2.6. Afin de préparer Python 3.0, nous avons commencé à ajouter:
de __future__ import unicode_literals
dans nos fichiers .py
(tels que nous les modifions). Je me demande si quelqu'un d'autre l'a déjà fait et a rencontré des pièges non évidents (peut-être après avoir passé beaucoup de temps à déboguer).
La principale source de problèmes rencontrés lors de l'utilisation de chaînes unicode provient du mélange de chaînes encodées en utf-8 et de chaînes unicode.
Par exemple, considérons les scripts suivants.
deux.py
# encoding: utf-8
name = 'helló wörld from two'
un.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 faisiez print name + two.name.decode('utf-8')
.
La même chose peut arriver si vous encodez une chaîne et essayez de les 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 parce que 'DEBUG: %s'
est une chaîne unicode et que, par conséquent, python tente de décoder html
. Il existe deux manières de corriger l’impression en utilisant 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.
De plus, dans la version 2.6 (avant la version 2.6.5 RC1 + de python), les littéraux unicode ne jouent pas avec les arguments de mot clé ( issue4978 ):
Le code suivant, par exemple, fonctionne sans unicode_literals, mais échoue avec TypeError: keywords must be string
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 trouvé que si vous ajoutez la directive unicode_literals
, vous devez également ajouter quelque chose comme:
# -*- coding: utf-8
à la première ou à la deuxième ligne de votre fichier .py. Sinon, des lignes telles que:
foo = "barré"
entraîner une erreur telle que:
SyntaxError: Caractère non ASCII '\ xc3' dans le fichier mumble.py à la ligne 198, mais aucun encodage déclaré; voir http://www.python.org/peps/pep-0263.html pour plus de détails
Tenez également compte du fait que unicode_literal
affectera eval()
mais pas repr()
(un comportement asymétrique qui est un bug), c’est-à-dire que 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 d'utilisation de unicode_literals
et 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+
La deuxième assertion fonctionne, puisque repr('\xa4')
est évalué à u'\xa4'
en Python 2.7.
Il y en a plus.
Il existe des bibliothèques et des commandes 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 ésotique) 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 à fixer avec
myenum = type(b'Enum', (), enum)
mais ce dernier est dévastateur si votre code est plein d'appels à pub.sendMessage () (le mien).
Dang it, hein?!?
Click déclenchera des exceptions unicode partout si un module ayant from __future__ import unicode_literals
est importé et que vous utilisez click.echo
. C'est un cauchemar…