Python package guidelines (Русский)
Этот документ охватывает стандарты и рекомендации по написанию файлов PKGBUILD для программ, работающих на Python.
Именование пакета
Для библиотек Python 3 используйте python-имямодуля. Также используйте префикс, если пакет предоставляет программу, тесно связанную с экосистемой Python (например, pip или tox). Для других приложений используйте только имя программы.
Архитектура
Смотрите PKGBUILD (Русский)#arch.
Пакет Python, содержащий расширения C, является архитектурно-зависимым. В противном случае он, скорее всего, является архитектурно-независимым.
Пакеты, собираемые с помощью setuptools, определяют свои расширения C с помощью ключевого слова ext_modules в setup.py.
Исходники
URL загрузки, связанные с сайтом PyPI, содержат непредсказуемый хэш, который необходимо получать с сайта PyPI каждый раз, когда пакет должен быть обновлён. Это делает их непригодными для использования в PKGBUILD. PyPI предоставляет альтернативную стабильную схему: массив source должен использовать следующие шаблоны URL:
- Архив с исходным кодом
https://files.pythonhosted.org/packages/source/${_name::1}/${_name//-/_}/${_name//-/_}-$pkgver.tar.gz- Пакет wheel, содержащий только Python-код
-
https://files.pythonhosted.org/packages/py2.py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py2.py3-none-any.whl(для пакета, совместимого с Python 2 и Python 3) -
https://files.pythonhosted.org/packages/py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py3-none-any.whl(для пакета, совместимого только с Python 3) - Обратите внимание, что имя дистрибутива может содержать тире, а его представление в имени файла wheel — нет (они преобразуются в символы подчёркивания).
- Архитектурно-зависимый пакет wheel
- Дополнительные массивы, специфичные для архитектуры, могут быть добавлены путём добавления подчёркивания и имени архитектуры, например,
source_x86_64=('...'). Также можно использовать_py=cp310, чтобы не повторять версию Python: https://files.pythonhosted.org/packages/$_py/${_name::1}/$_name/${_name//-/_}-$pkgver-$_py-${_py}m-manylinux1_x86_64.whl
Обратите внимание, что используется переменная _name вместо pkgname, поскольку пакеты Python обычно имеют префикс python-. Эта переменная может быть определена следующим образом:
_name=${pkgname#python-}
Методы установки
Пакеты Python обычно устанавливаются с помощью специального менеджера пакетов, такого как pip, который получает пакеты из онлайн-репозитория (обычно PyPI, Python Package Index) и отслеживает соответствующие файлы.
Однако для управления пакетами Python из PKGBUILD необходимо «установить» пакет Python во временное место $pkgdir/usr/lib/python<версия Python>/site-packages/$pkgname.
Для пакетов Python, использующих стандартные метаданные для указания бэкенда сборки в файле pyproject.toml, это проще всего сделать с помощью python-build и python-installer. Старые пакеты могут не указывать, что они используют setuptools, и предлагать только setup.py, который должен быть вызван вручную.
depends, иначе они не будут установлены.По стандартам (PEP 517)
$pkgver переменным окружения, специфичным для используемого инструментария:
-
python-flit-core, python-hatch-vcs и python-setuptools-scm:
SETUPTOOLS_SCM_PRETEND_VERSION -
python-pbr:
PBR_VERSION -
python-pdm-backend:
PDM_BUILD_SCM_VERSION
Рабочий процесс, основанный на стандартах, прост: создайте wheel-пакет с помощью python-build и установите его в $pkgdir с помощью python-installer:
makedepends также необходимо добавить бэкенд сборки. Все доступные в репозиториях Archlinux бэкенды добавлены в группу python-build-backend.
Чтобы узнать, какой бэкенд необходим для сборки проекта, смотрите строку build-system.build-backend в его файле pyproject.toml. Если бэкенд не указан, по умолчанию будет использоваться python-setuptools.makedepends=(python-build python-installer python-wheel)
build() {
cd $_name-$pkgver
python -m build --wheel --no-isolation
}
package() {
cd $_name-$pkgver
python -m installer --destdir="$pkgdir" dist/*.whl
}
где:
-
--wheelуказывает создать только wheel-пакет, без архива с исходным кодом. -
--no-isolationуказывает, что пакет должен быть собран с использованием установленных в системе зависимостей (включая пакеты, указанные вами вdepends); по умолчанию без этой опции для сборки создаётся изолированное виртуальное окружение. -
--destdir="$pkgdir"предотвращает попытку прямой установки в хост-систему, а не внутрь файла пакета, что привело бы к ошибке доступа -
--compile-bytecode=...или--no-compile-bytecodeможно передать вinstaller, но значение по умолчанию выбрано разумно, поэтому в этом нет необходимости.
build и помещение файла .whl в массив source не рекомендуется и должно использоваться только в тех случаях, когда сборка из исходников невозможна (например, для программ, которые поставляются только в виде wheel-пакетов и потому не могут быть собраны из исходных текстов).python-…-git), добавьте в функцию prepare команду git -C "${srcdir}/${pkgname}" clean -dfx. Это удалит устаревшие wheel-пакеты вместе с другими артефактами сборки и поможет избежать проблем в дальнейшем. Смотрите также issues в setuptools и Poetry.setuptools или distutils
Если pyproject.toml отсутствует или не содержит таблицу [build-system], это означает, что проект использует старый формат с файлом setup.py, который вызывает функцию setup из setuptools или distutils.core. Обычно такие пакеты можно собрать и установить тем же способом, при помощи python-build и python-installer, и это предпочтительный метод, но для таких проектов понадобится добавить в makedepends пакет python-setuptools.
Вы по-прежнему можете собрать и установить такие пакеты, напрямую используя setup.py. Этот способ описан ниже для тех случаев, когда современный PEP 517 не работает или не может быть использован. Тем не менее, помните, что это устаревший подход и использовать его не рекомендуется. Сборочная система сообщит Вам об этом таким предупреждением на этапе package:
SetuptoolsDeprecationWarning: setup.py install is deprecated.
Также имейте в виду, что, начиная с версии 3.12, библиотека distutils не включена в стандартную поставку Python, что означает, что пакеты, собираемые старым способом через setup.py, как правило должны включать пакет python-setuptools в makedepends, поскольку он предоставляет свою собственную версию distutils.
makedepends=('python-setuptools')
build() {
cd $_name-$pkgver
python setup.py build
}
package() {
cd $_name-$pkgver
python setup.py install --root="$pkgdir" --optimize=1
}
где:
-
--root="$pkgdir"работает как--destdirвыше -
--optimize=1заранее компилирует оптимизированные файлы байткода (.opt-1.pyc), чтобы их мог отслеживать pacman, вместо ленивого их создания при запуске программы. - Добавление
--skip-buildоптимизирует ненужную попытку повторного выполнения шагов сборки, уже запущенных в функцииbuild(), если это имеет место.
Если для сборки используется python-setuptools-scm, скорее всего, Вы получите ошибку:
LookupError: setuptools-scm was unable to detect version for /build/python-jsonschema/src/jsonschema-3.2.0. Make sure you're either building from a fully intact git repository or PyPI tarballs. Most other sources (such as GitHub's tarballs, a git checkout without the .git folder) don't contain the necessary metadata and will not work.
Чтобы пакет собрался, нужно экспортировать переменную окружения SETUPTOOLS_SCM_PRETEND_VERSION со значением $pkgver:
export SETUPTOOLS_SCM_PRETEND_VERSION=$pkgver
Проверка
- Избегайте использования
toxдля запуска тестов, поскольку он предназначен для проверки воспроизводимых конфигураций, загруженных из PyPI во время работыtox, и не проверяет версию, которая будет установлена пакетом. Это противоречит целям самой функции check; - Не используйте в checkdepends плагины pytest для линтинга, coverage-тестов и проверки типов (см. раздел #Отключение опций pytest). Они лишь усложняют процесс, но не требуются для сборки пакетов, поскольку не проверяют корректность работы программ (они используются для других проверок и в основном нужны самим разработчикам проекта);
- Избегайте использования относительных путей при установке пользовательского
PYTHONPATHдля тестов, так как это может привести к неочевидным ошибкам. Старайтесь всегда использовать абсолютные пути.
Большинство проектов Python предоставляют набор тестов в файле или каталоге с названием test или tests и используют для их запуска unittest runner из пакета python, nosetests из пакета python-nose или pytest из пакета python-pytest. В общем случае для запуска набора тестов достаточно просто запустить nosetests или pytest.
check(){
cd $srcdir/foo-$pkgver
# встроенный unittest
python -m unittest discover -v
# nosetests
nosetests
# pytest
pytest
}
Если есть скомпилированное расширение C, тесты необходимо запускать, используя $PYTHONPATH, отражающий текущую мажорную и минорную версию Python, чтобы найти и загрузить его.
check(){
cd $_name-$pkgver
local python_version=$(python -c 'import sys; print("".join(map(str, sys.version_info[:2])))')
# встроенный unittest
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" python -m unittest discover -vs .
# nosetests
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" nosetests
# pytest
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" pytest
}
Советы и рекомендации
Указание версии Python
Иногда во время подготовки, сборки, тестирования или установки требуется указать мажорную и минорную версию Python для системы. Не записывайте это жёстко в коде (например, 3.9 или 3.10), а запустите интерпретатор Python для получения информации и сохранения её в локальной переменной:
check(){
local python_version=$(python -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
...
}
Использование site-packages
Иногда во время сборки, тестирования или установки требуется обратиться к системному каталогу site-packages. Не следует записывать путь жёстко в коде, вместо этого запустите интерпретатор Python для получения пути и сохранения его в локальной переменной:
check(){
local site_packages=$(python -c "import site; print(site.getsitepackages()[0])")
...
}
Тестовый каталог в site-packages
Проверяйте, что Вы не установили каталог с именем tests прямо в site-packages (т. е. /usr/lib/pythonX.Y/site-packages/tests/). Это может приводить к конфликтам между пакетами. Некоторые Python-проекты, использующие setuptools, иногда неправильно настроены и добавляют каталог с тестами в качестве Python-пакета верхнего уровня. Если вы столкнулись с этим, то можете помочь, отправив проекту сообщение об ошибке с просьбой исправить это, например, вот так.
Отключение опций pytest
При запуске pytest старайтесь не использовать дополнительные плагины, особенно плагины для линтинга и coverage-тестов: они не нужны для проверки функциональности программы, но изменения в них легко могут привести к сбоям процесса сборки. Для отключения опций pytest (например, addopts), предпочтительнее использовать переопределение параметров через команды файла PKGBUILD, а не вносить изменения патчами, поскольку при обновлении пакетов эти патчи будет сложнее поддерживать.
Чтобы отключить все дополнительные параметры, используйте:
pytest -o addopts=""
Исправление проблемы воспроизводимости с meson-python
meson-python в качестве бэкенда сборки PEP 517 использует случайные пути к папкам, что приводит к проблемам воспроизводимости. Этого можно избежать, жёстко задав каталог с помощью флага -Cbuild-dir:
python -m build --wheel --no-isolation -Cbuild-dir=build
Запуск тестов после установки пакета
Некоторые пакеты необходимо установить для того, чтобы тесты прошли успешно. В таких случаях (например, в пакете python-narwhals) вы можете создать виртуальное окружение, в котором этот пакет будет установлен и протестирован:
python -m venv --system-site-packages test-env test-env/bin/python -m installer dist/*.whl test-env/bin/python -P -m pytest