Python package guidelines (Русский)
32-bit – CLR – CMake – Cross – DKMS – Eclipse – Electron – Font – Free Pascal – GNOME – Go – Haskell – Java – KDE – Kernel – Lisp – Meson – MinGW – Node.js – Nonfree – OCaml – Perl – PHP – Python – R – Ruby – Rust – Shell – VCS – Web – Wine
Этот документ охватывает стандарты и рекомендации по написанию файлов 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-setuptools-scm:
SETUPTOOLS_SCM_PRETEND_VERSION -
python-pbr:
PBR_VERSION -
python-pdm-backend:
PDM_BUILD_SCM_VERSION
Рабочий процесс, основанный на стандартах, прост: создайте wheel-пакет с помощью python-build и установите его в $pkgdir с помощью python-installer:
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, который вызывает setuptools или distutils. Обратите внимание, что хотя distutils включен в стандартную библиотеку Python, наличие установленного setuptools означает, что вы используете пропатченную версию distutils.
makedepends=('python-setuptools') # если только он не требует именно distutils
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(), если это имеет место.
Некоторые пакеты пытаются использовать setuptools и возвращаются к distutils, если setuptools не может быть импортирован. В этом случае setuptools должен быть добавлен как makedepends, чтобы результирующие метаданные Python были лучше.
Если пакет требует сборки setuptools из-за включения исполняемых файлов (что не поддерживается distutils), но импортирует только distutils, то при сборке будет выдано предупреждение, а полученный пакет будет повреждён (он не будет содержать исполняемых файлов):
/usr/lib/python3.8/distutils/dist.py:274: UserWarning: Unknown distribution option: 'entry_points' warnings.warn(msg)
Необходимо сообщить об ошибке в upstream. Для обхода проблемы можно использовать недокументированную функцию setuptools:
# не работает из-за distutils python setup.py build # работает, используя setuptools shim python -m setuptools.launch setup.py 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 вообще.Большинство проектов Python, предоставляющих набор тестов, используют nosetests или pytest для запуска тестов с test в имени файла или каталога, содержащего набор тестов. В общем случае для запуска набора тестов достаточно просто запустить nosetests или pytest.
check(){
cd "$srcdir/foo-$pkgver"
# Для nosetests
nosetests
# Для pytest
pytest
}
Если есть скомпилированное расширение C, тесты необходимо запускать, используя $PYTHONPATH, отражающий текущую мажорную и минорную версию Python, чтобы найти и загрузить его.
check(){
cd "$pkgname-$pkgver"
local python_version=$(python -c 'import sys; print("".join(map(str, sys.version_info[:2])))')
# Для nosetests
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-${python_version}" nosetests
# Для pytest
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-${python_version}" pytest
}
Некоторые проекты предоставляют setup.py точки входа для запуска теста. Это работает как для pytest, так и для nosetests.
check(){
cd "$srcdir/foo-$pkgver"
# Для nosetests
python setup.py nosetests
# Для pytest - нужен python-pytest-runner
python setup.py pytest
}
Советы и рекомендации
Обнаружение отдельных подписей PGP на PyPI
Если для sdist-архива существуют отдельные PGP-подписи — их следует использовать для проверки архива. Однако файлы подписей не отображаются непосредственно в разделе загрузки файлов какого-либо проекта на pypi.org. Чтобы обнаружить sdist и их потенциальные файлы подписей, можно воспользоваться этим сервисом для получения обзора по каждому проекту: https://pypi.debian.net/.
Для python-requests это будет https://pypi.debian.net/requests.
Использование версии 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-пакета верхнего уровня. Если вы столкнулись с этим, то можете помочь, отправив проекту сообщение об ошибке с просьбой исправить это, например, вот так.