web-dev-qa-db-fra.com

Des pièges utilisant unicode_literals dans Python 2.6?

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).

99

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.

101
Koba

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
16
mfazekas

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 
13

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.

8
hvr

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?!?

5
GreenAsJade

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…

0
Sardathrion