Jump to content

# Python package guidelines (Русский)

4 languages

- English
- 日本語
- Português
- 中文（简体）

From ArchWiki
Состояние перевода: На этой странице представлен перевод статьи Python package guidelines. Дата последней синхронизации: 6 декабря 2023. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.

Этот документ охватывает стандарты и рекомендации по написанию файлов PKGBUILD для программ, работающих на Python.

## Именование пакета

Для библиотек Python 3 используйте `python-имямодуля`. Также используйте префикс, если пакет предоставляет программу, тесно связанную с экосистемой Python (например, pip или tox). Для других приложений используйте только имя программы.

Примечание: Название пакета должно быть полностью в нижнем регистре.

## Архитектура

Смотрите PKGBUILD (Русский)#arch.

Пакет Python, содержащий расширения C, является архитектурно-зависимым. В противном случае он, скорее всего, является архитектурно-независимым.

Пакеты, собираемые с помощью setuptools, определяют свои расширения C с помощью ключевого слова `ext_modules` в `setup.py`.

## Исходники

Примечание: Согласно RFC0020, исходники стоит по возможности получать непосредственно у апстрима, а не из sdist-архивов на PyPI.

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)

Совет: Если сборка производится из исходников апстрима, а апстрим полагается на git для получения строки версии проекта, перед сборкой wheel-пакета необходимо задать значение `$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-пакетов и потому не могут быть собраны из исходных текстов).
Важно: Если ваш пакет является VCS-пакетом (`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

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

Category:

- Arch package guidelines (Русский)