Fonctionnement des bibliothèques d'environnement virtuel

Avez-vous déjà pensé au fonctionnement des bibliothèques d'environnement virtuel en Python? Dans cet article, je propose de me familiariser avec le concept principal utilisé par toutes les bibliothèques pour les environnements, telles que virtualenv, virtualenvwrapper, conda, pipenv.

Au départ, en Python, il n'y avait pas de capacité intégrée pour créer des environnements, et cette fonctionnalité a été implémentée comme un hack. Il s'est avéré que toutes les bibliothèques sont basées sur une fonctionnalité très simple de l'interpréteur python.

Lorsque Python démarre l'interpréteur, il commence à rechercher le répertoire avec les modules (site-packages). La recherche commence par le répertoire parent concernant l'emplacement physique de l'exécutable de l'interpréteur (python.exe). Si le dossier du module n'est pas trouvé, alors Python monte d'un niveau et le fait jusqu'à ce que le répertoire racine soit atteint. Afin de comprendre qu'il s'agit d'un répertoire avec des modules, Python recherche le module os, qui doit être dans le fichier os.py et est requis pour que python fonctionne.

Imaginons que notre interprète se trouve dans /usr/dev/lang/bin/python . Ensuite, les chemins de recherche ressembleront à ceci:

 /usr/dev/lang/lib/python3.7/os.py /usr/dev/lib/python3.7/os.py /usr/lib/python3.7/os.py /lib/python3.7/os.py 

Comme vous pouvez le voir, Python ajoute un préfixe spécial ( lib/python$VERSION/os.py ) à notre chemin. Dès que l'interpréteur trouve la première correspondance (la présence du fichier os.py), il change sys.prefix et sys.exec_prefix en ce chemin (avec le préfixe supprimé). Si, pour une raison quelconque, aucune correspondance n'est trouvée, le chemin standard est utilisé, qui est compilé dans l'interpréteur.

Voyons maintenant comment fonctionne l'une des bibliothèques les plus anciennes et les plus célèbres, virtualenv.

 user@arb:/usr/home/test# virtualenv ENV Running virtualenv with interpreter /usr/bin/python3 New python executable in /usr/home/test/ENV/bin/python3 Also creating executable in /usr/home/test/ENV/bin/python Installing setuptools, pkg_resources, pip, wheel...done. 

Après exécution, il crée des répertoires supplémentaires:

 user@arb:/usr/home/test/ENV# tree -L 3 . ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── activate_this.py │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.7 │ ├── python │ ├── python-config │ ├── python3 -> python │ ├── python3.7 -> python │ └── wheel ├── include │ └── python3.7m -> /usr/include/python3.7m ├── lib │ └── python3.7 │ ├── __future__.py -> /usr/lib/python3.7/__future__.py │ ├── __pycache__ │ ├── _bootlocale.py -> /usr/lib/python3.7/_bootlocale.py │ ├── _collections_abc.py -> /usr/lib/python3.7/_collections_abc.py │ ├── _dummy_thread.py -> /usr/lib/python3.7/_dummy_thread.py │ ├── _weakrefset.py -> /usr/lib/python3.7/_weakrefset.py │ ├── abc.py -> /usr/lib/python3.7/abc.py │ ├── base64.py -> /usr/lib/python3.7/base64.py │ ├── bisect.py -> /usr/lib/python3.7/bisect.py │ ├── codecs.py -> /usr/lib/python3.7/codecs.py │ ├── collections -> /usr/lib/python3.7/collections │ ├── config-3.7m-darwin -> /usr/lib/python3.7/config-3.7m-darwin │ ├── copy.py -> /usr/lib/python3.7/copy.py │ ├── copyreg.py -> /usr/lib/python3.7/copyreg.py │ ├── distutils │ ├── encodings -> /usr/lib/python3.7/encodings │ ├── enum.py -> /usr/lib/python3.7/enum.py │ ├── fnmatch.py -> /usr/lib/python3.7/fnmatch.py │ ├── functools.py -> /usr/lib/python3.7/functools.py │ ├── genericpath.py -> /usr/lib/python3.7/genericpath.py │ ├── hashlib.py -> /usr/lib/python3.7/hashlib.py │ ├── heapq.py -> /usr/lib/python3.7/heapq.py │ ├── hmac.py -> /usr/lib/python3.7/hmac.py │ ├── imp.py -> /usr/lib/python3.7/imp.py │ ├── importlib -> /usr/lib/python3.7/importlib │ ├── io.py -> /usr/lib/python3.7/io.py │ ├── keyword.py -> /usr/lib/python3.7/keyword.py │ ├── lib-dynload -> /usr/lib/python3.7/lib-dynload │ ├── linecache.py -> /usr/lib/python3.7/linecache.py │ ├── locale.py -> /usr/lib/python3.7/locale.py │ ├── no-global-site-packages.txt │ ├── ntpath.py -> /usr/lib/python3.7/ntpath.py │ ├── operator.py -> /usr/lib/python3.7/operator.py │ ├── orig-prefix.txt │ ├── os.py -> /usr/lib/python3.7/os.py │ ├── posixpath.py -> /usr/lib/python3.7/posixpath.py │ ├── random.py -> /usr/lib/python3.7/random.py │ ├── re.py -> /usr/lib/python3.7/re.py │ ├── readline.so -> /usr/lib/python3.7/lib-dynload/readline.cpython-37m-darwin.so │ ├── reprlib.py -> /usr/lib/python3.7/reprlib.py │ ├── rlcompleter.py -> /usr/lib/python3.7/rlcompleter.py │ ├── shutil.py -> /usr/lib/python3.7/shutil.py │ ├── site-packages │ ├── site.py │ ├── sre_compile.py -> /usr/lib/python3.7/sre_compile.py │ ├── sre_constants.py -> /usr/lib/python3.7/sre_constants.py │ ├── sre_parse.py -> /usr/lib/python3.7/sre_parse.py │ ├── stat.py -> /usr/lib/python3.7/stat.py │ ├── struct.py -> /usr/lib/python3.7/struct.py │ ├── tarfile.py -> /usr/lib/python3.7/tarfile.py │ ├── tempfile.py -> /usr/lib/python3.7/tempfile.py │ ├── token.py -> /usr/lib/python3.7/token.py │ ├── tokenize.py -> /usr/lib/python3.7/tokenize.py │ ├── types.py -> /usr/lib/python3.7/types.py │ ├── warnings.py -> /usr/lib/python3.7/warnings.py │ └── weakref.py -> /usr/lib/python3.7/weakref.py └── pip-selfcheck.json 

