[Перевод] Управление зависимостями в Python: похоже, уже можно пользоваться
Единственным на моей памяти исключением является node.js, авторы которой разработали «с чистого листа» на удивление удачную систему управления зависимостями. Ну, как удачную? Проблем там тоже много, начиная автовыполняемыми скриптами и заканчивая переходом от древовидной к flat структуре в 3-й версии. Но по сравнению с тем, что на тот момент было в других языках, нода — это прорыв.
Совсем недавно экосистема пополнилась новой утилитой rnpm, которая позволяет одной командой устанавливать React Native зависимости. В которых, на секундочку, может быть бинарный код для android и ios. Для разных архитектур. И все это работает из коробки. Мы в Voximplant хорошо знакомы с этой штукой: с ее помощью ставится наш собственный React Native SDK.
Вашему вниманию предлагаем интересную статью, опубликованную всего два дня назад, в которой очень подробно рассказывается про управление зависимостями в Python. Про историю развития. Про проблемы. И, что самое ценное — про то, как сообщество их решает. Под катом адаптированный для Хабра перевод и возможность обсудить печальную тему зависимостей. И не только для Python.
Окай, ребята. Похоже, прошли те времена, когда мы могли рассказывать друг другу, насколько все плохо с зависимостями в Python. Сейчас расскажу, почему.
Потому что мы починили работу с зависимостями. Если ты разработчик и создаешь или используешь Python библиотеки, то теперь это делается легко и без боли. Я говорю это, потому что в течении долгого времени средства Python для работы с зависимостями были… с проблемами. Это уже не так, но многие люди продолжают читать о «проблемах с зависимостями у Python» и думают, что ничего не поменялось. Пора все рассказать.
Небольшой исторический экскурс. С зависимостями у Python дело обстояло примерно так…
Рассвет
Python увидел свет в те времена, когда добавление зависимости означало долгое путешествие в киберспейс. Для начала нужно было дождаться освобождения телефонной линии. Затем позвонить провайдеру. Позвонить по модему, что характерно. После боя с SLIP или PPP клиентом можно было задать вопрос в одну из групп, знает ли кто-нибудь хороший gopher-сайт, где можно найти библиотеку для решения стоящей перед тобой задачи. После чего можно было отключиться от интернета и подождать несколько десятков часов: вдруг кто-нибудь да ответит. Если повезло, то можно было поставить закачку на ночь.
Никакого pip search тогда не было!
Для того времени подход Python к управлению зависимостями был удивительно прозорливым. Директива «import» и расширяемая система загрузки модулей позволяла подключать зависимости откуда угодно.
Вместе с версией 2.0 Python (октябрь 2000 года) состоялся релиз Distutils. С его помощью разработчики смогли абстрактно описывать их коллекции модулей и создавать пригодные для распространения коллекции модулей и пакетов. И снова, это было очень, очень прозорливое решение. В те времена ни о чем подобном не было и речи.
Перематываем время к 2004 году. Создание setuptools для решения типовых задач, с которыми сталкиваются разработчики open source при распространении своих модулей через интернет. В 2005 была добавлена утилита easy_install для автоматического определения зависимостей и скачивания их в нужные директории.
Темные века
К сожалению, вместе с простыми механизмами описания зависимостей, setuptols притащили с собой чудовищное количество сложности. Автор посчитал, что директива «import» должна работать немного по-другому, и установка setuptools изменила ее поведение. По сравнению с обычной директивой «import», версия setuptools поддерживала работу с несколькими версиями одной и той же зависимости в рамках одной программы. Тогда это казалось замечательной идеей. Но время показало, что это не так. Более того, схожая идея возможности установки нескольких версий библиотеки на один компьютер (а не одновременно для одной программы) является важной и нужной штукой.
В дополнение к этому идеологическому отходу от стандартной семантики Python, setuptools имела проблемы с майнтейнером. Точнее, с отсутствием майнтейнеров. Она стала критичной частью Python экосистемы как раз в тот момент, когда автор решил заняться другими проектами — совершенно несвязанными с программированием. Довольно долго никто не мог договориться, кто и как будет поддерживать проект. Который, к тому времени, с одной стороны был уже форкнут, а с другой стороны, во многих дистрибутивах «окаменела» старая и глючная версия.
С 2008 по 2012 года управление зависимостями в Python представляло из себя не лучшее зрелище. Пользоваться им было по меньшей мере болезненно. Было непонятно, какие библиотеки и утилиты использовать, в какие стоит инвестировать свое внимание и изучать. Ручное управление зависимостями было утомительно, тогда как автоматизация требовала множество слабо документированных обходных путей.
Это еще не говоря о многочисленных дырах безопасности в разных частях тулчейна. Не было возможности запаковать и распространить Python библиотеку с нативным кодом так, чтобы у пользователя не было необходимости иметь на своем компьютере полностью настроенный компилятор.
В то же самое время все большую популярность стали завоевывать новые языки и экосистемы, в которых управление зависимостями было реализовано более-менее правильно с самого начала и которые имели лучшую поддержку распространения бинарных файлов. Эти решения выучили свой урок на ошибках Python и Perl.
И, наконец, Python Package Index. Сайт, хранящий и распространяющий все open source библиотеки, созданные сообществом Python. Не более чем proof-of-concept, который был запущен слишком рано, не имел никаких ресурсов для развития своей инфраструктуры и слишком часто становился оффлайн.
Все складывалось не лучшим образом для Python.
Интерлюдия
Вот мы и подошли к тому моменту, когда говорят, что «управление зависимостями в Python — #$@#@@!». Большинство таких высказываний — устаревшая информация, описывающая этот период времени. Множество блогпостов с жалобами, высоко ранжируемые поисковыми системами по запросу «проблемы». Те, кто использовал Python в это время и перешел на использование других языков, часто ворчат о том, как ужасно было управлять зависимостями, насколько сломана была экосистема и как часто лежал PyPI. Хуже всего, в сети есть множество советов по обходным путям решения проблем, которые уже давно не нужны, но все еще показываются поисковыми системами в топе выдачи. Следование таким советам ломает пользователям рабочее окружение и только усугубляет ситуацию.
Восставшие из пепла
И посредине этого великого полома ряд разработчиков героически, молча, медленно все чинили, по одному вгрызаясь в багрепорты. Стартовал проект «pip», многочисленные мейнтейнеры которого убрали большую часть сложности, что принесла «easy_install», и исправили многие ее недостатки. Donald Stufft взялся сразу за Pip и PyPI, исправив старые уязвимости и сделав систему более устойчивой. Daniel Holth написал PEP для формата «wheel», который позволяет распространять бинарный код вместе с библиотеками. То есть, другими словами, позволяет пользователями без настроенного компилятора «C» устанавливать пакеты, которые в противном случае потребуют этот компилятор для сборки.
В 2013 году setuptools снова объединился со своим форком distribute, что позволило вендорам операционных систем обновить системы работы с зависимостями и дать пользователям современные инструменты. Модуль «ensurepip» был включен в Python Core дистрибьюцию для версий 2.7 и 3.3, что позволило пользователям с более-менее современной версией Python одной командой получить себе работающее окружение для работы с зависимостями.
Новый Ренессанс
Я не буду рассказывать про современные приемы работы с зависимостями. Этому посвящен отдельный вебсайт. Но я покажу, насколько это сейчас просто. Для любой современной версии Python, если вы хотите получить работающее окружение без админских прав, все что вам нужно это:
$ python -m ensurepip --user
$ python -m pip install --user --upgrade pip
$ python -m pip install --user --upgrade virtualenv
Для каждого проекта вы можете создать собственную «песочницу» virtualenv:
$ python -m virtualenv lets-go
$ . ./lets-go/bin/activate
(lets-go) $ _
Это все. Можно использовать pip, чтобы устанавливать любые зависимостями. Для установки большинства вам даже не потребуется компилятор! Более того, эти заклинания не зависят от версии Python: они будут работать с Python 2, Python 3, PyPy и даже Jython (примечание переводчика: сюрприз, да?).
На самом деле, часто вам даже не потребуется шаг с «ensurepip», так как «pip» будет уже установлен и настроен! Тем не менее, лучше выполнить это простое заклинание, вреда оно не нанесет.
Более продвинутые операции над зависимостями тоже упростились. Нужен компилятор «C»? Создатели операционных систем плотно работали с сообществом, чтобы сделать настройку компилятора максимально безболезненной:
$ apt install build-essential python-dev # ubuntu
$ xcode-select --install # macOS
$ dnf install @development-tools python-devel # fedora
C:\> REM windows
C:\> start https://www.microsoft.com/en-us/download/details.aspx?id=44266
Ладно-ладно, последний шаг не такой уж безболезненный. Но компилятор хотя бы есть, бесплатен и устанавливается простым скачиванием с сайта! Хотите загрузить что-нибудь на PyPI? Для большинства проектов это будет вот настолько просто:
$ pip install twine
$ python setup.py sdist bdist_wheel
$ twine upload dist/*
Хотите собрать бинарные пакеты wheels? И для этого есть приложение (docker container, разумеется).
Что важно, можно уверенно говорить о высоком онлайне PyPI. А еще у них со дня на день будет новый, полностью переделанный сайт (примечание переводчика: еще один сюрприз).
Я много занимаюсь Python разработкой, и могу сказать, что самая серьезная проблема, с которой я столкнулся за последний год, решилась удалением закешированных файлов. Я использую современный тулчейн каждый день, и он работает!
Работа, которая нам предстоит
Текущая ситуация уже неплоха. Но ее нельзя назвать «замечательной». Что бы я еще хотел от нашей экосистемы:
- Тулзы для установки на компьютеры конечных пользователей могут быть лучше.
- Pip не повредит графический пользовательский интерфейс, чтобы его можно было использовать без заклинаний командной строки (примечание переводчика: и тут Остапа понесло…)
- Нужны тулзы для автоматического создания и обновления «setup.py». Ну или что-нибудь вроде «setup.python.json» или чего-нибудь подобного, чтобы не было необходимости писать код только ради пары строк метаданных.
- Сообщения об ошибках, когда не получается собрать что-то сишным компилятором, должны быть более понятны и содержать инструкции для пользователя.
- PyPI должен автоматически собирать «wheels» для всех платформ по факту загрузки sdists пакета. Да, это большая и амбициозная задача, но подумайте, насколько она упростит работу с зависимостями!
Я могу так долго продолжать. Есть множество способов улучшить работу с зависимостями в Python.
Послесловие
Что я хотел сказать этим лонгридом. Ситуация с зависимостями в Python далека от идеала, но уже нельзя говорить, что у других языков программирования все «намного лучше». Для Go продолжаются обсуждения разных вариантов управления зависимостей и ситуация с «CGo» далека от идеала. У Node собственные горячо обсуждаемые проблемы с культурой управления зависимостями и тулчейном. Hackage крут и все такое, но время сборки достигает поистине астрономических значений. Как обычно, у Rust с Cargo все почти идеально –, но ведь никто из читающих эти строки не использует Rust?
Не хочу сказать, что работа с зависимостями в этих языках как-то особо плоха. На самом деле у них все очень неплохо, особенно если сравнивать это со средней температурой по больнице пару лет назад; наблюдается постоянный прогресс и видимые для конечного пользователя улучшения.
По моему мнению, любое утверждение о том, что в каком-то языке работа с зависимостями намного лучше чем в Python, скорее всего устарело. Сейчас зависимостями в Python уже можно пользоваться, и это не больно. Конечно, может быть ещё лучше, но над улучшением работают множество программистов, и все основные барьеры, которые препятствовали дальнейшему развитию, они уже успешно убрали.
Пользуйтесь virtualenv! Хакните себе немного setup.py! Если последний раз вы работали с зависимостями в Python давно — попробуйте сейчас. Обещаю, все стало намного лучше.