Accueil / Blog / Métier / 2014 / Rendre son code compatible avec Python 3

Rendre son code compatible avec Python 3

Par Alex Marandon publié 14/01/2014
Comment rendre son projet compatible avec Python 2 et Python 3
Rendre son code compatible avec Python 3

Avec Django qui est maintenant officiellement compatible avec Python 3 et Pyramid qui l'est depuis un moment déjà, il devient tout à fait possible de développer des projets web avec Python 3. Toutefois, on peut être réticent à développer uniquement pour cette version du langage et ce pour plusieurs raisons :

  • si notre projet est un module réutilisable, on veut qu'il reste utilisable par des développeurs qui ne sont pas encore passés à Python 3 ;
  • s'il s'agit d'une application pour les utilisateurs finaux, on peut vouloir conserver la possibilité de la déployer sur des serveurs ne disposant pas de Python 3 ;
  • on peut souhaiter se réserver la possibilité d'intégrer une bibliothèque tierce qui n'est pas encore compatible avec Python 3, quitte à suspendre le support de Python 3 pour notre projet, le temps que ce composant tierce soit mis à jour pour fonctionner avec Python 3.

Nous vous présentons ici une approche qui permet de développer du code qui fonctionne à la fois sur Python 2 et sur Python 3, le plus facilement possible.

Quelle version de Python 3 utiliser ?

Il est beaucoup plus facile d'écrire du code rétro-compatible avec Python 3.3 qu'avec les version précédentes de Python 3, car Python 3.3 a réintroduit les chaîne unicode littérales explicites. En Python 3.2, ce code déclenche une erreur de syntaxe :

>>> s = u"serpent"
  File "<stdin>", line 1
    s = u"serpent"
                 ^
SyntaxError: invalid syntax

En Python 3.3, le préfixe u n'a aucun effet, puisque les chaînes sont de toute façon en unicode, mais il ne déclenche pas d'erreur et permet d'écrire des chaînes unicode littérales compatibles avec Python 2.

Pour cette raison, vous voudrez probablement ignorer Python 3.2 et vous concentrer sur le support de Python 3.3 et plus.

Test automatisés

Vous êtes probablement déjà convaincus de l'intérêt d'avoir des tests automatisés pour votre code. Si vous voulez que votre code soit compatible avec les deux versions de Python, cela devient absolument indispensable.

Pour vous permettre d'exécuter ces tests automatiquement sur les deux versions de Python, vous pouvez utiliser tox. Cet outil se configure à l'aide d'un fichier INI :

[tox]
envlist = py27, py33

[testenv]
commands = nosetests -v
deps = -rrequirements.txt
       -rdev_requirements.txt

Dans cet exemple, nous indiquons à tox que nous souhaitons exécuter nos tests avec nose, sous Python 2.7 et Python 3.3, en installant les dépendances listées dans les fichiers de dépendance pip requirements.txt et dev_requirements.txt. Bien entendu, tox peut être configuré pour installer des dépendances avec un autre outil que pip.

Avec un fichier tox.ini à la racine de votre projet, vous pourrez exécuter vos tests sur les deux versions de Python en une seule commande

$ tox

Le service d'intégration continue Travis vous permet également d'exécuter très facilement vos tests avec deux versions de Python. Voici les première ligne d'un fichier .travis.yml illustrant cet usage :

language: python

python:
  - 2.7
  - 3.3

# ...

Compatibilité de notre code

Comme vous le savez probablement, Python 3 n'est pas complètement rétro-compatible avec Python 2. Même s'il s'agit globalement du même langage, il y a certains cas où il est difficile d'écrire du code qui fonctionne sur les deux versions. Pour adresser ce problème, la bibliothèque six fournit une couche de compatibilité qui peut s'avérer d'une aide précieuse.

Par exemple si vous utilisez le module StringIO, vous constaterez qu'il ne peut être importé de la même façon dans les deux versions du langage. En Python 2 :

from StringIO import StringIO

Alors qu'on Python 3 il faut écrire :

from io import StringIO

La bibliothèque six fournit une couche de compatibilité pour les imports de module, de sorte que le code suivant fonctionnera à la fois sur Python 2 et sur Python 3 :