Comme vous pouvez le voir, l'environnement virtuel a été créé en copiant le binaire Python dans un dossier local (ENV / bin / python). Nous pouvons également remarquer que le dossier parent contient des liens symboliques vers les fichiers de bibliothèque standard python. Nous ne pouvons pas créer de lien symbolique vers le fichier exécutable, car l'interpréteur le renommera toujours en chemin réel.

Activons maintenant notre environnement:

 user@arb:/usr/home/test# source ENV/bin/activate 

Cette commande modifie la variable d'environnement $ PATH afin que la commande python pointe vers notre version locale de python. Ceci est réalisé en remplaçant le chemin local du dossier bin au début de la ligne $ PATH afin que le chemin local ait priorité sur tous les chemins à droite.

 export "/usr/home/test/ENV/bin:$PATH" echo $PATH 

Si vous exécutez le script à partir de cet environnement, il sera exécuté en utilisant le binaire dans /usr/home/test/ENV/bin/python . L'interpréteur utilisera ce chemin comme point de départ pour rechercher des modules. Dans notre cas, les modules de la bibliothèque standard se trouvent sur le chemin /usr/home/test/ENV/lib/python3.7/ .

C'est le hack principal, grâce auquel toutes les bibliothèques pour travailler avec des environnements virtuels fonctionnent.

Améliorations de Python 3


À partir de la version Python 3.3, un nouveau standard est apparu, appelé PEP 405 , qui introduit un nouveau mécanisme pour les environnements légers.

Ce PEP ajoute une étape supplémentaire au processus de recherche. Si vous créez le pyenv.cfg configuration pyenv.cfg , au lieu de copier le binaire Python et tous ses modules, vous pouvez simplement indiquer leur emplacement dans cette configuration.

Cette fonctionnalité est activement utilisée par le module venv standard, qui est apparu dans Python 3.

 user@arb:/usr/home/test2# python3 -m venv ENV user@arb:/usr/home/test2# tree -L 3 . └── ENV ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.5 │ ├── python -> python3 │ └── python3 -> /usr/bin/python3 ├── include ├── lib │ └── python3.7 ├── lib64 -> lib ├── pyvenv.cfg └── share └── python-wheels 

 user@arb:/usr/home/test2# cat ENV/pyvenv.cfg home = /usr/bin include-system-site-packages = false version = 3.7.0 user@arb:/usr/home/test2# readlink ENV/bin/python3 /usr/bin/python3 

Grâce à cette configuration, au lieu de copier le binaire, venv crée simplement un lien vers celui-ci. Si le paramètre include-system-site-packages changé en true , tous les modules de la bibliothèque standard seront automatiquement accessibles à partir de l'environnement virtuel.

Malgré ces changements, la plupart des bibliothèques tierces pour travailler avec des environnements virtuels utilisent l'ancienne approche.

PS: je suis l'auteur de cet article, vous pouvez poser toutes vos questions.

Source: https://habr.com/ru/post/fr418579/


All Articles