Python : un environnement vraiment isolé avec GNU Guix

Magazine
Marque
GNU/Linux Magazine
Numéro
195
|
Mois de parution
juillet 2016
|
Domaines


Résumé
Peut-on créer facilement un environnement de tests complètement reproductible pour son application Python ? La solution est-elle vraiment virtualenv ? Un gestionnaire de paquets fonctionnel ne ferait-il pas mieux l'affaire ?

Body

Virtualenv est un logiciel très populaire chez les développeurs Python : il leur permet de définir des environnements virtuels au sein desquels ils peuvent installer des paquets Python sans modifier l'état de leur système. Nous verrons dans cet article que cette approche présente quelques problèmes et montrerons comment les résoudre en utilisant GNU Guix.

De nombreux développeurs Python utilisent aujourd'hui virtualenv, un outil dédié à la création d'environnements virtuels isolés. Sa principale utilité est de permettre d'installer plusieurs versions d'un même paquet Python (par exemple pour tester une application Web avec Django 1.8 et 1.9), ce qui est généralement difficile, sinon impossible, avec le gestionnaire de paquets fourni par une distribution GNU/Linux classique. De plus, ces paquets ne sont pas installés dans le site-packages global, évitant ainsi de « polluer » le système. Malheureusement, virtualenv souffre de quelques défauts que nous commencerons par lister avant de montrer comment, à l'aide de GNU Guix[1], un gestionnaire de paquets fonctionnel, il est possible de construire un environnement de développement réellement isolé du reste du système.

1. Virtualenv : un environnement isolé ?

Voyons tout d’abord comment, actuellement, la plupart des développeurs Python créent un environnement de développement « isolé ».

1.1 Utilisation de virtualenv

On peut facilement créer un environnement « virtuel » embarquant Python 3.4 grâce à la commande suivante :

$ virtualenv -p python3.4 py34

Il suffit ensuite de l'activer, ce qui permet d'y gérer ses paquets sans être root :

$ source py34/bin/activate

(py34)$ pip list

pip (1.5.6)

setuptools (18.8)

(py34)$ python -c "import six"

Traceback (most recent call last):

   File "<string>", line 1, in <module>

ImportError: No module named 'six'

(py34)$ pip install six

Downloading/unpacking six

   Downloading six-1.10.0-py2.py3-none-any.whl

Installing collected packages: six

Successfully installed six

Cleaning up...

(py34)$ python -c "import six"

(py34)$ deactivate

$

On peut remarquer que la bibliothèque six, bien qu'installée sur le système (c'est le paquet python3-six sous Debian) n'est pas disponible au sein de l'environnement virtuel et doit y être installée avec pip.

Il est possible de créer de nombreux environnements virtuels et d'y installer des paquets différents. On peut ainsi lancer les tests d'un logiciel dans plusieurs environnements, fournissant des versions différentes de Python et des dépendances, afin de s'assurer que l'application fonctionnera correctement dans diverses configurations. Malheureusement, l'isolation n'est pas parfaite.

1.2 Dépendances

La gestion des dépendances peut parfois laisser à désirer : le problème principal est que pip, qui est utilisé pour installer des paquets au sein d'un environnement virtuel, ne peut gérer que des paquets Python. Voyons quelques exemples.

1.2.1 Erreur à l’installation

(py34)$ pip install cffi

...

c/_cffi_backend.c:15:17: fatal error: ffi.h: No such file or directory

compilation terminated

error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

Une erreur survient : l'installation du paquet cffi comporte en effet une phase de compilation qui requiert la présence de certains fichiers d'en-tête, dont ffi.h. Il convient donc d'installer libffi-dev, qui fournit ffi.h, (sous Debian) avant de chercher à installer cffi, ce qui est problématique pour deux raisons :

- une partie des dépendances sera gérée par apt : l'environnement n'est donc plus vraiment isolé ;

- la commande pip install cffi donnera des résultats différents selon la machine sur laquelle elle sera exécutée, selon que le fichier d'en-tête ffi.h s'y trouve ou non : on ne peut donc pas reproduire de manière fiable un environnement.

1.2.2 Erreur à l’exécution

Le projet OpenStack utilise pbr (Python Build Reasonableness), une bibliothèque dont l'objectif est de simplifier l'écriture du setup.py. Elle nécessite la présence du binaire Git lors de l'exécution ; que se passe-t-il s'il n'est pas disponible ?

(py34)$ git clone https://github.com/openstack/python-keystoneclient

(py34)$ sudo apt-get remove git

(py34)$ cd python-keystoneclient

(py34)$ python setup.py install

...Are you sure that git is installed?

...

(py34)$ echo $?

1

Certes, cet exemple est un peu tiré par les cheveux : pbr est conçu pour être utilisé dans un dépôt Git, il est relativement normal qu'il ne fonctionne pas lorsque Git n'est pas installé. Toutefois, nous voyons ici que le couple pip/virtualenv n'ayant pas connaissance des dépendances à l'exécution et ces dernières pouvant être installées en dehors de l'environnement virtuel, il est possible de supprimer l'une d'entre elles par erreur.

1.2.3 Impact sur les performances : le cas de PyYAML

Essayons maintenant d'exécuter le code suivant, qui charge un grand nombre de fichiers YAML et affiche à l'écran le temps d'exécution :

import os

import timeit

import yaml



try:

   from yaml import CSafeLoader as SafeLoader

except ImportError:

   from yaml import SafeLoader



def test():

   for f in os.listdir('/tmp/yaml-files'):

      yaml.load(f, Loader=SafeLoader)



if __name__ == '__main__':

   print(timeit.timeit("test()",

      setup="from __main__ import test",

      number=1000))

Le paquet yaml permet de charger un fichier grâce à la méthode load en utilisant un module écrit en Python (SafeLoader) ou en C (CSafeLoader) si la bibliothèque libyaml (le paquet libyaml-dev sous Debian) est présente sur le système. Les performances peuvent être radicalement différentes : sur ma machine, le temps d'exécution est passé de 3,5 secondes à 1 seconde en utilisant la version écrite en C !

Des problèmes de performance pourront donc apparaître sur certaines machines et pas d'autres, quand bien même les environnements virtuels seraient similaires : l'état du reste du système a en effet un impact sur le module utilisé pour charger nos fichiers YAML.

1.3 Variables d’environnement

Un des avantages d'un environnement dit « virtuel » est de permettre de reproduire le comportement d'un programme sur de nombreuses machines différentes, notamment afin que tous les développeurs puissent lancer les tests unitaires dans des conditions similaires. Il est toutefois extrêmement facile d'introduire des variations, même en utilisant virtualenv. Ainsi, les variables d'environnement ne sont pas modifiées lors de l'activation d'un environnement virtuel, or elles peuvent changer le fonctionnement d'un programme du tout au tout :

$ LANG=C python3 -c "print('é')"

Unable to decode the command from the command line:

UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 7:

surrogates not allowed


$ LANG=en_US.UTF-8 python3 -c "print('é')"

é

Deux développeurs ayant deux valeurs différentes pour la variable LANG verront donc deux comportements très différents pour ce programme Python pourtant très simple.

2. Construire son environnement avec Guix