from six.moves import StringIO

Voici un autre exemple. En Python 2, pour déterminer si un objet est une chaîne de caractère, étant donné qu'on avait deux types de chaînes (str et unicode), on utilisait généralement leur classe parente basetring de cette façon :

>>> isinstance('éléphant', basestring)
True
>>> isinstance(u'éléphant', basestring)
True
>>> isinstance(42, basestring)
False

En Python 3, il n'y a plus qu'un seul type de chaîne et basestring a disparu. Pour résoudre ce problème, la bibliothèque six fournit la constante string_types, qui permet de faire un test compatible :

>>> import six
>>> isinstance('éléphant', six.string_types)
True

En dernier recours, il est possible de brancher facilement sur la version de Python à l'aide des constantes six.PY2 et six.PY3 :

>>> if six.PY2:
...     print "Je suis Python 2"
... else:
...     print("Je suis Python 3")
...

Pour rendre son code compatible on va donc essayer ces trois approches, par ordre décroissant de préférence :

  • utiliser des constructions Python standard qui fonctionnent avec les deux versions. Par exemple on mettra systématiquement des parenthèses autour des arguments de print, même si ce n'est pas nécessaire avec Python 2, ou encore on préfixera systématiquement les chaînes unicodes littérales avec un u , même si ce n'est pas nécessaire en Python 3 ;
  • utiliser la bibliothèque six comme couche de compatibilité, comme indiqué précédemment ;
  • enfin, en dernier recours, brancher sur la version de Python grâce aux constantes de la bibliothèque six pour écrire des instructions différentes suivant la version du langage.

Compatibilité des bibliothèque tierces

De plus en plus de bibliothèques Python sont portées sur Python 3, mais il faut parfois chercher un peu pour dénicher ces portages, car ils ne sont pas toujours effectués par les créateurs originaux des bibliothèques en question. Citons à titre d'exemple le cas du module django-ckeditor, dont la version originale n'a pas intégré le support de Python 3. Un développeur a pris l'initiative d'effectuer le portage et de le distribuer sous un nouveau nom : django-ckeditor-updated.

Du côté de Pyramid, on peut citer le cas du paquet xlwt qui est nécessaire au fonctionnement des outils d'internationalisation et qui n'est pas compatible Python 3. Un développeur a fait l'effort de corriger ça et distribue sa version mise à jour de xlwt sous un nouveau nom : xlwt-future.

Il ne faut donc pas hésiter à fouiller parmi les tickets des projets qui ne supportent pas Python 3 à première vue, on y trouvera souvent un lien vers un fork qui corrige la situation.

À vous de jouer

Il est plus facile que jamais de rendre son code compatible Python 3. Cela demande quelques efforts, mais rien d'insurmontable. Conserver la compatibilité avec Python 2 nous permet de nous rendre compte des différences à travers des cas concrets et de constater lorsqu'il y a des incompatibilités que c'est Python 3 qui a raison. Nous découvrons alors un Python plus cohérent, débarrassé des scories du passé.

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Inscrivez-vous à notre prochaine formation d'initiation au Python Scientifique Inscrivez-vous à notre prochaine formation d'initiation au Python Scientifique 22/03/2021

Du 29 au 31 mars, nous ouvrons une session de formation à distance «Introduction au Python ...

Migrer une application de Python 2 à Python 3 Migrer une application de Python 2 à Python 3 21/07/2020

Le support de Python 2 est officiellement terminé, c'est le moment de passer vos applications à ...

Machine Learning : classer automatiquement vos données à l'import Machine Learning : classer automatiquement vos données à l'import 20/03/2018

Comment utiliser des algorithmes de machine learning pour importer correctement des données dans ...

Utilisation de la vision par ordinateur pour redresser des images Utilisation de la vision par ordinateur pour redresser des images 14/05/2019

Dans un module de comparaison d'images, lorsque deux photographies ne sont pas cadrées de la même ...

Bien configurer ses tests Python avec tox et Travis Bien configurer ses tests Python avec tox et Travis 18/03/2019

Le plus difficile dans le développement des tests unitaires c'est souvent de se motiver à écrire ...