Наука о данных в реальном мире

Решение управления зависимостями в Python с помощью Poetry

Простота и согласованность рабочего процесса Python

Моя команда недавно решила добавить еще один инструмент в наш технологический стек - Поэзия. Как и любое техническое решение, это было сделано тщательно, чтобы избежать ненужных сложностей. Так почему мы решили использовать Поэзию?

Исходя из мира создания веб-сайтов, я привык к npm. С помощью npm мир JavaScript рассматривает управление зависимостями как решенную проблему. В наших проектах Python мы просто использовали pip, и есть два основных рабочих процесса, которым мы могли бы следовать:

  • Ручное управление нужными вам зависимостями на верхнем уровне
  • Сбор всех ваших зависимостей вместе с помощью pip freeze

Определение того, что вам нужно

При таком подходе вы создаете файл требований, который выглядит так:

pandas==1.0.1

Это имеет то преимущество, что его проще понять, потому что вы используете только pandas напрямую. Затем вы используете pip для установки этих зависимостей и их зависимостей:

pip install -r requirements.txt

Это означает, что вы можете создать отдельный файл для любых зависимостей разработки:

pip install -r dev_requirements.txt

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

К сожалению, вы должны поддерживать этот файл, и у вас мало контроля над зависимостями. Каждый раз, когда вы запускаете pip install, ваши зависимости могут быть установлены с другой версией.

Замораживание всех зависимостей

Другой подход - использовать больше функций pip для замораживания ваших зависимостей. Запустив pip install, а затем pip freeze, вы получите список всех установленных пакетов. Например, запуск pip install pandas приведет к выходу pip freeze:

numpy==1.18.1
pandas==1.0.1
python-dateutil==2.8.1
pytz==2019.3
six==1.14.0

Это замечательно, так как позволяет вам держать ваши зависимости привязанными к определенным версиям, создавая повторяемую среду. К сожалению, если вы установите что-нибудь в среде, в которой работает pip, это будет добавлено в этот список. Например, вы решили установить numba, чтобы посмотреть, ускоряет ли он вашу обработку. Это приводит к следующим замороженным зависимостям:

llvmlite==0.31.0
numba==0.48.0
numpy==1.18.1
pandas==1.0.1
python-dateutil==2.8.1
pytz==2019.3
six==1.14.0

Но, к сожалению, numba не дала желаемого улучшения производительности. Когда вы затем запускаете pip uninstall numba, он удаляет numba, но не обязательно все зависимости, установленные numba:

llvmlite==0.31.0
numpy==1.18.1
pandas==1.0.1
python-dateutil==2.8.1
pytz==2019.3
six==1.14.0

Обратите внимание, что теперь у вас есть этот дополнительный llvmlite пакет, от которого вы, возможно, никогда не избавитесь?

Стоит отметить, что есть инструменты, позволяющие обойти это и удалить все зависимости. Но есть еще одно предостережение. если вы устанавливаете зависимости для разработки, они также будут способствовать возникновению этой проблемы.

Лучший из двух миров

Поэзия способна делать и то, и другое с простым интерфейсом. Когда вы вызываете poetry add, он добавляет пакет в pyproject.toml файл, чтобы отслеживать зависимости верхнего уровня (включая сам Python):

[tool.poetry.dependencies]
python = "^3.7"
pandas = "^1.0.1"

Это связано с файлом poetry.lock, который включает все установленные пакеты, привязанные к определенной версии. Встраивание файла блокировки в эту статью займет много места, потому что он по умолчанию включает хэши для обеспечения целостности пакета. Вот фрагмент:

[[package]]
category = "main"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
version = "1.14.0"
[metadata]
content-hash = "5889192b2c2bef6b6ceae7457fc90225ba0c38a80ecd15bbbbf5871f91a08825"
python-versions = "^3.7"
[metadata.files]
six = [
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
{file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"},
]

Чтобы повторить наш эксперимент с numba, мы используем poetry add numba и poetry remove numba. Эти команды также удаляют llvmlite из файла блокировки среды и зависимостей. Удалив зависимости и очистив, Poetry позволяет нам попробовать больше пакетов с простым способом очистки нашей виртуальной среды.

Наконец, Poetry разделяет производственные зависимости и зависимости разработки. Если мы хотим написать несколько тестов, мы можем использовать poetry add -D pytest, что приведет к:

[tool.poetry.dependencies]
python = "^3.7"
pandas = "^1.0.1"
[tool.poetry.dev-dependencies]
pytest = "^5.3.5"

При создании производственного пакета вы можете использовать poetry install --no-dev, чтобы игнорировать все, что используется для разработки.

Работа с виртуальными средами

В прошлом году один из моих коллег написал отличный пост о том, почему мы используем виртуальные среды в Data Science. Поэзии тоже есть что добавить, автоматическое управление ими для ваших проектов. Итак, вместо того, чтобы запускать:

python -m venv .venv
. .venv/bin/activate
super-command

Вместо этого вы просто набираете:

poetry run super-command

Poetry создает виртуальную среду для вашего проекта, которая соответствует указанной вами версии Python. По умолчанию он хранится вне корня вашего проекта, чтобы не создавать беспорядок. Моя команда все же решила вернуть виртуальную среду в каталог проекта по другим причинам, что было очень просто:

poetry config --local virtualenvs.in-project true

Что создает виртуальную среду в довольно стандартной папке .venv. Это немного раздражает, потому что вместо использования основного pyproject.toml создается еще один poetry.toml файл.

Неужто это было сделано раньше?

Есть и другие инструменты, существовавшие до Поэзии, которые могут удовлетворить эти требования. Poetry также использует множество собственных предложений по усовершенствованию Python, таких как PEP-508, PEP-517 и PEP-518. Эти PEP направлены на использование pyproject.toml в качестве основного места для настройки проекта. При рассмотрении долговечности Poetry соблюдение PEP дает нам уверенность в будущей поддержке функций, которые мы используем.

Надеюсь, это объясняет, почему я был уверен в том, что моя команда выбрала Poetry вместо альтернатив, которые были до нее. Если вы ищете более подробное введение в историю и более конкретные поэтические команды, я бы посоветовал Упаковать проекты Python надлежащим образом Тодда Бирчарда ». А чтобы узнать о других инструментах, которые можно комбинировать с Поэзией, посмотрите Как настроить отличную среду Python для науки о данных или чего-то еще от Саймона Хаве.

Это был действительно простой переход, когда инженеры и специалисты по обработке данных совместно работали над переводом проектов. Создание новых проектов также стало намного проще, что позволяет нам экспериментировать с различными пакетами и получать конкретную детерминированную сборку, когда мы переходим к производству.