В этом уроке мы более подробно рассмотрим работу с зависимостями с помощью poetry. Заданием прошлого урока было создание проекта с именем hello
. Примеры из этого урока будут показаны применительно к тому проекту-примеру.
Добавление и удаление зависимостей
Добавим к проекту hello
зависимость — пакет colorama
. Это популярная библиотека, которая позволяет раскрасить текст в терминале. Добавим зависимости командой poetry add ИМЯ
:
poetry add colorama
Using version ^0.4.5 for colorama
Updating dependencies
Resolving dependencies... (0.8s)
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing colorama (0.4.5)
Теперь взглянем на секцию tool.poetry.dependencies
файла pyproject.toml. Там окажется следующее:
[tool.poetry.dependencies]
python = "^3.10"
colorama = "^0.4.5"
Как видите, colorama
не только установилась в виртуальное окружение, но еще и появилась в списке зависимостей проекта. Если кто-то захочет запустить ваш код, он просто выполнит команду poetry install
и получит все необходимые зависимости.
Обратите внимание на текст "^0.4.5"
. Он означает не только конкретную версию 0.4.5, но и все версии, совместимые с ней.
Для версии 0.4.5 совместимыми будут считаться версии вплоть до "0.5.0". Так же можно указывать конкретную версию, перечислить несколько допустимых версий или вручную указать диапазон. Все это разнообразие описано в документации.
Теперь попробуем удалить зависимости командой poetry remove ИМЯ
. Из файла pyproject.toml удаляемые пакеты тоже автоматически уберутся:
poetry remove colorama
Updating dependencies
Resolving dependencies... (0.1s)
Writing lock file
Package operations: 0 installs, 0 updates, 1 removal
• Removing colorama (0.4.5)
Группы зависимостей
Многие инструменты для разработки Python-проектов написаны на Python. При этом для запуска кода они, как правило, не требуются.
Возьмем, к примеру, pytest — популярный фреймворк для написания тестов. У этого пакета много собственных зависимостей. В большом проекте подобные инструменты разработки могут занимать приличное количество места.
Проблема в том, что все эти зависимости пользователю не нужны — он же не будет запускать тесты. Тем не менее пользователь получает все зависимости вместе с программой и тратит место на хранение зависимостей.
К счастью, Poetry позволяет описать такие пакеты в виде группы зависимостей. Все созданные группы будут учтены при создании виртуального окружения, но к конечному пользователю попадут только зависимости из основной группы — то есть из [tool.poetry.dependencies]
.
Добавим пакет pytest к проекту hello
. Укажем для него группу dev
— это сокращение от слова development, так принято называть инструменты и среду разработки. Посмотрим, как это работает:
poetry add --group dev pytest
Using version ^7.1.3 for pytest
Updating dependencies
Resolving dependencies... (0.8s)
Writing lock file
...
• Installing pytest (7.1.3)
Когда будете выполнять команду самостоятельно, обратите внимание, сколько пакетов будет установлено вместе с нужным нам pytest
.
Теперь в файле pyproject.toml добавилась новая запись:
[tool.poetry.group.dev.dependencies]
pytest = "^7.1.3"
В итоге мы можем запустить pytest. Только не забывайте писать poetry run pytest
вместо pytest
— программа установлена в виртуальное окружение и снаружи не видна. Попробуем запустить:
poetry run pytest
# Пока pytest не запустил тесты,
# потому что мы же еще не написали их
====== test session starts ========
platform linux -- Python 3.10.4, ...
rootdir: /.../first
collected 0 items
====== no tests ran in 0.00s ======
Команда poetry run
запускает не только команды из виртуального окружения — она запускает любые программы в контексте виртуального окружения. Например, с помощью системной программы which
можно узнать, где находится исполняемый файл pytest:
poetry run which pytest
/../hello/.venv/bin/pytest
Теперь попробуем удалить зависимость. Для этого надо указать ее группу:
poetry remove --group dev pytest
Updating dependencies
Resolving dependencies... (0.1s)
Writing lock file
...
• Removing pytest (7.1.3)
Обновление зависимостей
Для обновления всех зависимостей нужно выполнить команду poetry update
, а чтобы выполнить обновление конкретной зависимости — poetry update ИМЯ
.
Зависимости обновятся в соответствии с указанными диапазонами.
Lock-файл
На предыдущем шаге каждая новая установка зависимостей приводила сначала к созданию, а потом и обновлению lock-файла poetry.lock.
Обсудим этот файл подробнее. Как мы уже обсуждали, в файле pyproject.toml указываются зависимости. При этом у каждой зависимости могут быть свои собственные зависимости, которые также обновляются и так до бесконечности.
Зависимости зависимостей называются транзитивными, и с ними все не просто. Система зависимостей может быть очень запутанной. Для такой ситуации придумали специальный термин — «ад зависимостей» или dependency hell.
Проблема заключается в том, что мы никак не фиксируем версии транзитивных зависимостей. Представим такой пример:
- В нашем проекте есть зависимый пакет A с зафиксированной версией
1.3.2
- У зависимости А есть зависимый пакет B с версией
*
В такой ситуации и без lock-файла команда poetry install
поставила бы:
- Для A — указанную версию
- Для B — последнюю доступную версию из репозитория
Другими словами, выбор версии не детерминирован. Если автор обновит B и нарушит обратную совместимость, то пакет А перестанет работать — весь проект просто сломается.
Можно вручную отслеживать зависимости всех зависимостей и явно прописывать их версии в pyproject.toml. Но такой способ вряд ли сработает, потому что пакеты постоянно обновляются и меняются. Еще отслеживать вручную сложно, потому что связей слишком много — даже в проекте с пятью зависимостями будут сотни транзитивных зависимостей.
Другой выход — требовать, чтобы создатели всех библиотек всегда указывали версии. Этот вариант тоже не сработает, на этот раз из-за человеческого фактора.
Есть одно решение, которое точно сработает — это lock-файл. По сути это автоматизированное отслеживание зависимостей. Содержимое lock-файла выглядит примерно так:
[[package]]
name = "colorama"
version = "0.4.5"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "a07e5731d39f80ad23e37e53ffb7d54de3b1534e6306267399f3b25fcce783ad"
[metadata.files]
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
...
Первый запуск установки зависимостей сформирует этот файл. Туда запишутся все установленные зависимости, в том числе транзитивные с версиями и хеш-суммами.
При дальнейших запусках команда poetry install
всегда ставит то, что указано в lock-файле. Это сработает, даже если удалить папку .venv или добавить новые версии пакетов в файл pyproject.toml. Повторный запуск через любой промежуток времени приведет к тому же результату. Теперь с уверенностью можно сказать — проект запустится в любое время и для любого пользователя.
Наличие lock-файла никак не влияет на поведение команды update
для прямых зависимостей. Представим, что у пакета из нашего файла pyproject.toml вышла новая версия.
В этом случае Poetry уточнит, можно ли обновить его до новой версии. Если да, то загрузится новая версия, а lock-файл обновится автоматически.
Если устанавливать пакеты не хочется, то для такого есть команда poetry update --lock
. Она проверит, какие из новых версий подходят под указанные в конфигурации версии и обновит только lock-файл.
Самостоятельная работа
- Добавьте
colorama
как обычную зависимость в ваш проектhello
- Добавьте
ipython
в группу dev-зависимостей - Запустите IPython
- Выполните в IPython
from colorama import Fore
. Импортирование должно пройти успешно - Попробуйте парочку примеров из описания пакета
colorama
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.