Во время разработки проектов на Python вы часто будете использовать готовые модули, такие, как, например библиотеки по типу requests, numpy и другие.
Также вам, возможно, понадобится дописать модули к готовому проекту, не изменяя его исходный код.
В этой статье будут рассмотрены создание и установка Python-пакетов, а также pip и venv, как инструменты для управления зависимостями.
Примеры команд из статьи приведены для Unix/MacOS.
Введение в терминолгию
Пакет (Python package) - модуль с кодом, который можно установить как зависимость для вашего проекта.
pip - утилита для управления пакетами.
venv (или virtualenv в python2) - система для создания изолированной среды для хранения пакетов.
PyPI - репозиторий для публикации Python-пакетов.
Создание и публикация пакетов
Для начала разберемся с тем, как на практике реализуется создание пакета.
Возьмем готовый проект, из которого вы хотите создать пакет. Его структура будет выглядеть примерно так:
packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│ └── example_package_YOUR_USERNAME_HERE/
│ ├── __init__.py
│ └── example.py
└── tests/
Нетрудно заметить, что помимо директорий с кодом и тестами (src и tests) в корне проекта имеются 3 файла. Среди них файл с описанием проекта README, файл с описанием лицензированием пакета, а также файл pyproject.toml.
В современных версиях pip этот файл является стандартом для задания метаинформации о пакете. В более ранних версиях стандартом был файл setup.py, который все еще может понадобиться для установки пакета в режиме разработки (для редактирования его файлов после установки).
Наиболее важной частью его содержимого является информация, необходимая для сборки пакета:
[build-system]
requires = [
"hatchling",
"my_pkg_name @ git+ssh://git@github.com/my-github-name/my_repo"]
build-backend = "hatchling.build"
В поле requires задается список пакетов, являющихся build-зависимостями вашего проекта. Например, можно использовать пакеты из PyPI, пакеты из git репозиториев, пакеты из архивов и.т.д.
В поле build-backend выбирается система сборки для пакета. На начальном уровне в целом не имеет значения, какую систему использовать: setuptools, hatchling, flit или прочие.
Помимо информации, необходимой для сборки, файл также содержит множество различных метаданных о проекте: его название, текущую версию, авторов, различные ссылки и.т.д.
[project]
name = "example_package_YOUR_USERNAME_HERE"
version = "0.0.1"
authors = [
{ name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
[project.urls]
"Homepage" = "https://github.com/pypa/sampleproject"
"Bug Tracker" = "https://github.com/pypa/sampleproject/issues"
Из важного: если вы планируете выложить пакет в репозиторий PyPI, в поле name вам необходимо указать уникальное название для пакета. Остальная информация является скорее опциональной.
Для более подробного ознакомления с форматом содержимого можно прочитать официальную спецификацию.
Для сборки пакета необходимо выполнить следующие команды из корня проекта:
python3 -m pip install --upgrade build
python3 -m build
В директории dist появится 2 файла: .whl - собранный пакет, и архив .tar.gz - сжатый архив с проектом. Оба файла будут необходимы в дальнейшем для установки пакета: система управления пакетами сначала попытается использовать .whl, но при необходимости скачает архив и выполнит сборку .whl уже на устройстве пользователя.
python3 -m pip install --upgrade twine
python3 -m twine upload --repository testpypi dist/*
В результате ваш пакет будет доступен по ссылке https://test.pypi.org/project/example_package_YOUR_USERNAME_HERE, где последняя часть будет эквивалентна названию, заданному в pyproject.toml.
Подробнее о пакетах
Установка пакетов через pip и venv
pip - стандартный пакетный менеджер Python, используемый для установки и обновления пакетов. Для понимания базовых механик работы pip рассмотрим наиболее используемый скрипт pip install.
Аргументы скрипта - пары {пакет, ограничения на версию}, описывающие необходимые для установки пакеты, либо, при указании ключа -r, аргументом становится путь до текстового файла с описанием зависимостей в аналогичной форме.
При его запуске последовательно запускается несколько этапов для установки заданных зависимостей.
Во-первых, pip определяет, в каком формате ему задана зависимость. Это может быть ссылка на git или архив, .whl файл, локальная директория и.т.д.
Для удовлетворения зависимости pip необходимо узнать ее имя и версию. Из названия .whl файлов эти параметры определяются однозначно, для локальных директорий используется команда setup.py egg_info.
Далее pip выбирает наиболее актуальную версию пакета, удовлетворяющую заданным ограничениям (>=min_version & <=max_version).
Наконец, pip устанавливает runtime зависимости в порядке зависимости пакетов друг от друга снизу вверх.
По умолчанию пакеты устанавливаются в директорию /usr/bin/pythonX.Y/site-packages, где X.Y - версия Python, установленная в системе как дефолтная.
Очевидна главная проблема этого подхода: нет возможности для разных проектов установить одинаковый пакет с различными версиями. Для этого в Python существует механизм виртуальной среды исполнения venv (или vitualenv в python2). Создать venv для проекта можно запустив следующие команды в директории проекта:
python3 -m pip install --user virtualenv
python3 -m venv env
При создании venv в директорию env скопируются все необходимые исполняемые файлы, фактически эмулируя директорию интерпретатора по-умолчанию.
Также по в директории env создастся скрипт bin/activate, который позволяет установить venv как интерпретатор по-умолчанию для текущей сессии терминала, выполнив:
source env/bin/activate
Теперь установленные пакеты будут храниться в env/lib/pythonX.Y/site-packages, не конфликтуя с пакетами, установленными в директории дефолтного интерпретатора.
Подробнее об установке зависимостей