Сравнение инструментов управления зависимостями (dependency management) в Python.

Статья является переводом поста в blogs.sap.com: Why you should use Poetry instead of Pip or Conda for Python Projects.

Управление зависимостями – важная составляющая любого проекта, которая требует использования внешних библиотек и пакетов (тех, что не входят в программную среду по умолчанию). Ряд приложений, разрабатываемых с помощью Python, является примером такого использования. Существует множество внешних инструментов для управления зависимостями в Python-проектах, такие как Pip, Conda и даже Poetry. Понимая, что у каждого разработчика есть свои предпочтения, я бы хотел поделиться больше тем, почему иногда может быть лучше использовать Poetry, а не Pip или Conda. Данные инструменты рассматриваются в этой статье потому, что я имею непосредственный опыт работы с каждым из них.

Для начала

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

  • Conda – это инструмент управления зависимостями, пришедший из Anaconda. Последняя обычно используется в data science теми, кто только начинает работу с Python и не хочет сильно утруждаться с установкой общих зависимостей (dependencies), необходимых в данной сфере: numpy, pandas, jupiter и scikit-learn. Больше информации о Conda можно найти в документации и на официальной странице Anaconda.
  • Pip – инструмент управления зависимостями, пришедший из стандартных настроек Python для Windows. Он может быть установлен через Homebrew для MacOS или менеджер пакетов для Linux (например, APT для Debian и Ubuntru). В этой статье дается вполне хорошее описание того, как можно начать работу с Pip.
  • Poetry – новый инструмент управления зависимостями, который набирает популярность среди пользователей Python. Использование файлов pypoproject.toml и poetry.lock делает его похожим на Node Package Manager (npm) для Node.js. Более подробную информацию про Poetry можно найти в документации.

Интересная часть

Вот теперь я попытаюсь убедить вас, что Poetry – лучший выбор из всех трёх инструментов, что описаны выше.

Избегайте Conda повсюду, где вы собираетесь использовать Python в продуктиве.

Я знаю, "избегайте x повсюду" – достаточно сильная фраза в данном контексте, но у меня есть причины говорить так.

Предустановленные пакеты в Anaconda

Если мы запустим команду conda list в базовом окружении, мы можем увидеть нечто такое:

(base) user:~$ conda list

# packages in environment at /home/user/anaconda3:
#
# Name                    Version                   Build  Channel
_ipyw_jlab_nb_ext_conf    0.1.0            py39h06a4308_0  
_libgcc_mutex             0.1                        main  
_openmp_mutex             4.5                       1_gnu  
alabaster                 0.7.12             pyhd3eb1b0_0
...
jupyter                   1.0.0            py39h06a4308_7
...
numpy                     1.20.3           py39hf144106_0
...
pandas                    1.3.4            py39h8c16a72_0
...
scikit-learn              0.24.2           py39ha9443f7_0
...

Это имеет смысл для тех, кто только приступил к изучению Python в data science и пока не хочет тратить слишком много времени на попытки определить, как вывести все зависимости, которые им нужны. Однако, когда дело касается реального проекта, мы обычно не хотим загружать пакеты, которые нам не слишком-то и нужны. Их наличие только заполняет память и пространство системы, которые можно было бы использовать для чего-то более важного. Конечно, существует также Miniconda, которая использует базу Conda без всех этих дополнительных пакетов, но...

Ненужные пакеты добавляются во время установки

Например, если мы запустим команду conda install -c conda-forge numpy==1.22.3, мы увидим следующее:

The following NEW packages will be INSTALLED:

  _libgcc_mutex      conda-forge/linux-64::_libgcc_mutex-0.1-conda_forge
  _openmp_mutex      conda-forge/linux-64::_openmp_mutex-4.5-1_gnu
  bzip2              conda-forge/linux-64::bzip2-1.0.8-h7f98852_4
  ca-certificates    conda-forge/linux-64::ca-certificates-2021.10.8-ha878542_0
  ld_impl_linux-64   conda-forge/linux-64::ld_impl_linux-64-2.36.1-hea4e1c9_2
  libblas            conda-forge/linux-64::libblas-3.9.0-14_linux64_openblas
  libcblas           conda-forge/linux-64::libcblas-3.9.0-14_linux64_openblas
  libffi             conda-forge/linux-64::libffi-3.4.2-h7f98852_5
  libgcc-ng          conda-forge/linux-64::libgcc-ng-11.2.0-h1d223b6_15
  libgfortran-ng     conda-forge/linux-64::libgfortran-ng-11.2.0-h69a702a_15
  libgfortran5       conda-forge/linux-64::libgfortran5-11.2.0-h5c6108e_15
  libgomp            conda-forge/linux-64::libgomp-11.2.0-h1d223b6_15
  liblapack          conda-forge/linux-64::liblapack-3.9.0-14_linux64_openblas
  libnsl             conda-forge/linux-64::libnsl-2.0.0-h7f98852_0
  libopenblas        conda-forge/linux-64::libopenblas-0.3.20-pthreads_h78a6416_0
  libstdcxx-ng       conda-forge/linux-64::libstdcxx-ng-11.2.0-he4da1e4_15
  libuuid            conda-forge/linux-64::libuuid-2.32.1-h7f98852_1000
  libzlib            conda-forge/linux-64::libzlib-1.2.11-h166bdaf_1014
  ncurses            conda-forge/linux-64::ncurses-6.3-h27087fc_1
  numpy              conda-forge/linux-64::numpy-1.22.3-py310h45f3432_2
  openssl            conda-forge/linux-64::openssl-3.0.2-h166bdaf_1
  pip                conda-forge/noarch::pip-22.0.4-pyhd8ed1ab_0
  python             conda-forge/linux-64::python-3.10.4-h2660328_0_cpython
  python_abi         conda-forge/linux-64::python_abi-3.10-2_cp310
  readline           conda-forge/linux-64::readline-8.1-h46c0cb4_0
  setuptools         conda-forge/linux-64::setuptools-62.1.0-py310hff52083_0
  sqlite             conda-forge/linux-64::sqlite-3.38.2-h4ff8645_0
  tk                 conda-forge/linux-64::tk-8.6.12-h27826a3_0
  tzdata             conda-forge/noarch::tzdata-2022a-h191b570_0
  wheel              conda-forge/noarch::wheel-0.37.1-pyhd8ed1ab_0
  xz                 conda-forge/linux-64::xz-5.2.5-h516909a_1
  zlib               conda-forge/linux-64::zlib-1.2.11-h166bdaf_1014

