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-пакета верхнего уровня. Если вы столкнулись с этим, то можете помочь, отправив проекту сообщение об ошибке с просьбой исправить это, например, вот так.