Idéalement, nous aimerions conserver les avantages de virtualenv (construire des environnements virtuels, ne pas polluer notre système en y installant des paquets, gérer plusieurs versions d'une bibliothèque) sans rencontrer les problèmes que nous venons de lister. C'est chose possible grâce à GNU Guix !

2.1 Manuellement

GNU Guix est un gestionnaire de paquets fonctionnel dont les avantages et l'architecture sont détaillés dans GNU/Linux Magazine n° 194 pages 46 à 50. Il peut également être utilisé pour offrir des fonctionnalités similaires à celles proposées par virtualenv, grâce à la commande guix environment. Elle prend en argument un ou plusieurs noms de paquets disponibles dans GNU Guix :

$ guix environment python-keystoneclient

Dans GNU Guix, les paquets Python portent les mêmes noms que sur PyPI, mais sont préfixés par python-, pour les paquets Python 3 (la version par défaut), ou par python2-. La commande précédente crée un environnement dans lequel sont disponibles les dépendances du paquet python-keystoneclient. Nous pouvons vérifier que le paquet pytest, requis pour lancer les tests de python-keystoneclient, est bien disponible et a été construit par Guix :

$ python

>>> import pytest

>>> pytest.__file__

'/gnu/store/n7pr4jnw9p51vmyblw41ppimhpg5f9js-profile/lib/python3.4/site-packages/pytest-2.6.1-py3.4.egg/pytest.py'

Nous nous éloignons toutefois légèrement de l'approche de virtualenv, qui permet d'installer une liste de paquets plutôt que leurs dépendances ; on peut retrouver ce comportement en utilisant l'option --ad-hoc :

$ guix environment --ad-hoc python python-keystoneclient

$ python

>>> import keystoneclient

>>>

2.2 Une bien meilleure isolation

Nous pouvons donc reproduire le comportement de virtualenv et retrouver ses avantages en utilisant GNU Guix : nous pouvons en effet utiliser des paquets Python sans les installer réellement sur notre système et nous pouvons en installer plusieurs versions différentes si elles sont empaquetées. Mais qu'avons-nous vraiment à y gagner ?

2.2.1 Gestion complète des dépendances

L'isolation du reste du système est bien meilleure qu'avec virtualenv : en effet, toutes les dépendances sont spécifiées dans un paquet GNU Guix. Si l'on revient rapidement à la première partie de l'article, cela signifie que :

- l'installation de cffi ne peut pas échouer à cause de l'absence du fichier d'en-tête ffi.h : libffi est en effet une dépendance de cffi ;

- les dépendances à l'exécution sont spécifiées, même si ce ne sont pas des paquets Python : le paquet git est une dépendance du paquet python-pbr ;

- le paquet python-pyyaml sera toujours installé avec les extensions C, puisque libyaml est une de ses dépendances.

Nous pouvons vérifier tout cela en étudiant les dépendances des paquets grâce à la commande guix package :

$ guix package -s python-cffi | recsel -p dependencies

dependencies: ... python-pytest-2.6.1 ...


$ guix package -s python-pbr | recsel -p dependencies

dependencies: git-2.6.3 ...


$ guix package -s python-pyyaml | recsel -p dependencies

dependencies: libyaml-0.1.5 ...

2.2.2 Variables d'environnement sous contrôle

Nous avons vu que virtualenv ne modifiait pas les variables d'environnement et que cela pouvait être un problème pour reproduire un environnement correctement. GNU Guix permet de supprimer ces variables grâce à l'option --pure :

$ guix environment --ad-hoc --pure python python-keystoneclient

$ echo $PATH

/gnu/store/36106pz9mjkbp0ikb6f3cqsxfl0mc5ay-profile/bin

$ echo $PYTHONPATH

/gnu/store/36106pz9mjkbp0ikb6f3cqsxfl0mc5ay-profile/lib/python3.4/site-packages

Les variables PATH et PYTHONPATH ne contiennent plus qu'un seul chemin, qui mène aux binaires et aux bibliothèques d'un profil GNU Guix temporaire, dans lequel ne sont installés que les paquets python et python-keystoneclient. Les autres variables d'environnement sont supprimées, à l'exception de HOME, USER, LOGNAME, DISPLAY, TERM, TZ et PAGER.

2.2.3 La cerise sur le gâteau : un conteneur !

Nous venons de voir que la variable PATH était « purifiée » grâce à l'option --pure, ce qui devrait nous empêcher d'appeler une commande installée en dehors de notre environnement virtuel. Toutefois, il est encore possible d'utiliser un chemin absolu :

$ guix environment --ad-hoc --pure python

[env]$ make -v

bash: make: command not found

[env]$ /usr/bin/make -v

GNU Make 4.0

...

Pour pallier ce problème, la commande guix environment fournit une option --container, qui, comme son nom l'indique, permet de placer l'environnement virtuel dans un conteneur :

$ guix environment --ad-hoc --pure --container python

[env]sh-4.3# /usr/bin/make -v

sh: /usr/bin/make: No such file or directory

Nous avons donc désormais un environnement particulièrement bien isolé de notre système.

2.3 Aller plus loin avec guix-tox

De nombreux projets utilisent Tox [2], un outil en ligne de commande permettant de gérer des environnements virtuels, notamment pour lancer les tests unitaires :

$ cd monprojet/

$ tox -l # Listons les environnements définis dans tox.ini

py26

py27

py33

py34

pep8

La commande tox -epy27 utilisera virtualenv afin de créer un environnement virtuel, puis y lancera les tests en utilisant Python 2.7. Le but de cet article n'est pas de faire une présentation complète de Tox, mais il convient d'indiquer que des configurations complexes peuvent être définies dans le fichier tox.ini, ce qui en fait un outil de gestion d'environnements virtuels très pratique.

Malheureusement, Tox utilise virtualenv, ce qui nous expose aux problèmes définis précédemment. Une preuve de concept non officielle remplace virtualenv par tox dans Guix [3]. Un exemple d'utilisation :

$ GUIX_TOX_EXTRA=openssl guix-tox --env=guix -epy34

...

Ran 1133 (+1132) tests in 30.764s

PASSED (id=65, skips=4)

Nul besoin de modifier la configuration. De même, les commandes sont les mêmes,à deux exceptions près :

- l'option --env qui permet de sélectionner le gestionnaire d'environnements virtuels souhaité (guix ou virtualenv);

- la variable d'environnement GUIX_TOX_EXTRA qui permet de spécifier des dépendances non disponibles sur PyPI : ici le binaire openssl, utilisé dans les tests.

Conclusion

Nous avons vu les défauts de virtualenv qui nous empêchent d'obtenir des environnements virtuels de développement réellement isolés du reste de notre machine et parfaitement reproductibles, et nous savons désormais comment corriger ces problèmes grâce à GNU Guix. Toutefois, ce dernier n'est pas un remplacement miraculeux pour le couple pip/virtualenv : en effet, tous les paquets disponibles sur PyPI ne sont pas présents dans GNU Guix. Toutefois, l'approche fonctionnelle nous semble plus robuste que les solutions existantes.

Références

[1] Site officiel de GNU Guix :http://www.gnu.org/s/guix

[2] Site officiel de Tox : https://tox.readthedocs.org/

[3] Dépôt Git de guix-tox : https://git.framasoft.org/Steap/guix-tox


Sur le même sujet

Google Skaffold

Magazine
Marque
GNU/Linux Magazine
Numéro
237
|
Mois de parution
mai 2020
|
Domaines
Résumé

J'aime par-dessus tout les choses simples et, selon moi, si je dois faire la même chose deux fois, c'est qu'il est temps d'automatiser. Quand je développe une application, j'ai régulièrement besoin de la déployer et de la tester ; aussi, j'ai mes astuces. Mais depuis quelque temps, je dois aussi conteneuriser cette même application, et voilà maintenant que je dois également la kuberneteuriser. Et mxxxx !

Apprenez à utiliser kubeadm

Magazine
Marque
GNU/Linux Magazine
Numéro
236
|
Mois de parution
avril 2020
|
Domaines
Résumé

Combien de fois m'avez-vous entendu dire « nous allons utiliser kubeadm pour faire ceci ou faire cela », et puis boum, un kubeadm init plus tard, tout est prêt ? Souvent ? Très souvent ? Trop souvent ? Alors pour une fois, pourquoi ne pas consacrer un article entier à ce merveilleux projet ?

Les alternatives à Git

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
107
|
Mois de parution
mars 2020
|
Domaines
Résumé

Dans le petit monde des gestionnaires de versions concurrentes, il n'existe pas que Git. D'autres projets permettent également de conserver les différentes versions d'un code et cet article permettra d'en faire un petit tour d'horizon.

Git, un tour d’horizon

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
107
|
Mois de parution
mars 2020
|
Domaines
Résumé

Le vocabulaire et les concepts-clés de Git ayant été définis dans le précédent article, nous allons maintenant nous intéresser à la manière dont les concepts présentés se réalisent à l’aide des commandes de l’outil. Bref, passons à la pratique !

Analyse UEFI avec Windbg

Magazine
Marque
MISC
Numéro
108
|
Mois de parution
mars 2020
|
Domaines
Résumé

La dernière étape dans la mise en place d’un environnement de travail complet en UEFI va être l’installation d’un debugger. Celui-ci va permettre de, non seulement faciliter le développement au sein du micrologiciel, mais aussi de comprendre dynamiquement le code source. Dès lors, il sera possible de mieux appréhender ce code afin de l’éditer et créer son propre UEFI.

Par le même auteur

Empaquetez (facilement) votre projet avec upt

Magazine
Marque
GNU/Linux Magazine
Numéro
222
|
Mois de parution
janvier 2019
|
Domaines
Résumé
Les logiciels que nous utilisons sur nos distributions GNU/Linux et *BSD proviennent généralement des dépôts associés à ces dernières. Ils y sont présents, car les mainteneurs de notre OS préféré les ont empaquetés : cela nous évite de devoir recompiler les sources nous-mêmes, de résoudre les problèmes de dépendances ou de parfaire l'intégration avec le reste de notre système. Nous verrons dans cet article comment les empaqueteurs ont réussi à automatiser une partie de leur travail. Nous proposerons ensuite une solution logicielle permettant à toutes les distributions d'empaqueter facilement le code disponible sur des plateformes telles que PyPI, RubyGems ou CPAN.

Faut s’démener au FOSDEM !

Magazine
Marque
GNU/Linux Magazine
Numéro
213
|
Mois de parution
mars 2018
|
Résumé
Le FOSDEM (Free and OpenSource Developers European Meeting) est tellement incontournable qu’une part non négligeable des auteurs de GLMF s’y rend chaque année. De nombreuses mains et points de vue ont donc participé à ce compte-rendu, pour vous faire part du foisonnement de ce week-end intense.

Étendez Pandoc avec Lua

Magazine
Marque
GNU/Linux Magazine
Numéro
199
|
Mois de parution
décembre 2016
|
Domaines
Résumé
Il est parfois nécessaire de convertir un fichier d'un langage de balisage vers un autre : de Markdown vers du HTML, d'Org-mode vers LaTeX, etc. Pandoc est un outil permettant ce type de conversion, et il est capable de gérer un nombre impressionnant de formats différents. Que faire si l'on souhaite utiliser un format ésotérique inconnu de Pandoc ? Il est possible de l'étendre en Lua !