Poetry - это инструмент для управления зависимостями в Python проектах (аналог встроенного pip). Идея реализации данного инструмента пришла его создателю в связи с тем, что различные способы менеджмента пакетов (requirements.txt, setup.cfg, MANIFEST.ini и другие) показались создателю Poetry не очень-то удобными. 

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

Установка

Установить poetry на windows можно либо при помощи pip:

pip install poetry

Либо более гибким вариантом через powershell:

(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python -

Отличие от pip

Pip хранит данные о зависимостях в файле requirements.txt (название на самом-то деле может быть любое), poetry хранит информацию в файле pyproject.toml, однако, в случае с pip, в его файле хранится только список зависимостей с описанием версий, а в .toml хранится вся основная информация о проекте, что очень удобно, так как все данные собраны в одном месте (далее последует более подробное описание).

Чтобы установить зависимости в pip необходимо выполнить:

pip install -r requirements.txt

Что для poetry займет всего 2 слова:

# для первичной установки
poetry install
# для обновления
poetry update

Просмотр зависимостей в pip можно сделать только командой:

pip freeze

Однако будут показаны только актуальные версии библиотек, не будет возможности увидеть структуру всех пакетов с их зависимостями. В poetry в файле poetry.lock можно увидеть сведения обо всех установленных пакетах, команда:

poetry show --tree

Покажет древовидную структуру пакетов с их личными зависимостями.

Так же запуск проекта в pip (в случае с виртуальным окружением) создает неудобства, так как первым делом необходимо зайти в это самое окружение при помощи команды:

./venv/Scripts/activate.bat

И только затем можно выполнять какие-либо изменения с проектом. В poetry нет необходимости активировать виртуальное окружение, достаточно лишь зайти в папку с проектом и начинать пользоваться командами. Poetry сам найдет нужное окружение. Также в poetry можно менять версию python без необходимости менять старое виртуальное окружение.

Это лишь малая часть преимуществ. Далее рассмотрим примеры команд и структуру poetry файлов.

pyproject.toml

Главный файл для poetry - это pyproject.toml. Все данные о проекты должны быть записаны в нём. При установке пакетов poetry берёт данные из этого файла и формирует файл с зависимостями poetry.lock (если уже есть готовый файл poetry.lock, то данные будут браться из него). Toml файл состоит из нескольких блоков, каждый из которых имеет свои особенности, рассмотрим данные блоки:

[tool.poetry] - содержит основную информацию о проекте, такую как:

  • name - имя проекта

  • version - версия проекта

  • description - описание проекта

  • license - лицензия проекта

  • authors - список авторов проекта в формате name <email>

  • maintainers - список менторов проекта формате name <email>

  • readme - readme файл проекта в формате README.rst или README.md

  • homepage - URL сайта проекта

  • repository - URL репозитория проекта

  • documentation- URL документации проекта

  • keywords - список ключевых слов проекта  (макс: 5)

  • classifier - список PyPI классификаторов

[tool.poetry.dependencies] - содержит описание всех зависимостей проекта. Каждая зависимость должна иметь название с указанием версии, также присутствует возможность скачать проекта с github с указанием ветки/версии/тэга, например:

[tool.poetry.scripts] - в данном разделе можно описать различные сценарии или скрипты, которые будут выполняться при установке пакетов или при запуске приложения. Например:

  • poetry = 'poetry.console:run'

  • main-run = 'new_proj.main:run' (после чего достаточно запустить poetry main-run и будет выполнен запуск функции run в файле new_prof/main.py)

[tool.poetry.extras] - в данном блоке описываются группы зависимостей, которые можно устанавливать отдельно:

[tool.poetry.dependencies]
psycopg2 = { version = "^2.7", optional = true }
pymysql = { version = "1.0.2", optional = true }
[tool.poetry.extras]
mysql = ["pymysql"]
pgsql = ["psycopg2"]

Далее зависимости можно установить двумя способами:

poetry install --extras "mysql pgsql"
poetry install -E mysql -E pgsql

[tool.poetry.urls] - помимо основных URL, указанных в [tool.poetry], можно указывать свои URL:

Пример данных в pyproject.toml
[tool.poetry]
name = "new_proj"
version = "0.1.0"
description = "My description"
authors = ["Daniil Gorbenko <dani.gorbenko@gmail.com>"]

[tool.poetry.dependencies]
python = "^3.10"
pygame = "^2.1.0"
icecream = "^2.1.1"
requests = "^2.26.0"
psycopg2 = { version = "^2.7", optional = true }
pymysql = { version = "1.0.2", optional = true }

[tool.poetry.dev-dependencies]
Pympler = "^0.9"

[tool.poetry.urls]
"Bug Tracker" = "https://github.com/python-poetry/poetry/issues"

[tool.poetry.scripts]
run-main = "new_proj.main:main_def"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Основные методы

new

Чтобы создать новый проект с помощью Poetry, достаточно выполнить poetry new <название папки с проектом>. После чего создастся папка с названием вашего проекта, в этой папке будет лежать файл pyproject.toml.

poetry new new_project

init

Чтобы сделать пакетным менеджером poetry в уже имеющемся проекте, достаточно выполнить:

poetry init
Далее будет предложено заполнить немного основной информации о проекте
Пример заполнения информации о проекте
Пример заполнения информации о проекте

Метод может принимать некоторые необязательные параметры:

  • --name: имя проекта

  • --description: описание проекта

  • --author: имя автора

  • --python: совместимые версии Python

  • --dependency: требуемый пакет с версией пакета

  • --dev-dependency: требования к разработке

После чего в проекте появится файл pyproject.toml, в котором вручную можно изменить любые данные.

install

Чтобы установить зависимости проекта достаточно выполнить команду:

poetry install

Poetry считывает данные из pyproject.toml, строит дерево зависимостей проекта, разрешая проблемы с версиями зависимостей, и устанавливает все пакеты. Однако, если в проекте уже есть файл poetry.lock, то будут использоваться точные версии из этого файла.

Метод может принимать параметры:

  • --remove-untracked: удалит старые пакеты, которые больше не используются в проекте

  • --no-dev: dev пакеты не будут устанавливаться

update

Чтобы обновить версии зависимостей (тем самым обновив файл poetry.lock) достаточно выполнить:

poetry update

Также есть возможность обновить лишь определенные пакеты:

poetry update icecream pygame

Метод может принимать дополнительные параметры:

  • --no-dev : игнорирует обновление dev пакетов

  • --lock : не устанавливает и не обновляет пакеты, а только обновляет файл poetry.lock

add

Чтобы добавить новую библиотеку достаточно выполнить:

poetry add pygame

Можно указывать версию пакета:

 poetry add "pygame>=2"
 poetry add pygame@^2

Можно передать параметры:

  • --dev (-D): установит пакет в качестве dev зависимости

  • --path: путь к пакету (если пакет лежит локально)

  • --lock : не устанавливает зависимости, а только обновляет файл poetry.lock

remove

Чтобы удалить зависимость достаточно выполнить:

poetry remove pygame

Дополнительно можно передать параметры:

  • --dev : удалит пакет из dev зависимостей

show

Чтобы посмотреть зависимости проекта достаточно выполнить:

poetry show
результат poetry show
результат poetry show
результат poetry show

Если необходимо посмотреть информацию о конкретном пакете:

poetry show pygame
результат poetry show pygame
результат poetry show pygame
результат poetry show pygame

Посмотреть дерево зависимостей проекта можно при помощи:

poetry show --tree
результат poetry show --tree
результат poetry show --tree
результат poetry show --tree

Также можно передать параметры:

  • --tree: список зависимостей в виде дерева

  • --latest (-l): показать последние версии проектов

  • --outdated (-o): показать последние версии только для устаревших пакетов

run

Чтобы запустить проект достаточно выполнить:

poetry run python <имя python файла>
poetry run python main.py 
poetry run <имя скрипта, описанного в [tool.poetry.scripts]>
poetry run main-run

PyCharm & Poetry

В PyCharm 2021.3 добавили поддержку Poetry. Теперь при создании проекта можно сразу указать poetry основным пакетным менеджером. Перед использованием необходимо установить executable версию poetry (на windows через poweshell):

(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python -

После чего можно без проблем выбрать Poetry при создании проекта:

Установка poetry как менеджер пакетов в Pycharm
Установка poetry как менеджер пакетов в Pycharm

Далее автоматически будет создан .toml файл:

Структура проекта
Структура проекта

При установке пакетов через менеджер PyCharm файл pyproject.toml будет автоматически обновляться сам:

Загрузка пакета через PyCharm
Загрузка пакета через PyCharm
Обновленный .toml
Обновленный .toml

Версии зависимостей

При установке пакета можно указать точную версию проекта, например:

[tool.poetry.dependencies]
pygame = "2.1.0"

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

[tool.poetry.dependencies]
pygame = "^2.1"
pygame = "~2.1"
pygame = "2.1.*"
pygame = "*"

Вот какие диапазоны принимают данные префиксы версий:

Зависимость

Минимальная версия

Максимальная версия

^1.2.3

>=1.2.3

<2.0.0

^1.2

>=1.2.0

<2.0.0

^1

>=1.0.0

<2.0.0

^0.2.3

>=0.0.3

<0.0.4

~1.2.3

>=1.2.3

<1.3.0

~1.2

>=1.2.0

<1.3.0

~1

>=1.0.0

<2.0.0

*

>=0.0.0

-

1.*

>=1.0.0

<2.0.0

1.2.*

>=1.2.0

<1.3.0

Заключение

Poetry - отличная альтернатива pip. Он позволяет отказаться от requirements.txt в пользу более гибкой настройки проекта. Благодаря poetry можно в любой момент посмотреть информацию о зависимостях любого пакета, гибко настраивать версии и обмениваться poetry.lock файлами с уже заготовленным списком версий пакетов.

Комментарии (35)


  1. SSSerg
    06.12.2021 10:49

    Ни слова про packages и команду build...


    1. daniilgorbenko Автор
      06.12.2021 10:55

      старался выделить самое основное, что поможет начать работать с poetry
      в самом начале написано, что цель статьи - немного познакомить с основными командами, а также в сравнении с pip указано, что не все моменты/преимущества выделены
      информации пришлось поместить много, поэтому не всё добавил


  1. snp
    06.12.2021 11:42
    +2

    poetry справедливее сравнивать не с голым pip, а с pip-tools — там можно и requirements.txt сохранить, и получить такой же чёткий контроль всех версий пакетов.


    Выбор между poetry и pip-tools уже не так очевиден.


  1. kalbas
    06.12.2021 11:46
    +4

    В какой-то момент добавил к использованию pip пакет pip-tools, и в принципе проблема с тем, что непонятно откуда какие зависимости приходят ушла, теперь все нужные для проекта зависимости описываю в requirements.in, а requirements.txt генерится и содержит в принципе ту же метадату о взаимосвязях зависимостей. Есть ли в poetry еще плюсы? Может установка быстрее или что-нибудь еще?


    1. rg_software
      06.12.2021 12:20
      +1

      Для меня, скажем, основной плюс в том, что это all-in-one solution. На чистой машине выкачиваю репозиторий проекта, по toml файлу автоматом создаётся virtual environment и устанавливаются зависимости. Вместо python набираю poetry run python -- запускается версия Питона в виртуальном окружении. Можно настроить так, чтобы окружение создавалось в папке проекта, тогда если проект больше не нужен, папка удаляется -- и всё с концами. Ещё у меня была проблема с тем, что какой-то из более стандартных менеджеров зависимостей на каких-то проектах зависал навечно при попытке определить скачиваемые пакеты, вот и пришлось искать альтернативы.


    1. daniilgorbenko Автор
      06.12.2021 12:39

      +удобно использовать несколько версий Python
      poetry env use <пусть до python.exe нужной версии>

      особенно, когда хотите перейти от более старой версии к более новой, но боитесь что-то сломать, самое-то


      1. kalbas
        06.12.2021 12:46
        +4

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


        1. daniilgorbenko Автор
          06.12.2021 13:37
          -3

          докеры - хороший вариант, когда работаете над большими/долго живущими проектами
          а если, допустим, человек хочет дома создать какой-то пет-проект или надо быстро опробовать какую-то фишку (которая не факт, что пригодится), то не совсем удобно тянуть за собой докер, когда можно выполнить poetry new test_project и начинать работать
          но это дело привычки, если рука набита или есть заготовки, чтобы создать докер за минуту, то почему бы и нет


        1. B7W
          06.12.2021 20:36

          А расскажите как вы разрабатываете в docker?


          1. kalbas
            06.12.2021 21:56
            +2

            Ну как, описываю Dockerfile, с помощью docker-compose пробрасываю нужные порты/волюмы/переменные окружения/дебажную команду запуска, цепляю удаленный интерпретатор из собранного образа в pycharm и сижу довольный.


    1. alexxxnf
      06.12.2021 14:38

      pyproject.toml - это часть стандарта PEP 517 и PEP 518. Насколько я понимаю, pip-tools использует свой собственный подход не основанный на PEP.


      1. kalbas
        06.12.2021 15:13
        +2

        Ооо, зашел на сайт Poetry, почитал и понял, что эта штука по сути куда шире и роскошнее, чем описано в статье. Pip -- это просто package installer, а Poetry -- средство управления проектом на python (здесь и управление зависимостями, и изоляция, и публикация, и этот волшебный poetry run). До этого я также не вникал и думал, что это просто какая-то замена pip, потому что читал статьи типа этой. Спасибо в общем за ваш комментарий!


      1. snp
        06.12.2021 16:13

        Стандартный workflow для pip-tools:


        pip-compile --output-file=requirements.txt requirements.in
        git add requirements.*
        git commit -v

        Получаем обычный requirements.txt, такой же, каким он был и 10 лет назад, просто в каждой строчке жёстко прописана версия пакета, в т.ч. все зависимости (которые в requirements.in не требуется указывать). Никакой магии. Потом ставим:


        .venv/bin/pip install -r requirements.txt

        PEP-517/518 это, конечно, хорошо, но вариант с pip-tools выглядит проще, при этом получаем то же самый результат.


        1. alexxxnf
          06.12.2021 16:39
          +1

          У poetry есть то же самое: poetry export -o requirements.txt. Я это использую в Docker-образах, чтобы не тащить туда лишние зависимости.

          Чем это лучше или хуже pip-tools, я не знаю. Мне удобно.


          1. B7W
            06.12.2021 20:44

            Есть более правильный способ

            poetry build

            А в Dockerfile

            ENV WHEEL "socks5_tune-$VERSION-py3-none-any.whl"
            
            COPY dist/$WHEEL /app/
            
            RUN pip install --no-cache-dir /app/$WHEEL


            1. dolfinus
              06.12.2021 21:16
              +4

              И в итоге сборка будет не воспроизводимой, потому что файл создавали на каком-то конкретном хосте, а не в явно заданном окружении в Dockerfile


              1. B7W
                06.12.2021 22:03
                -1

                Почему не воспроизводимой? Собирайте на CI в той же версии image. Или используйте docker multi-stage build.


  1. glader
    06.12.2021 13:46

    Подскажите, что делать, если нужны два списка пакетов, например для тестирования и для продакшена?


    1. daniilgorbenko Автор
      06.12.2021 14:03

      при установке пакетов можно использовать

      # установить зависимость в общий список
      poetry add pygame
      # установить зависимость в качестве dev пакета
      poetry add pygame --dev

      либо вручную вписать в toml файл для продакшена в блок [tool.poetry.dependencies]

      [tool.poetry.dependencies]
      pygame = "^2.1.0"

      для dev

      [tool.poetry.dev-dependencies]
      Pympler = "^0.9"

      и при установке делать так:

      # установить все зависимости (в т.ч. dev)
      poetry install
      # игнорировать dev зависимости
      poetry install --no-dev


      1. alexxxnf
        06.12.2021 14:42

        Кроме dev-зависимостей, новая версия Poetry будет поддерживать ещё и произвольные группы: https://python-poetry.org/docs/master/managing-dependencies/#dependency-groups


      1. dolfinus
        06.12.2021 21:43
        +2

        Недавно наткнулся на вот такой баг:

        https://github.com/python-poetry/poetry/issues/3139

        Если сначала установить зависимости для одного окружения, а потом поверх установить зависимости для другого, poetry сначала удалит все пакеты первого окружения, и затем накатит второе. Как я понял, это было сделано для упрощения решения проблем с конфликтами версий установленных пакетов, поскольку так не нужно учитывать наличие уже установленных, но несовместимых друг с другом версий пакетов.

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

        Да и если говорить о других зависимостях, сейчас это работает странно - poetry может считать, что зависимость установлена, хотя сам же ее и удалил при очистке окружения, и в итоге установка падает на пустом месте с кучей ошибок


  1. domix32
    06.12.2021 14:40
    +1

    То бишь cargo для питона? А как сами пакеты ставятся при наличии проекта и toml? Откуда пакеты берутся и в каком формате (eggs, wheels)? Появился новый Poetry registry с пакетами?


    1. stepalxser
      06.12.2021 16:35

      Стандартный источник pypi.org. Можно указывать свои кастомные.
      Насчет формата не скажу, про предполагаю, что стандарт сейчас wheels.


  1. amarao
    06.12.2021 14:45
    +3

    Я так и не понял, что оно даст мне, если я перестану использовать pip и начну poetry. Что-то станет ещё более гибким, ок. А проблему оно какую решает?


    1. Mel
      06.12.2021 15:06
      +1

      Нормальный фриз зависимостей пакетов которые вы используете через poetry.lock что бы воспроизвести точно такое же окружение на 2 машине.
      С обычным пипом частенько было когда на 2 машине устанавливались другие, обновленные подзависимости у пакетов и получались проблемы.


      1. amarao
        06.12.2021 15:59
        +3

        Это не "фриз", это "lock". Да, из всех языков, только pip жил без лока и его, местами, не хватает.


        1. snp
          06.12.2021 16:15

          Собственно, pip-tools всё это и делает. В requirements.in пишем пакеты, которые нам нужны, а pip-tools создаёт requirements.txt, где вписаны абсолютно все пакеты, со всеми зависимостями и для каждого задана точная версия. Тот же самый лок.


  1. SilverDrakon
    06.12.2021 15:56

    А можно ли при помощи poetry скачать (не устанавливая) все нужные файлы зависимостей, чтобы их можно было перенести на сервер без интернета и там развернуть? (и умеет ли poetry инсталлировать скачанное заранее в оффлайн-режиме?)


    1. stepalxser
      06.12.2021 16:46

      https://pip.pypa.io/en/stable/cli/pip_wheel/

      На сервере-сервере сборщике:
      pip wheel --wheel-dir /tmp/wheels -r requirements.txt

      На офлайн-сервере, после копирования wheels через флешку:
      pip install ./wheels/*

      Ну и сам детерминированный
      requirements.txt можно получить с помощью poetry.


  1. vba
    06.12.2021 16:26
    +3

    По мне так poetry нисколько не прекрасен, тормознутная поделка, которая с помощью непонятной магии пытается вам втюхать venv + pip в своей обертке. Совершено не подходит для проектов на conda, от слова совсем. По мне так pip на любом проекте хватает с лихвой и нет ему достойной альтернативы, пока.


    1. tzlom
      06.12.2021 18:40
      +2

      conda вещь в себе, то что в ней pip работает это скорее недосмотр а не фича, нормально с ним конда работать не умеет.

      По сути - питон в окнах и питон в линуксе это две совершенно разные среды которые делают одинаково больно, но по разному.


  1. NightShad0w
    07.12.2021 00:30

    Главная особенность poetry не упомянута. Проект обещает наиболее корректное разрешение версий зависимостей, без внезапных неудач посреди установки сотни зависимостей, потому что где-то противоречия возникли.


    1. dolfinus
      07.12.2021 02:08
      +2

      Интересно каким образом он обещает это сделать, если на уровне API pypi и аналогичные ему репозитории не умеют отдавать метаданные пакета без его скачивания?

      Только в этом году, после переключения в pip на новый резолвер, оказавшийся очень медленным поначалу, разработчики всерьез взялись за исправление этой проблемы:

      https://github.com/pypa/warehouse/issues/8254

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

      https://github.com/pypa/warehouse/pull/9972

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

      А без них, что pip, что poetry находятся в одинаковой ситуации - чтобы скачать зависимости пакета, нужно сначала скачать пакет, распарсить его метаданные, потом скачать его зависимости, зависимости его зависимостей и т.п.

      Более того, проблема решаема только для .whl, потому что он собирается для определенной версии Python и ОС. А вот если выгружать в pypi исходники пакета в .tar.gz, метаданные из него вынуть не выйдет, потому что зависимости могут быть указаны в setup.py, т.е. определяться динамически в момент запуска сборки.

      Что такого революционного предлагает poetry, кроме requirements.lock файла? Так это файл ему точно так же придется подготавливать, скачивая пакеты из pypi


      1. NightShad0w
        07.12.2021 15:03

        Извините, промахнулся ответом. Ответ в главной ветке


  1. NightShad0w
    07.12.2021 14:58

    Спасибо за замечание, я не был в курсе таких проблем в pypi.


    Я с pipenv в ситуацию кривых разрешений версий попадаю часто, и это вынуждает разрешать версии в ручную, выясняя, какие пакеты в каком порядке ставить. Poetry меня обнадеживает.


    Процитирую poetry readme, как дополнение к исходному комментарию:


    Dependency resolution
    The dependency resolution is erratic and will fail even if there is a solution. Let's take an example:
    
    pipenv install oslo.utils==1.4.0
    will fail with this error:
    
    Could not find a version that matches pbr!=0.7,!=2.1.0,<1.0,>=0.6,>=2.0.0
    while Poetry will get you the right set of packages:
    
    poetry add oslo.utils=1.4.0
    results in :
    
      - Installing pytz (2018.3)
      - Installing netifaces (0.10.6)
      - Installing netaddr (0.7.19)
      - Installing oslo.i18n (2.1.0)
      - Installing iso8601 (0.1.12)
      - Installing six (1.11.0)
      - Installing babel (2.5.3)
      - Installing pbr (0.11.1)
      - Installing oslo.utils (1.4.0)
    This is possible thanks to the efficient dependency resolver at the heart of Poetry.
    
    Here is a breakdown of what exactly happens here:
    
    oslo.utils (1.4.0) depends on:
    
    pbr (>=0.6,!=0.7,<1.0)
    Babel (>=1.3)
    six (>=1.9.0)
    iso8601 (>=0.1.9)
    oslo.i18n (>=1.3.0)
    netaddr (>=0.7.12)
    netifaces (>=0.10.4)
    What interests us is pbr (>=0.6,!=0.7,<1.0).
    
    At this point, poetry will choose pbr==0.11.1 which is the latest version that matches the constraint.
    
    Next it will try to select oslo.i18n==3.20.0 which is the latest version that matches oslo.i18n (>=1.3.0).
    
    However this version requires pbr (!=2.1.0,>=2.0.0) which is incompatible with pbr==0.11.1, so poetry will try to find a version of oslo.i18n that satisfies pbr (>=0.6,!=0.7,<1.0).
    
    By analyzing the releases of oslo.i18n, it will find oslo.i18n==2.1.0 which requires pbr (>=0.11,<2.0). At this point the rest of the resolution is straightforward since there is no more conflict.