Главная » Хабрахабр » [Перевод] Как работают библиотеки виртуального окружения

[Перевод] Как работают библиотеки виртуального окружения

Вы когда-нибудь задумывались о том, как работают библиотеки виртуального окружения в Python? В этой статье я предлагаю ознакомится с главной концепцией, которую используют все библиотеки для окружений, такие как virtualenv, virtualenvwrapper, conda, pipenv.

Как оказалось, все библиотеки базируются на очень простой особенности интерпретатора питона. Изначально, в Python не было встроенной возможности создавать окружения, и такая возможность была реализована в виде хака.

Поиск начинается с родительской директории относительно физического расположения исполняемого файла интерпретатора (python.exe). Когда Python запускает интерпретатор, он начинает искать директорию с модулями (site-packages). Для того, чтобы понять, что это директория с модулями, Python ищет модуль os, который должен лежать в файле os.py и является обязательным для работы питона.
Давайте представим, что наш интерпретатор располагается по адресу /usr/dev/lang/bin/python. Если папка с модулями не найдена, то Python переходит на уровень выше, и делает это до тех пор, пока не будет достигнута корневая директория. Тогда пути поиска будут выглядеть так:

/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

Как вы можете видеть, Python добавляет специальный префикс (lib/python$VERSION/os.py) к нашему пути. Как только интерпретатор находит первое совпадение (наличие файла os.py), он изменяет sys.prefix и sys.exec_prefix на этот путь (с удаленным префиксом). Если по каким-то причинам совпадений не найдено, то используется стандартный путь, который вкомпилирован в интерпретатор.

Теперь давайте посмотрим как это делает одна из самых старых и известных библиотек — 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.

После выполнения, она создает дополнительные директории:

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

Как вы можете видеть, виртуальное окружение было создано путём копирования бинарника Python в локальную папку (ENV/bin/python). Так же мы можем заметить, что родительская папка содержит символические ссылки на файлы стандартной библиотеки питона. Мы не можем создать символическую ссылку на исполняемый файл, т.к. интерпретатор всё равно разименует её до фактического пути.

Теперь давайте активируем наше окружение:

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

Эта команда меняет переменную окружения $PATH, таким образом, чтобы команда python указывала на нашу локальную версию питона. Это достигается путём подстановки локального пути папки bin в начало строки $PATH, чтобы локальный путь имел приоритет перед всеми путями справа.

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

Если вы запустите скрипт из этого окружения, то он выполнится с помощью бинарника по адресу /usr/home/test/ENV/bin/python. Интерпретатор будет использовать этот путь как стартовую точку для поиска модулей. В нашем случае, модули стандартной библиотеки будут найдены по пути /usr/home/test/ENV/lib/python3.7/.

Это основной хак, благодаря которому работают все библиотеки для работы с виртуальными окружениями.

Улучшения в Python 3

Начиная с версии Python 3.3, появился новый стандарт, именуемый как PEP 405, который вводит новый механизм для легковесных окружений.

Если создать файл конфигурации pyenv.cfg, то вместо копирования бинарника Python и всех его модулей, можно просто указать их расположение в этом конфиге. Этот PEP добавляется дополнительный шаг к процессу поиска.

Эту фичи активно использует стандартный модуль venv, который появился в 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

Благодаря этому конфигу, вместо копирования бинарника, venv просто создает ссылку на него. Если параметр include-system-site-packages изменить на true, то все модули стандартной библиотеки будут автоматически доступны из виртуального окружения.

Несмотря на данные изменения, большинство сторонних библиотек для работы с виртуальными окружениями использует старый подход.

S.: Я являюсь автором этой статьи, можете задавать любые вопросы. P.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 18: «Частный просмотр интернета», часть 1

Массачусетский Технологический институт. Курс лекций #6.858. «Безопасность компьютерных систем». Николай Зельдович, Джеймс Микенс. 2014 год Computer Systems Security — это курс о разработке и внедрении защищенных компьютерных систем. Лекции охватывают модели угроз, атаки, которые ставят под угрозу безопасность, и методы ...

[Из песочницы] Как я понял, что ем много сладкого, или классификация товаров по чекам в приложении

Задача В этой статье мы хотим рассказать, как мы создали решение для классификации названий продуктов из чеков в приложении для учёта расходов по чекам и помощника по покупкам. Мы хотели дать пользователям возможность просматривать статистику по покупкам, собранную автоматически на ...