Пакетирование и деплоймент
Что умеет pip install:
- Скачать исходники, собрать и установить их
- Нужны сборочные зависимости
В случае модулей на Си это может быть очень непросто
/ ⇒ часто встречаются прихаканные в исходниках бинарники (например, скомпилированные переводы, а то и прямо исполняемые файлы, как в cmake)
Публиковать нужно и то, и то (лицензия, вопросы доработки и т. п.), а вот рассчитывать на сборку «на месте» не стоит.
Сборка
Две задачи:
Сформировать дистрибутив (wheel)
- ⇒ Предварительно собрать все целевые генераты
Сформировать т. н. sdist — полный комплект исходных текстов, достаточный для сборки в окружении только сборочных зависимостей
- ⇒ Предварительно убрать все генераты и мусор
На примере Setuptools
pyproject.toml
vs. setup.cfg
vs. setup.py
Tom's Obvious Minimal Language: tomli (< 3.10), tomllib (⩾ 3.11)
- не поддерживает чтение комментариев
(BTW, ∃ tomli-w — write-only)
pyproject.toml
- Нужны почти все)
Минимальный шаблон (при сборке с помощью setuptools / build)
[project] name="Имя" description="Описание" version="версия" readme="README.md" [build-system] requires = ["Сборочные", "зависимости"] build-backend = "setuptools.build_meta" [tool.setuptools.package-dir] Имя="Каталог" [tool.setuptools.package-data] Имя=["Список", "шаблонов"]
Имя — название пакета
Сборочные, зависимости — список пакетов, необходимых для сборки
Каталог — каталог, в котором лежат все файлы пакета (чаще всего совпадает с именем, но не всегда)
В пакет попадают только файлы .py
Список шаблонов — шаблоны с именами не-.py файлов из каталога Каталог (сам Каталог в шаблонах отсутствкет), которые тоже должны упаковываться в пакет Имя
Особенность TOML: однострочный и секционированный форматы представления одних и тех же данных (отсюда и далее
- Другие системы сборки
Особенность pyproject.toml: простой и сложный варианты описания:
Reference implementation: build
Что должно входить в wheel / sdist?
- Пакет(ы) c python-файлами
- Сценарии запуска (см. далее про точки входа)
- Непрограммные файлы (картинки, переводы, встроенная документация и т. п.)
Для того, чтобы упаковать какие-то дополнительные файлы в дистрибутив, необходимо, чтобы после сборки эти файлы (неважно, генераты или нет) оказались в каталоге с пакетом
Проблема: после изобретения Namespace packages любой каталог внутри пакета может считаться вложенным подпакетом; это приводит к ошибкам
Описать их в include-package-data
Будут считаться namespace-пакетами, но это без вариантов: возможности поставить файлы из пакета куда-то мимо PYTHONPATH у pip-а нет
Для того, чтобы упаковать в исходный дистрибутив дополнительные файлы (переводы, тесты и т. п.), их надо перечислить в заготовке файла-манифеста MANIFEST.in. Некоторые файлы включаются автоматически.
- Имеет смысл проследить за тем, чтобы в исходном дистрибутиве было всё, что лежит в репозитории
Более простой пример, в котором:
- Генерируются и упаковываются в wheel переводы
- Различные нужные файлы упаковываются в архив исходников
Babel и doit не указаны в devel-зависимостях pyproject.toml (сам setuptools мим не пользуется), но есть Pipfile
TODO: Научиться запускать doit прямо из setuptools
- Возможно, это не самый очевидный путь
Пакетные зависимости
- Эксплуатационные зависимости
- Набор модулей, необходимых для эксплуатации приложения
- Обязательные и предполагаемые
- Включённые в python-инфраструктуру и все остальные (с этим проблема)
- Сборочные зависимости
Набор модулей и ПО, необходимый для разработки
+ Средства сборки
+ Средства тестирования
- Возможно, часть эксплуатационных зависимостей замещена квазиобъектами
Отслеживание зависимостей
∃ Более сложный инструмент — poetry, но он предписывает определённый workflow и скрывает многие действия под капотом, попробуем разобраться на нижнем уровне.
- Скопировать в чистое окружение и тупо запускать и добавлять в зависимости всё, из-за отсутствия чего падает)
Сделать pip freeze и угадать, что из этого всего понадобится при запуске
Соблюдать дисциплину с помощью pipenv:
Для установки чего не попадя — pip install что ни попадя
Для установки эксплуатационных зависимостей — pipenv install эксплуатационные зависимости
записывается в секцию [packages] файла Pipfile
pipenv install пакет обновляет Pipfile, даже если до этого был сделан pip install пакет
Для установки сборочных зависимостей — pipenv install -d сборочные зависимости)
Обновляет секцию [dev-packages] в Pipfile
При формировании дистрибутива / архива исходников
Эксплуатационные зависимости
Хранятся в секции project.dependencies файла pyproject.toml статически заданными.
Вариант: хранить «по старинке» в requirements.txt, но генерировать его:
В pyproject.toml написать
[project] … dynamic = ["dependencies"] [tool.setuptools.dynamic] dependencies = { file = "requirements.txt" }
Если используется pipenv или аналог + все эксплуатационные пакеты установлены с помощью pipenv install
- либо pipenv requirements, либо вручную разобрать секцию [packages] в Pipfile (он тоже TOML).
- - установить в сборочное окружение все эксплуатационные пакеты не всегда возможно / хочется
- Можно отследить по наличию import-ов в модуле.
- В т. ч. автоматически, но
Есть прорва инструментов, ни одного годного
Часть из них импортит внутренности pip, что запрещено ☹ (пример: pip-check-reqs, оно пока работает)
Самый (?) адекватный — поисковик python3 зависимостей
Автор — Daniel Zagaynov
Сборочные зависимости. Строго говоря, не хранятся нигде — но нужны всем участникам разработки.
Можно составлять вручную и записывать в файл requirements-dev.txt
При этом можно анализировать установленные в виртуальное окружение пакеты — смотреть на pip freeze или pipenv requirements --dev-only (если испльзуется pipenv или аналог + дисциплина установки сборочных пакетов pipenv install -d). Но там будут все пакеты, поставленные по зависимостям и их строгие версии.
Если используется pipenv или аналог + дисциплина установки сборочных пакетов (pipenv install -d), можно разобрать секцию [packages-dev] в Pipfile) — это просто TOML
Можно отследить путём трассировки import-ов при полной сборке
Но инструмента такого, похоже, нет!
Прототип инструмента, отслеживающего сборочные заивсимости
Пример такого инструмента: pip запускается из командной строки. Выдаёт довольно адекватную информацию, как минимум про репозиторий с примером
Составить словарь: какому из установленных пакетов какой файл принадлежит (
эта задача почему-то ещё не решена)
Учитывать только пакеты, установленные непосредственно, а не по зависимостям (например, с помощью pip list --not-required)
Запустить все инструменты сборки и тестирования с трассировкой (PYTHONVERBOSE=import python … или python -v …)
- Проанализировать журнал трассировки на предмет использования файлов, принадлежащих пакетам из локального окружения
Возможны ситуации, когда для сборки требуется инструмент, который был поставлен автоматом по зависимости, а то, что его требовало, для сборки не нужно.
Строгие VS нестрогие зависимости на версии
- Для публикации: скорее всего, нестрогие
- Для разработки (особенно совместной) — лучше строгие
Pipfile vs requirements.txt
Сам pip умеет в requirements.txt: pip install -r
Сборочные зависимости можно положить просто в отдельный файл (requirements-dev.txt, например)
pip freeze не умеет отличать «мусор» (левые пакеты) и пакеты установленные только по зависимостям от собственно зависимостей
- сборочные зависимсоти от эксплуатационных он тоже не отличает
- Приходится всё это отслеживать вручную
Pipfile нестандартен
нормально с ним работает только pipenv
- отличает все четыре вида установленных пакетов (см. выше)
- не умеет напрямую работать с «мусором»
только pipenv lock; pipenv --rm; pipenv sync (пересоздание окружения)
pipenv умеет отчитываться в формате requirements:
pipenv lock -r и
pipenv lock -r --dev-only
Точки входа
Для запуска python -m модуль в нём должен присутствовать __main__.py
Для создания стартового сценария в pyproject.toml необходимо описать точки входа
Д/З
Обеспечить в семестровом проекте:
- Сборку wheel
- Установку проекта в чистом окружении из этого wheel (должны притягиваться нужные зависимости)
- Сборку архива с исходниками
В архиве должны присутствовать все нужные файлы и некоторые полезные псевдо-генераты (например, requirements-dev.txt или Makefile от Sphinx, если он используется)
- Запуск и полноценную работу проекта после установки в чистое окружение:
- проверить наличие зависимостей
- Проверить наличие непрогарммных файлов
- Проверить работу переводов
Это весь семестровый проект, готовый к сдаче!