Доклад подготовлен в рамках курса ФКН НИУ ВШЭ "Язык программирования Python (углубленный курс)".
Система управления пакетами (или же «пакетный менеджер») — инструмент, позволяющий управлять процессом установки, обновления и удаления различных компонентов программного обеспечения, например, библиотек для Python, которые вы можете использовать в работе над собственной программой. Одним из таких менеджеров является Poetry. Позволим Poetry представить себя самостоятельно [docs]:
Poetry — это инструмент для управления зависимостями в Python. Он позволяет фиксировать библиотеки, от которых зависит ваш проект, и он будет управлять (устанавливать / обновлять) их для вас. Poetry использует lockfile для обеспечения воспроизводимой установки зависимостей и может упаковать ваш проект для распространения.
Ни добавить, ни убавить. Вам нужен NumPy / pandas / Django / <что-нибудь еще>? Тогда Poetry спешит к вам.
«Стоп», — возможно, скажете вы, — «но я все время делал(-a) что-то вроде pip install package_name
», — и будете совершенно правы. Pip — это тоже пакетный менеджер, да еще какой, ведь он устанавливается вместе с Python (практически всегда) по умолчанию. Более того, pip является старичком в своем деле. Впервые он был представлен в 2008 году, а в релиз с версией 1.0 вышел в 2011 [proof]. С тех пор прошло много лет, прежде чем в 2018 году [check here] свет увидела самая первая версия Poetry, 0.1.0.
Сегодня Poetry подрос, окреп и собрал целых 27 500 звезд на GitHub.
Но у pip побольше?
А вот и нет — 9 000. Конечно, это не говорит о том, что Poetry используется разработчиками чаще, чем встроенный pip, но как минимум говорит о том, что «фанатов» у первого больше.Чем же обусловлены появление и взлет Poetry? Конечно, причин несколько. На этом этапе мы не будем вдаваться в подробности, но посмотрим на слабые места pip и на альтернативу, которую предлагает Poetry.
Pip хранит список зависимостей проекта в файле с расширением *.txt
. Этот файл принято называть requirements.txt, его можно встретить во многих проектах.
Пример содержимого requirements.txt
fastapi==0.103.2
uvicorn[standard]==0.23.2
Все просто и, кажется, логично: название библиотеки, рядом используемая версия. Теперь посмотрим, как это делает Poetry. Во-первых, расширение файла меняется на *.toml
, а сам файл носит название pyproject.toml. Во-вторых, файл значительно подробнее:
Пример содержимого pyproject.toml
[tool.poetry]
name = "poetry-presentation"
version = "0.1.0"
description = "A very cool description"
authors = ["Grigory Yolkin <[email protected]>",]
license = "MIT"
readme = "readme.md"
[tool.poetry.dependencies]
python = "^3.8"
fastapi = "^0.103.2"
uvicorn = {extras = ["standard"], version = "^0.23.2"}
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Помимо самих зависимостей под tool.poetry.dependencies
, можно также заметить tool.poetry
и build-system
. Об их предназначении поговорим позже, а пока сделаем главный вывод, который напрашивается после увиденного: Poetry в одном месте хранит зависимости и некоторую другую информацию о проекте, что может быть удобно в ряде случаев.
Надо признать, файл pyproject.toml носит весьма общее название. Кажется, он не так прост, как кажется из примера выше. Так и есть! Сила pyproject.toml в том, что этот файл в качестве файла конфигурации помимо Poetry используют такие утилиты, как black, mypy, ruff, coverage, pytest и другие. Это значит, что в том же месте помещается ряд полезных настроек проекта, отвечающих за зависимости, форматирование, линтинг, тестирование кода и т.д.
Еще одна актуальная для многих разработчиков проблема заключается в необходимости разделения зависимостей. Вот, например, утилита black, использующаяся для форматирования кода, очень полезна в процессе разработки, но не нужна в продакшене. Мы можем установить зависимости из файла при помощи pip вот так:
pip install -r requirements.txt
То есть рекурсивно устанавливаем каждую зависимость из requirements.txt, никак не разделяя пакеты, необходимые в продакшене, при разработке или тестировании проекта. Нехорошо, особенно если таких зависимостей много. Poetry предлагает филигранное решение проблемы, о котором мы вскоре обязательно поговорим.
Использование Poetry выглядит более гибким решением для управления зависимостями в вашем проекте. Выше приведены не все отличия, однако, кажется, Poetry достаточно хорош, чтобы о нем знать и иметь в арсенале инструментов, которые вы сможете уверенно использовать в случае необходимости.
Сначала посмотрим на уже известные нам из примера выше tool.poetry
и build-system
. После разберемся с остальными тегами, которые вы можете встретить или внести в pyproject.toml.
Этот тег не имеет прямого отношения к Poetry, поэтому не будем на нем долго останавливаться. Стоит лишь сказать, что под ним мы указываем, какие инструменты сборки проекта использовать, существуют различные инструменты — среди них и Poetry. Стандартный вариант, который рекомендуется указывать, если вы используете этот пакетный менеджер:
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Если вам интересно узнать больше про build-system, то вы можете прочитать об этом в PEP 517.
Ключевой для Poetry тег в pyproject.toml. Он содержит в себе несколько секций, настраивает Poetry для вашего проекта в целом.
Обязательное поле, означающее название вашего проекта. PyPI (Python Package Index) ограничивает названия такой регуляркой, но без учета регистра:
^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$
Если из регулярного выражения вам не понятно, что можно, а что нельзя, то можете воспользоваться следующим кодом для проверки:
import re
pattern = r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'
project_title = "string"
print(bool(re.fullmatch(pattern, project_title, re.IGNORECASE)))
Обязательное поле, означающее версию проекта. Значение должно быть строкой наподобие "0.1.0"
. Но возможны и менее стандартные варианты, если это необходимо, например, "1.0.0.post1"
. Подробнее про это можно прочитать в PEP 440.
Обязательное поле, означающее описание проекта. Описание не должно быть длинным, желательно вообще уместить его в одно предложение.
Обязательное поле. Представляет из себя список авторов проекта, перечисленных через запятую в формате name <email>
.
Опциональное поле, означающее лицензию продукта. Наиболее популярные Apache 2.0 и MIT. Это поле можно не заполнять, однако крайне рекомендуется это сделать, если вы создаете open-source проект. Подробнее про лицензии можете почитать на Хабре. Статья была написана в далеком 2014 году, но не потеряла в актуальности.
Опциональное поле. Представляет из себя список людей, поддерживающих проект (англ. maintainer — хранитель, сопровождающий). Люди в списке должны отличаться от авторов, перечисляются в таком же формате.
Опциональное поле, указывающее путь до README-файла. Может принимать список путей, если таких файлов несколько. Если вы собираетесь опубликовать результат своей работы в PyPI, то рекомендуется заполнить это поле. Файл README может содержать в себе различную информацию, например, намного более длинное и детальное описание, чем в description.
Опциональные поля, содержащие ссылки на веб-страницу проекта, гит-репозиторий или документацию соответственно.
Опциональное поле, содержащее список ключевых слов, связанных с проектом.
Опциональные поле, содержащее список PyPI классификаторов проекта.
Опциональное поле, содержащее список пакетов и модулей, которые должны быть включены в окончательный дистрибутив вашего проекта. Это специфическое поле, предусматривающее вариативные варианты включения информации и дополнительных пакетах и модулях, поэтому лучше прочитать об этом подробнее самостоятельно в документации Poetry.
Итак, тег tool.poetry
может содержать в себе разнообразные данные, которые можно включать или исключать по своему усмотрению. Если вы не планируете публиковать проект в PyPI или выкладывать его в публичный доступ, но используете Poetry, то можно обойтись самым минимумом информации. Если же ваша цель заключается в том, чтобы показать свое творение миру, то, наверное, подробности не помешают — это сделает проект более прозрачным и подскажет другим разработчикам, в каком направлении он двигается.
Poetry позволяет разделить список зависимостей вашего проекта по группам. Must-have зависимости, которые необходимы проекту в продакшене, указываются в tool.poetry.dependencies
, а вот с зависимостями для разработки возможно некоторое творчество. Например, представим, что мы используем type-checker и форматер — mypy и black, для тестирования — pytest, а mkdocs для документирования проекта. Тогда в pyproject.toml можно разбить dev-зависимости на группы в зависимости от их предназначения:
[tool.poetry.dependencies]
# основные зависимости
[tool.poetry.group.test.dependencies]
pytest = "*"
pytest-mock = "*"
[tool.poetry.group.docs.dependencies]
mkdocs = "*"
[tool.poetry.group.tools.dependencies]
black = "*"
mypy = "*"
Это особенно полезно, когда может потребоваться быстро установить зависимости только из конкретной группы. С группами мы еще поработаем, пока просто важно знать, что они есть.
Под этим тегом вы можете описать любые сценарии или скрипты, которые необходимо воспроизвести при установке пакета. Если, например, у вас в проекте есть какая-то консольная утилита, которая генерирует своеобразный dependency report (указывая на уязвимости, актуальные обновления и т.д), то ее может быть очень кстати запускать, используя tool.poetry.scripts
.
Список дополнительных зависимостей, которые можно установить, а можно и не устанавливать. Можно также собрать кластеры зависимостей, т.е. несколько дополнительных зависимостей, объединенных под одним кодовым словом для быстрой комплексной установки. В документации Poetry приводится такой пример:
[tool.poetry.dependencies]
# ...
psycopg2 = { version = "^2.9", optional = true }
mysqlclient = { version = "^1.3", optional = true }
[tool.poetry.extras]
mysql = ["mysqlclient"]
pgsql = ["psycopg2"]
databases = ["mysqlclient", "psycopg2"]
Это может быть полезно при работе с различными СУБД, администраторскими панелями и инструментами кастомизации кода.
Poetry поддерживает встраивание различных плагинов. В общем виде синтаксис определения плагина в файле pyproject.toml выглядит следующим образом:
[tool.poetry.plugins] # опционально
[tool.poetry.plugins."A"]
"B" = "C:D"
- A: тип плагина,
- B: название плагина,
- C: путь к соответствующему Python-модулю,
- D: entry point плагина (т.е. функция или класс, которые его запускают).
Взглянем на конкретный пример, чтобы лучше понять, как это работает:
[tool.poetry.plugins."poetry.application.plugin"]
poetry-dotenv-plugin = "poetry_dotenv_plugin.dotenv_plugin:DotenvPlugin"
В данном случае мы добавляем в проект Poetry Dotenv Plugin. Его предназначение в том, чтобы автоматически загружать переменные окружения из .env
файла перед выполнением команд Poetry. Больше плагинов вы можете найти, например, на GitHub. Среди них есть много тех, что заслуживают внимания и действительно могут прокачать ваш DX при работе с проектом.
Под этим тегом можно указать какие-то дополнительные URL, кроме упомянутых выше homepage
, repository
или documentation
. После публикации в PyPI все ссылки из этого раздела попадут в секцию Project Links
.
Вкратце резюмируем, что нам известно о Poetry на данный момент:
- Это пакетный менеджер, помогает управлять зависимостями в вашем проекте;
- Он мощнее, чем pip, которым вам, возможно, уже приходилось пользоваться;
- Poetry конфигурируется в общем для разных утилит файле pyproject.toml и представляет возможность использовать весьма тонкие настройки.
Впечатляюще. Однако, чтобы попробовать Poetry, его нужно сначала установить. Может, это покажется забавным, но проще всего сделать это через pip (в глобальном окружении):
pip install poetry
Тем не менее этот способ не рекомендуется. В документации приводятся следующие способы установить Poetry: через pipX, официальный установщик, ручками. Можете выбрать наиболее удобный для вас и выполнить установку, обычно это не занимает много времени.
Конечно, руками создавать и заполнять pyproject.toml необходимости нет. Установив Poetry в систему, мы получаем возможность использовать консольные команды (CLI) для работы с пакетными менеджером. Прямо так, как раньше мы это делали с pip. Воспользуемся магической командой:
poetry new poetry-demo
И, после непродолжительного ожидания, получим папку poetry-demo
со следующим содержимым:
poetry-demo
├── pyproject.toml
├── README.md
├── poetry_demo
│ └── __init__.py
└── tests
└── __init__.py
Poetry подготовил для нас структуру проекта, которая включает в себя README файл, тот самый pyproject.toml с базовыми настройками, папку poetry_demo (по названию проекта, которое мы указали) и папку tests.
А что насчет существующего проекта?
Конечно, чтобы использовать Poetry необязательно все начинать с чистого листа. Добавить этот пакетный менеджер можно и в уже существующий проект. В корне необходимо выполнить следующую команду:poetry init
Poetry предложит заполнить основную информацию о проекте и перечислить используемые зависимости (по желанию, это можно будет сделать позже) для генерации pyproject.toml файла.
По умолчанию Poetry создаст папку с виртуальным окружением в {cache-dir}/virtualenvs
. В Windows, например, полный путь из диска C будет выглядеть как-то так: C:/Пользователи/User/AppData/Local/pypoetry/Cache/virtualenvs/*
. Это может быть несколько непривычно для тех, кто привык генерировать venv
(или env
, или как-то еще) папку прямо в папке проекта. Зато это освобождает от необходимости каждый раз записывать папку с виртуальным окружением в .gitignore
. Тем не менее в этом аспекте Poetry сохраняет приверженность гибкости и позволяет изменить стандартный путь, установив новый путь в качестве значения для переменной окружения POETRY_CACHE_DIR
[подробности].
Poetry предоставляет десятки комбинаций различных команд, некоторые из них используется достаточно редко, а рассмотреть все команды, настройки и параметры невозможно. Мы остановимся лишь на основных из них, которых вполне хватит для того, чтобы комфортно работать, обращаясь к документации, когда нужно больше.
Шаблон команды в консоли выглядит стандартно:
poetry <command>
Если вы работали с pip, то можете неправильно воспринимать команду poetry install
— здесь она служит инструментом для установки всех зависимостей, перечисленных в pyproject.toml, а не для установки какого-то конкретного пакета.
Выше мы говорили о том, что Poetry позволяет гибко определять и устанавливать зависимости. Это достигается в том числе за счет команды install, поскольку можно установить флаг --without
, чтобы установить зависимости, исключая все зависимости из каких-либо групп. Например, если у вас есть группы зависимостей test и docs, установить зависимости без этих групп можно следующим образом:
poetry install --without test,docs
Кроме того, есть флаги --only
, --with
и --only-root
. Флаг --only
служит для установки зависимостей только из определенных групп. Флаг --with
для выбора групп зависимостей, которые мы хотим установить. Флаг --only-root
позволит установить сам проект, без зависимостей.
После установки зависимостей будет создан файл poetry.lock, который содержит информацию о них, а также их зависимостей.
Одной из сильных сторон Poetry является возможность автоматически обновлять зависимости вашего проекта, решая при этом возможные конфликты.
poetry update
Команда поддерживает уже известные нам флаги --only
, --without
и --with
. Алгоритм работы у нее простой: сначала обновляется lockfile, затем все зависимости устанавливаются в проект (устаревшие заменяются на новые). Если вы не хотите автоматически устанавливать все зависимости, но хотите обновить lockfile, то можно применить флаг --lock
.
Эту команду можно назвать аналогом команды pip install
. Однако она не только устанавливает требуемый пакет в виртуальное окружение, но и добавляет запись о новой зависимости в pyproject.toml. Кроме того, команда поддерживает специфический синтаксис, позволяя гибко определять необходимые зависимости:
# Различные варианты установки pendulum
poetry add "pendulum>=2.0.5"
poetry add pendulum==2.0.5
poetry add pendulum@latest
poetry add git+https://github.com/sdispater/pendulum.git
# Установка mkdocs в группу зависимостей docs
poetry add mkdocs --group docs
В общем случае хватит выполнить просто:
poetry add pendulum
Если хотите оценить все возможности Poetry в вопросе установки зависимостей, то смело обращайтесь к этому разделу документации.
Команда для удаления зависимости из проекта. Можно указать флаг --group
, как и в случае с add, чтобы удалить зависимость из конкретной группы.
poetry remove pendulum
Как и другие команды, автоматически обновляет poetry.lock и pyproject.toml.
Еще одна интуитивно понятная команда! poetry show
выводит список имеющихся зависимостей, это своеобразный аналог pip list
. Кстати, можно вывести довольно детальную информацию о конкретной зависимости:
poetry show pendulum
# пример вывода
name : pendulum
version : 1.4.2
description : Python datetimes made easy
dependencies
- python-dateutil >=2.6.1
- tzlocal >=1.4
- pytzdata >=2017.2.2
required by
- calendar >=1.4.0
# Выведет дерево зависимостей проекта, иногда полезная вещь
poetry show --tree
Сразу поговорим о list
, чтобы не путать команду с уже упомянутым pip list
. В Poetry эта команда служит чем-то вроде типичной справки: выводит список всех команд Poetry. Кстати, очень рекомендуем изучить его.
С помощью данной команды вы можете выполнить какую-то другую команду внутри виртуального окружения проекта. Также вы можете выполнить скрипт, определенный в [tool.poetry.scripts]
.
poetry run python -V
# или
poetry run black .
Команда для создания виртуального окружения для проекта, если оно еще не создано. Автоматически активирует виртуальное окружение.
poetry shell
Активировать виртуальное окружение при последующей работе над проектом можно с помощью команды poetry env use python
, Poetry все сделает за вас. Более подробная информация про команду env
и управление виртуальными окружениями содержится в документации Poetry.
Пара специфических команд, необходимых для тех, кто работает над open-source пакетом, который в будущем планирует опубликовать. Так, дистрибутив простого проекта можно собрать командой poetry build
:
poetry build
# пример вывода
Building bye (0.1.0)
- Building sdist
- Built bye-0.1.0.tar.gz
- Building wheel
- Built bye-0.1.0-py3-none-any.whl
Команда poetry publish
автоматически отправит собранный дистрибутив в удаленный репозиторий, по умолчанию это PyPI. Команда предусматривает использование таких флагов, как --username
, --password
, --cert
и других, чтобы загрузка произошла успешно. Подробнее про них можно прочитать в документации.
Может показаться, что Poetry — превосходное решение, лишенное всяких недостатков, что, конечно, неверно. Poetry точно можно назвать all-in-one solution, но не идеальным и безупречным подходом к управлению зависимостями в проекте. Некоторые разработчики критикуют Poetry, часто эта критика небеспричинна. Например, на YouTube есть видеоролик, который называется why I will never use python-poetry. Его стоит посмотреть, если вы собираетесь использовать Poetry. Автор критикует пакетный менеджер по нескольким линиям, среди которых:
- Большое количество зависимостей у самого Poetry (около 50 штук). Вы можете взглянуть на poetry.lock файл. Да, Poetry использует Poetry. Извините за частое упоминание слова Poetry.
- Подход Poetry к управлению зависимостями по умолчанию. Так, при установке новой зависимости в проект с помощью команды
poetry add <package>
без всякой спецификации требуемой версии, в pyproject.toml будет записно что-то вроде^<version>
. То есть версия большая или равная указанной, но меньшая чем следующий major-релиз. В перспективе это может привести к конфликтам версий среди различных библиотек. - И некоторые другие вещи...
Впрочем, не зря существуют другие относительно популярные пакетные менеджеры, как, например, более совершенный вариант pip — pip-tools от известной команды разработчиков JazzBand.
Попробуйте работать с Poetry. Это один из инструментов, который точно стоит попробовать в своей практике. Он мощнее и интереснее, чем многие аналоги, а для старта работы требует незначительных действий: быстрой установки и применения пары консольных команд.