Я вполне уверен, что библиотеки типа ca-certificates and openssl не слишком нужны при numpy, но давайте посмотрим, что мы получим, когда попытаемся установить numpy в новое виртуальное окружение, используя вместо этого Pip. Я также запущу команду pip list, чтобы увидеть какие пакеты установятся вместе с numpy:

user:~$ pip install numpy==1.22.3
Collecting numpy==1.22.3
  Downloading numpy-1.22.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.8 MB)
     |████████████████████████████████| 16.8 MB 10.2 MB/s 
Installing collected packages: numpy
Successfully installed numpy-1.22.3

user:~$ pip list
Package    Version
---------- -------
numpy      1.22.3
pip        21.2.2
setuptools 57.4.0
wheel      0.36.2

Ух ты, это даже близко не стоит к тому количеству пакетов Conda, которые были предложены, когда мы устанавливали numpy с помощью неё. Такая "раздутость" является серьезной причиной отказаться от Conda в управлении зависимостями в продуктиве.

Странное поведение при отображении установленных зависимостей

Например, если я установлю numpy с помощью Pip в новое виртуальное окружение Conda, я увижу что-то вроде этого:

(test-env) user:~$ conda list
# packages in environment at /home/user/anaconda3/envs/test-env:
#
# Name                    Version                   Build  Channel

(test-env) user:~$ pip list
Package    Version
---------- -------
numpy      1.22.3
pip        21.2.2
setuptools 57.4.0
wheel      0.36.2

Хм, странно. numpy не показывается, когда мы запускаем команду conda list, но всплывает, когда мы запускаем pip list command.

Но это не всё. Если я сейчас установлю pandas через conda-forge, мы увидим что-то похожее на то, что мы получили, когда запускали conda list и pip list commands:

(test-env) user:~$ conda list
# packages in environment at /home/user/anaconda3/envs/test-env:
#
# Name                    Version                   Build  Channel
...
numpy                     1.22.3                   pypi_0    pypi
...
pandas                    1.4.2                    pypi_0    pypi
...

(test-env) user:~$ pip list
Package         Version
--------------- -------
numpy           1.22.3
pandas          1.4.2
...

Похоже, что пакеты, установленные через Pip, показываются в выводе conda list, если другой пакет, использующий их, был установлен через conda-forge. Это может сбивать с толку пользователей, так как:

1. Нам нужно использовать две разные команды, чтобы проверить все наши зависимости.

2. Мы можем пропустить некоторые зависимости, если используем только conda list, чтобы проверить их.

Я надеюсь, что эти доводы достаточно красноречивы, чтобы вы поняли, почему лучше вообще отказаться от использования Conda, если мы планируем делать какую-то серьёзную работу. Поняв, что точно нужно исключить, сосредоточимся теперь на сравнении Poetry и Pip.

Почему же тогда на Pip?

Я понимаю, что существуют люди, которые предпочитают использовать Pip по вполне резонным причинам. Например, Pip существует дольше, чем Poetry, а люди склонны придерживаться своих привычек. Тем не менее, я бы хотел поделиться некоторыми наблюдениями и всё-таки убедить вас в том, что Poetry в данном случае – лучший выбор.

Более эффективная работа с конфликтами

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

(test-env) user:~$ pip list
Package         Version
--------------- -------
numpy           1.22.3
pandas          1.4.2
pip             21.1.1
python-dateutil 2.8.2
pytz            2022.1
setuptools      56.0.0
six             1.16.0

(test-env) user:~$ pip install "numpy<1.18.5"
Collecting numpy<1.18.5
  Downloading numpy-1.18.4-cp38-cp38-manylinux1_x86_64.whl (20.7 MB)
     |████████████████████████████████| 20.7 MB 10.9 MB/s
Installing collected packages: numpy
  Attempting uninstall: numpy
    Found existing installation: numpy 1.22.3
    Uninstalling numpy-1.22.3:
      Successfully uninstalled numpy-1.22.3
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
pandas 1.4.2 requires numpy>=1.18.5; platform_machine != "aarch64" and platform_machine != "arm64" and python_version < "3.10", but you have numpy 1.18.4 which is incompatible.
Successfully installed numpy-1.18.4

(test-env) user:~$ pip list
Package         Version
--------------- -------
numpy           1.18.4
pandas          1.4.2
pip             21.1.1
python-dateutil 2.8.2
pytz            2022.1
setuptools      56.0.0
six

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти