Инструменты упаковки Python: разбираем сложную историю дистрибуции
Для кого эта статья:
- Python-разработчики, желающие улучшить свои навыки в упаковке и дистрибуции проектов
- Новички, сталкивающиеся с проблемами выбора инструментов для дистрибуции Python-пакетов
Специалисты, которые хотят понять эволюцию и текущее состояние систем упаковки в Python экосистеме
Когда я впервые столкнулся с задачей упаковки Python-проекта для распространения, мой мозг почти взорвался: distutils, setuptools, distribute, distutils2... Почему нельзя просто иметь ОДИН стандартный инструмент? Мир дистрибуции Python-пакетов напоминает запутанное семейное древо, где все как-то связаны, но никто толком не объясняет, кто кому приходится родственником. Давайте распутаем этот клубок инструментов вместе, чтобы вы смогли принять осознанное решение и не тратить драгоценные часы на эксперименты. 🕵️♂️
Прокачать свои навыки в области Python-разработки и навсегда забыть о проблемах с упаковкой и дистрибуцией проектов можно на курсе Обучение Python-разработке от Skypro. Здесь вы не просто изучите современные инструменты упаковки, но и получите практический опыт создания полноценных приложений, готовых к публикации в PyPI. Обучение строится на реальных кейсах с профессиональными ревью кода.
История развития инструментов дистрибуции Python
История инструментов дистрибуции Python напоминает эволюционное древо с множеством мутаций, ответвлений и тупиковых ветвей. Все началось довольно невинно: в 2000 году в стандартной библиотеке Python появился модуль distutils, разработанный Грегом Уордом. Этот инструмент предлагал базовый механизм для упаковки и распространения Python-кода.
В 2004 году Филлип Дж. Эби, разочарованный ограничениями distutils, создал setuptools, значительно расширив возможности управления пакетами. Появилась поддержка зависимостей, автоматической загрузки пакетов из PyPI, установка в "режиме разработки" и многие другие возможности, которые мы принимаем как должное сегодня.
Антон Соловьев, тимлид Python-разработки
Однажды я присоединился к проекту, где codebase существовал уже около 10 лет. Первые компоненты были упакованы с помощью чистого distutils, затем постепенно команда перешла на setuptools. Когда я начал разбираться, почему некоторые модули устанавливаются не так, как ожидалось, то обнаружил настоящий зоопарк из различных подходов к упаковке.
"Почему у нас тут такое разнообразие?" — спросил я архитектора.
"Ну, сначала мы использовали distutils, потому что ничего другого не было. Затем setuptools казался революционным с его entrypoints и findpackages(). А distribute мы начали использовать, когда setuptools перестал активно поддерживаться. Потом они снова объединились... Честно говоря, сам не помню все переходы."
Мне пришлось стандартизировать систему сборки во всем проекте, и это заняло почти две недели. Переход на унифицированный setuptools сократил время сборки проекта на 40% и избавил от периодических загадочных ошибок установки.
Однако setuptools не был идеальным. Проблемы с совместимостью, непоследовательные обновления и трудности в поддержке привели к тому, что в 2008 году появилась "вилка" проекта — distribute. Он был совместим с setuptools, но с улучшенной поддержкой Python 3 и лучшей кодовой базой.
Примерно в то же время Python-сообщество инициировало проект distutils2 (временно известный как "packaging") — попытку стандартизировать систему упаковки и включить лучшие идеи из setuptools обратно в стандартную библиотеку. К сожалению, distutils2 никогда не был завершен.
В 2013 году произошло знаменательное событие: distribute был "воссоединен" с setuptools, который снова стал активно разрабатываться. Фактически, distribute прекратил существование, влившись в setuptools 0.7.
К 2014 году distutils2 был официально признан заброшенным, а setuptools укрепил свои позиции как de-facto стандарт для упаковки Python-проектов. В 2016 году появилось новое поколение инструментов — pip начал поддерживать формат wheel, появились flit, poetry и другие альтернативные системы упаковки, основанные на PEP 517/518.
| Год | Событие | Значимость |
|---|---|---|
| 2000 | Появление distutils в стандартной библиотеке | Первый стандартный инструмент упаковки |
| 2004 | Создание setuptools | Революция в управлении зависимостями |
| 2008 | Появление distribute как форка setuptools | Улучшенная поддержка Python 3 |
| 2011 | Начало работы над distutils2 | Попытка стандартизации |
| 2013 | Воссоединение distribute и setuptools | Консолидация экосистемы |
| 2014 | Официальное признание distutils2 заброшенным | Конец попытки стандартизации |
| 2016+ | Появление новых инструментов (poetry, flit) | Современный подход к упаковке |

Distutils: базовая система распространения пакетов
Distutils — это исходный, базовый инструмент для создания и распространения Python-пакетов, встроенный в стандартную библиотеку. Его имя происходит от "distribution utilities" — утилиты для дистрибуции. Несмотря на свой почтенный возраст, он до сих пор лежит в основе большинства современных решений для упаковки Python-кода.
Основные возможности distutils:
- Компиляция модулей расширения на C
- Создание дистрибутивов исходного кода (source distributions, sdist)
- Установка пакетов в системные директории Python
- Базовый механизм настройки через setup.py
- Обработка метаданных пакета (версия, автор и т.д.)
Ключевым компонентом distutils является файл setup.py, который содержит информацию о пакете и инструкции для его установки. Вот простой пример такого файла:
from distutils.core import setup
setup(
name='MyPackage',
version='1.0',
description='A sample Python package',
author='Python Developer',
author_email='dev@example.com',
packages=['mypackage'],
)
Этот минимальный setup.py позволяет выполнять базовые операции с пакетом:
python setup.py build— сборка пакетаpython setup.py install— установка пакетаpython setup.py sdist— создание архива с исходным кодом
Однако у distutils есть существенные ограничения, которые привели к появлению более продвинутых альтернатив:
- Нет механизма управления зависимостями
- Отсутствие автоматической загрузки пакетов из репозиториев
- Нет поддержки "разработческой" установки (develop/editable mode)
- Ограниченная поддержка метаданных
- Нет механизма точек входа (entry points) для создания консольных скриптов
Distutils остается в стандартной библиотеке, но фактически находится в режиме обслуживания. Новая функциональность не добавляется, хотя критические исправления всё еще вносятся. Python-сообщество рекомендует использовать более современные инструменты, которые построены поверх distutils или полностью его заменяют.
Дмитрий Орлов, DevOps-инженер
В нашей компании был небольшой внутренний пакет для сбора метрик с серверов, который использовал чистый distutils для установки. Обычно это не вызывало проблем — пакет был простым и не имел внешних зависимостей.
Всё изменилось, когда мы начали переходить на Python 3.10. Один из разработчиков решил добавить в пакет зависимость от библиотеки для работы с Prometheus. При установке начались странные ошибки: зависимости не устанавливались автоматически, а при ручной установке возникали конфликты версий.
"Почему мы до сих пор используем голый distutils? Ему же лет 20!" — спросил я автора пакета.
"Когда я писал этот код, setuptools казался излишним усложнением для такого простого пакета," — ответил он.
Мы потратили полдня на диагностику проблем, прежде чем решили перейти на setuptools. Замена четырех строк в setup.py решила все проблемы, а добавление зависимостей в install_requires автоматизировало установку нужных версий пакетов. Теперь у нас правило: даже для самых простых внутренних пакетов используем как минимум setuptools.
Setuptools и distribute: расширенные возможности и отличия
Setuptools стал ответом сообщества на ограничения distutils, предложив расширенный функционал для управления Python-пакетами. Фактически, setuptools создавался как надстройка над distutils, сохраняя обратную совместимость, но значительно расширяя возможности.
Основные преимущества setuptools по сравнению с distutils:
- Управление зависимостями через параметр install_requires
- Автоматический поиск пакетов с помощью find_packages()
- "Разработческий" режим установки (develop)
- Поддержка точек входа (entry points) для создания исполняемых скриптов
- Namespace packages для распределения пакета по нескольким дистрибутивам
- Расширенная поддержка метаданных и классификаторов
- Easy Install — инструмент для автоматической загрузки и установки пакетов
- Pkg_resources — API для работы с ресурсами пакета
Типичный setup.py для setuptools выглядит так:
from setuptools import setup, find_packages
setup(
name='MyPackage',
version='1.0',
description='A sample Python package',
author='Python Developer',
author_email='dev@example.com',
packages=find_packages(),
install_requires=[
'requests>=2.25.0',
'numpy',
],
entry_points={
'console_scripts': [
'mycommand=mypackage.cli:main',
],
},
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
],
)
Но в истории setuptools был неспокойный период. К концу 2000-х годов проект столкнулся с проблемами в развитии: недостаточно активная поддержка, проблемы с совместимостью с Python 3 и накопившийся технический долг. Это привело к появлению distribute — форка setuptools, нацеленного на решение этих проблем.
Distribute сохранял полную обратную совместимость с setuptools, но имел следующие улучшения:
- Лучшая поддержка Python 3
- Более чистая и поддерживаемая кодовая база
- Регулярные выпуски новых версий
- Активное сообщество разработчиков
- Улучшенное тестирование
С точки зрения API и использования, distribute был практически неотличим от setuptools — код, работающий с одним, работал и с другим. Различия были "под капотом". Именно поэтому distribute быстро стал популярным и начал вытеснять оригинальный setuptools.
Эта ситуация не могла продолжаться вечно. В 2013 году произошло знаменательное событие — distribute "воссоединился" с setuptools. Фактически, код distribute был интегрирован в setuptools версии 0.7, а сам проект distribute был объявлен устаревшим. Разработчикам рекомендовалось переходить (или возвращаться) к setuptools.
| Функция | Distutils | Setuptools | Distribute |
|---|---|---|---|
| Управление зависимостями | ❌ | ✅ | ✅ |
| Автоматический поиск пакетов | ❌ | ✅ | ✅ |
| Разработческий режим | ❌ | ✅ | ✅ |
| Точки входа | ❌ | ✅ | ✅ |
| Namespace packages | ❌ | ✅ | ✅ |
| Поддержка Python 3 | Ограниченная | Ранее слабая, сейчас хорошая | ✅ |
| Современный статус | В поддержке (legacy) | Активно развивается | Слит с setuptools |
После воссоединения setuptools стал снова активно разрабатываться, получил лучшую поддержку Python 3 и современных практик упаковки. Сегодня setuptools — это де-факто стандарт для упаковки Python-проектов, хотя уже появляются и набирают популярность альтернативные инструменты, такие как poetry и flit. 🛠️
Distutils2: попытка стандартизации и современный статус
Distutils2 представлял собой амбициозную попытку стандартизировать и улучшить систему упаковки Python, перенеся лучшие идеи из setuptools обратно в стандартную библиотеку. Проект был запущен в 2009 году с целью решить проблемы, связанные с разрозненной экосистемой инструментов дистрибуции.
Ключевые цели distutils2 включали:
- Создание стандартизированной, официальной системы упаковки Python
- Перенос лучших функций setuptools в стандартную библиотеку
- Отказ от императивного подхода (setup.py) в пользу декларативной конфигурации (setup.cfg)
- Улучшенная поддержка метаданных и зависимостей
- Более последовательный API для разработчиков пакетов
- Внедрение PEP 345 (улучшенные метаданные) и PEP 386 (стандартизированные версии)
Одним из ключевых отличий distutils2 был переход от использования программного файла setup.py к конфигурационному файлу setup.cfg. Это позволяло описывать пакет декларативно, без выполнения произвольного Python-кода, что повышало безопасность и предсказуемость:
[metadata]
name = MyPackage
version = 1.0
description = A sample Python package
author = Python Developer
author-email = dev@example.com
[files]
packages = mypackage
[requires]
python-version = >=3.6
[install_requires]
requests = >=2.25.0
numpy =
Однако, несмотря на амбициозные цели и серьезную работу, проект столкнулся с рядом проблем:
- Сложность координации разработки между многочисленными участниками
- Трудности интеграции с существующей экосистемой
- Потеря основных разработчиков и снижение активности
- Запаздывание относительно быстро развивающихся альтернатив
К 2012 году разработка distutils2 значительно замедлилась, а в 2013 году большая часть работы фактически прекратилась. В 2014 году проект был официально признан неактивным.
Хотя distutils2 как цельный проект не увидел свет, многие его идеи и наработки не пропали даром. Они были реализованы в новых PEP и инструментах:
- Концепция декларативной конфигурации позже была реализована в setuptools через setup.cfg и pyproject.toml
- Работа над метаданными пакетов повлияла на разработку PEP 621
- Разделение описания пакета и механизма сборки легло в основу PEP 517/518
- Библиотека packaging, начатая как часть distutils2, выжила как отдельный проект
В чем же сейчас заключаются современные рекомендации Python-сообщества после "неудачи" distutils2? Вот текущий консенсус:
- Setuptools остается наиболее широко используемым и рекомендуемым инструментом
- Для новых проектов следует рассмотреть современные альтернативы, такие как poetry или flit
- pyproject.toml (PEP 517/518) становится стандартом конфигурации для инструментов сборки
- Пакеты должны поддерживать wheel-формат для более быстрой и надежной установки
- pip утвердился как стандартный установщик пакетов, заменив easy_install
Несмотря на то, что distutils2 не стал тем универсальным решением, на которое надеялось сообщество, его влияние на развитие экосистемы Python-упаковки невозможно переоценить. Многие идеи и концепции, первоначально разработанные для distutils2, сейчас формируют основу современных инструментов и практик. 📦
Выбор оптимального инструмента для вашего Python-проекта
После рассмотрения истории и особенностей различных инструментов упаковки, давайте ответим на главный вопрос: какое решение выбрать для вашего проекта? Ответ зависит от нескольких факторов, включая сложность проекта, требования к совместимости и ваши предпочтения как разработчика.
Вот мои рекомендации по выбору инструмента упаковки в 2023 году:
- Setuptools — оптимальный выбор для большинства проектов. Он хорошо документирован, активно поддерживается, имеет огромную экосистему и совместим практически со всем.
- Poetry — отличный выбор для новых проектов, особенно если вы цените современный, более декларативный подход и хотите иметь интегрированное управление зависимостями и виртуальными окружениями.
- Flit — подходит для простых библиотек, особенно если вы хотите минимализировать конфигурацию и получить самый короткий путь к публикации в PyPI.
- PDM — многообещающий новичок, сочетающий лучшие идеи poetry и pip, с хорошей совместимостью с PEP 517/518.
- Hatch — современный инструмент с акцентом на управление средами, тестирование и публикацию.
Что касается других инструментов из нашего обзора:
- Distutils — слишком ограничен для современной разработки. Не рекомендуется для новых проектов.
- Distribute — больше не существует как отдельный проект, влился в setuptools.
- Distutils2 — заброшен и никогда не был завершен. Не следует использовать.
При выборе инструмента упаковки, учитывайте следующие критерии:
- Сложность проекта: Для сложных проектов с множеством зависимостей setuptools или poetry будут более подходящими.
- Требования к совместимости: Если ваш пакет должен работать в различных средах и с различными версиями Python, setuptools обеспечивает наилучшую совместимость.
- Опыт команды: Если ваша команда уже знакома с определенным инструментом, это может стать весомым аргументом в его пользу.
- Требования CI/CD: Некоторые инструменты лучше интегрируются с конкретными платформами непрерывной интеграции.
- Тип проекта: Библиотека, приложение или инструмент командной строки могут иметь разные оптимальные решения для упаковки.
Вот сравнительная таблица современных инструментов упаковки Python по ключевым функциям:
| Функция | Setuptools | Poetry | Flit | PDM |
|---|---|---|---|---|
| Управление зависимостями | ✅ Базовое | ✅ Продвинутое | ✅ Базовое | ✅ Продвинутое |
| Разрешение зависимостей | Через pip | Встроенное | Через pip | Встроенное |
| Управление виртуальными окружениями | ❌ | ✅ | ❌ | ✅ |
| Публикация в PyPI | ✅ Через twine | ✅ Встроенная | ✅ Встроенная | ✅ Встроенная |
| Формат конфигурации | setup.py, setup.cfg, pyproject.toml | pyproject.toml | pyproject.toml | pyproject.toml |
| Сложность освоения | Средняя | Низкая | Очень низкая | Низкая |
| Поддержка C-расширений | ✅ Нативная | ✅ Через setuptools | Ограниченная | ✅ Через setuptools |
Лично я рекомендую следующий подход:
- Для новых проектов начните с poetry, если у вас нет особых требований к совместимости или C-расширениям.
- Для проектов с C-расширениями или сложными требованиями к сборке используйте setuptools с pyproject.toml.
- Для простых библиотек без внешних зависимостей рассмотрите flit для максимально быстрого старта.
- Для существующих проектов на setuptools нет необходимости мигрировать, если у вас нет конкретных проблем.
Помните, что независимо от выбранного инструмента, следуйте современным практикам: используйте pyproject.toml для конфигурации, предоставляйте wheel-дистрибутивы, следуйте семантическому версионированию и поддерживайте актуальные метаданные для вашего пакета. 🚀
Выбор инструмента для упаковки Python-проектов — это не просто техническое решение, но и стратегический выбор, влияющий на долгосрочное развитие вашего проекта. Хотя история distutils, setuptools, distribute и distutils2 демонстрирует сложный путь эволюции этих инструментов, современная экосистема предлагает ясные ориентиры. Для большинства проектов setuptools остается "безопасным" выбором, в то время как poetry представляет собой современную альтернативу с интегрированным управлением зависимостями. Главное — не бояться экспериментировать и выбирать инструмент, соответствующий вашим конкретным потребностям, а не слепо следовать трендам.