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

NixOS, quand la programmation fonctionnelle rencontre Linux

Magazine
Marque
Linux Pratique
Numéro
117
|
Mois de parution
janvier 2020
|
Domaines
Résumé

NixOS est une distribution basée sur le gestionnaire de paquets Nix et dont toute la configuration se fait à partir d’un fichier. Nous verrons que NiXOS permet de gérer son système de manière reproductible, avec des mises à jour atomiques et des rollbacks, des espaces utilisateurs, l’installation simultanée de plusieurs versions d’un paquet, la configuration fine de chaque composant ainsi qu’un cache binaire, testé et contenant plus de 47 000 paquets. Nous testerons ensuite NixOS dans une machine virtuelle.

Du legacy dans Kubernetes avec Kubevirt

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

Les dissidents ont mon oreille. J'ai toujours aimé les articles qui prenaient les modes à contre-pied, comme d'imaginer abandonner Docker et Kubernetes pour revenir à LXC. Cependant, je ne suis pas convaincu par ce retour en arrière, tant il est possible d'à peu près tout faire avec Kubernetes, comme d'y faire tourner des machines virtuelles !

LXC, l’autre star des conteneurs

Magazine
Marque
Linux Pratique
Numéro
117
|
Mois de parution
janvier 2020
|
Domaines
Résumé

Depuis quelques années, la « star » des conteneurs s’appelle Docker. Tout le monde l'utilise pour presque tout et n'importe quoi. Cependant, il ne faut pas oublier que Docker n'est pas la seule solution de conteneurs disponible pour Linux et qu'il est même arrivé bien tard dans le paysage. Nous allons ici étudier celui qui fut à l'origine, j'ai nommé LXC.

Puppet : External Node Classifiers

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

La gestion de la configuration de votre parc via le fichier site.pp de Puppet peut rapidement devenir une tâche complexe et rébarbative. Un External Node Classifiers (ENC) peut rendre les choses plus simples. Nous allons voir les avantages de cette solution et comment mettre en place un ENC.

Programmation UEFI

Magazine
Marque
MISC
Numéro
107
|
Mois de parution
janvier 2020
|
Domaines
Résumé

La deuxième étape de la compréhension du système UEFI va s’articuler autour de l’implémentation en C avec l’optique de fabriquer sa propre brique UEFI. Le développement va permettre d’illustrer le processus de la chaîne de boot avec pour objectif de mieux appréhender les différentes problématiques de sécurité.

Gérez, protégez et partagez vos mots de passe avec KeePassXC

Magazine
Marque
Linux Pratique
Numéro
117
|
Mois de parution
janvier 2020
|
Domaines
Résumé

Nous stockons de nombreuses informations, pour beaucoup sensibles, et dans des formats différents. Cela fait autant de mots de passe à créer, à retenir et à utiliser. À utiliser pour souvent quotidiennement, il faut donc que leur utilisation soit la plus transparente possible et s’adapte aux différents services clients : données sur une partition chiffrée, site internet, client d’une application bancaire, application en ligne de commandes. Vous utilisez peut-être déjà une extension web pour les sites web : c’est bien, mais cela ne gère pas tous vos besoins en mots de passe, mots de passe qui sont peut-être gérés par une société tierce sur leurs serveurs lorsque vous les rentrez dans l’extension. Dans cet article, nous allons découvrir KeePassXC, un gestionnaire de mots de passe libre qui vous permettra de répondre à tous types de besoins et de ne pas partager vos mots de passe avec une société tierce.

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 !

Filtrez vos courriels avec Python

Magazine
Marque
GNU/Linux Magazine
Numéro
195
|
Mois de parution
juillet 2016
|
Domaines
Résumé
Vous utilisez sans doute IMAP (Internet Message Access Protocol) pour lire votre courrier. N'avez -vous jamais eu envie d'écrire un script utilisant ce protocole afin d'effectuer des opérations courantes (marquer un fil de discussion comme lu, le déplacer, etc.) automatiquement ? C'est possible grâce à Python et son module imaplib, qui fait partie de la bibliothèque standard.