Spec-Zone.ru › Homebrew

Python для авторов формул

В этом документе объясняется, как успешно использовать Python в формуле Homebrew.

Homebrew различает Python-приложения и Python-библиотеки. Разница в том, что пользователям обычно неважно, написаны ли приложения на Python; обычно пользователь не ожидает, что после установки приложения можно будет import foo. Примеры приложений: ansible и jrnl.

Python-библиотеки предназначены для импорта другими модулями Python; они часто являются зависимостями Python-приложений. Обычно они полезны в терминале лишь попутно. Примеры библиотек: certifi и numpy.

Биндинги — особый вид библиотек, позволяющий коду на Python взаимодействовать с библиотекой или приложением, реализованными на другом языке. Например, биндинги Python, устанавливаемые с помощью libxml2.

Homebrew не возражает против приложений, написанных на Python, независимо от того, доступны ли они в PyPI. Homebrew обычно не принимает библиотеки, которые можно корректно установить с помощью pip install foo. Биндинги можно устанавливать для пакетов, которые их предоставляют, особенно если аналогичная функциональность недоступна через pip. Аналогично, хорошими кандидатами могут быть библиотеки со значительным объёмом нативного кода, компиляция которого занимает много времени. Однако, если сомневаетесь, не добавляйте библиотеки в пакеты.

Приложения должны безусловно включать в себя все зависимости и библиотеки на языке Python и устанавливать любые неудовлетворённые зависимости; эти подходы подробно рассматриваются в следующих разделах.

Приложения

Объявления Python для приложений

Формулы для приложений, которым требуется Python 3, должны объявлять безусловную зависимость от "python@3.y". Эти приложения должны работать с текущей формулой Homebrew Python 3.y.

Установка приложений

Начиная с Python@3.12, Homebrew следует PEP 668. Приложения необходимо устанавливать в виртуальное окружение Python с корневым каталогом в libexec. Это предотвращает загрязнение системного site-packages модулями Python приложения и наоборот.

Все зависимости приложения от модулей Python (а также их зависимости, рекурсивно) следует объявить как resources в формуле и также установить в виртуальное окружение. Каждую зависимость следует указывать явно; не полагайтесь на setup.py или pip при автоматическом разрешении зависимостей по описанным здесь причинам.

Вы можете использовать brew update-python-resources, чтобы помочь себе написать блоки ресурсов. Для этого просто запустите brew update-python-resources <formula>. Иногда brew update-python-resources не удаётся автоматически обновить ресурсы. В этом случае попробуйте запустить brew update-python-resources --print-only <formula>, чтобы вывести блоки ресурсов вместо непосредственного внесения изменений в файл. Затем вы сможете скопировать и вставить нужные ресурсы.

Если brew update-python-resources не работает, воспользуйтесь homebrew-pypi-poet, чтобы помочь себе написать блоки ресурсов. Для этого настройте виртуальное окружение и установите в него свой пакет вместе со всеми его зависимостями. Затем установите pip install homebrew-pypi-poet в то же виртуальное окружение. Запуск poet some_package создаст необходимые блоки ресурсов. Это можно сделать так:

# Use a temporary directory for the virtual environment
cd "$(mktemp -d)"

# Create and source a new virtual environment in the venv/ directory
python3 -m venv venv
source venv/bin/activate

# Install the package of interest as well as homebrew-pypi-poet
pip install some_package homebrew-pypi-poet
poet some_package

# Destroy the virtual environment
deactivate
rm -rf venv

Homebrew предоставляет вспомогательные методы для создания виртуальных окружений и наполнения их содержимым. Чтобы воспользоваться ими, добавьте include Language::Python::Virtualenv в начало определения класса Formula.

Для большинства приложений достаточно написать:

class Foo < Formula
  include Language::Python::Virtualenv

  # ...
  url "https://example.com/foo-1.0.tar.gz"
  sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"

  depends_on "python@3.y"

  def install
    virtualenv_install_with_resources
  end
end

Это в точности то же самое, что написать:

class Foo < Formula
  include Language::Python::Virtualenv

  # ...
  url "https://example.com/foo-1.0.tar.gz"
  sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"

  depends_on "python@3.y"

  def install
    # Create a virtualenv in `libexec`.
    venv = virtualenv_create(libexec, "python3.y")
    # Install all of the resources declared on the formula into the virtualenv.
    venv.pip_install resources
    # `pip_install_and_link` takes a look at the virtualenv's bin directory
    # before and after installing its argument. New scripts will be symlinked
    # into `bin`. `pip_install_and_link buildpath` will install the package
    # that the formula points to, because buildpath is the location where the
    # formula's tarball was unpacked.
    venv.pip_install_and_link buildpath
  end
end

Пример формулы

Установка формулы с зависимостями будет выглядеть так:

class Foo < Formula
  include Language::Python::Virtualenv

  desc "Description"
  homepage "https://example.com"
  url "..."

  resource "six" do
    url "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz"
    sha256 "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"
  end

  resource "parsedatetime" do
    url "https://files.pythonhosted.org/packages/a8/20/cb587f6672dbe585d101f590c3871d16e7aec5a576a1694997a3777312ac/parsedatetime-2.6.tar.gz"
    sha256 "4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455"
  end

  def install
    virtualenv_install_with_resources
  end
end

Можно также воспользоваться более многословной формой и указать ресурсы, которые следует установить:

class Foo < Formula
  include Language::Python::Virtualenv

  desc "Description"
  homepage "https://example.com"
  url "..."

  def install
    venv = virtualenv_create(libexec)
    %w[six parsedatetime].each do |r|
      venv.pip_install resource(r)
    end
    venv.pip_install_and_link buildpath
  end
end

если для разных ресурсов нужно выполнять разные действия.

Биндинги

Чтобы добавить биндинги для Python 3, добавьте depends_on "python@3.y" для работы с текущей формулой Homebrew Python 3.y.

Зависимости для биндингов

Для зависимостей от модулей Python биндингам следует руководствоваться теми же рекомендациями, что и библиотекам; подробности приведены ниже.

Установка биндингов

Если биндинги устанавливаются вызовом setup.py, сделайте примерно следующее:

system "python3.y", "-m", "pip", "install", *std_pip_args(build_isolation: true), "./source/python"

Autotools

Если скрипт configure принимает флаг --with-python, обычно ему не требуется дополнительная помощь для поиска Python. Однако если в дереве зависимостей есть несколько формул Python, может потребоваться подсказать ему, какую из них выбрать.

Если скрипты configure и make не хотят устанавливать файлы в Cellar, иногда можно:

  1. вызвать ./configure --without-python (или параметр с похожим названием)
  2. вызвать pip для каталога с биндингами Python (как описано выше)

Иногда приходится на лету редактировать Makefile, чтобы задать префикс Homebrew для биндингов Python с помощью вспомогательного метода inreplace.

CMake

Если cmake находит Python, отличный от указанного в прямой зависимости, иногда можно помочь ему найти нужный Python, задав одну из следующих переменных с помощью параметра -D:

  • Python3_EXECUTABLE для модуля FindPython3
  • Python_EXECUTABLE для модуля FindPython
  • PYTHON_EXECUTABLE для модуля FindPythonInterp

Meson

Из-за особенностей установки символических ссылок в Homebrew и исправления sysconfig для Python meson может не удаваться автоматически определить каталоги Cellar, в которые следует устанавливать биндинги Python. Если описание сборки meson в формуле использует install_sources() или аналогичные методы, можно задать python.purelibdir и/или python.platlibdir, чтобы переопределить пути по умолчанию.

Если meson находит Python, отличный от указанного в прямой зависимости, а файл определения параметров meson в формуле не предоставляет параметр, задаваемый пользователем, необходимо проверить, как определяется исполняемый файл Python. Часто для этого используется метод find_installation(), поведение которого зависит от значения аргумента name_or_path.

Библиотеки

Помните: случаи, когда библиотеки можно включать в пакеты, крайне ограничены (например, если компилируется значительный объём нативного кода), поэтому, если сомневаетесь, не добавляйте их в пакеты.

Для таких формул мы не используем префикс python-!

Примеры библиотек, допустимых в homebrew-core

  • numpy, scipy: долгая сборка, сложный процесс сборки

  • cryptography: собирается с помощью rust

  • certifi: формула исправлена, чтобы любые формулы на основе Python могли использовать установленные через Homebrew сертификаты CA (см. https://github.com/orgs/Homebrew/discussions/4691).

Объявления Python для библиотек

Библиотеки, собираемые для Python 3, должны включать depends_on "python@3.y", который обеспечит сборку пакета для Python 3.y из Homebrew.

Установка библиотек

Библиотеки можно устанавливать в libexec и добавлять в sys.path, записав файл .pth (с именем вроде «homebrew-foo.pth») в каталог site-packages prefix. Это упрощает устранение последствий случайного использования pip для обновления установленного через Homebrew пакета и предотвращает накопление устаревших файлов .pyc в site-packages Homebrew.

В настоящее время большинство формул просто устанавливают файлы в prefix. С любыми устаревшими файлами .pyc справляется brew cleanup.

Зависимости библиотек

Зависимости библиотек необходимо устанавливать так, чтобы их можно было импортировать. Чтобы свести к минимуму вероятность конфликтов при связывании, зависимости следует устанавливать в libexec/<vendor> и добавлять в sys.path, записав второй файл .pth (с именем вроде «homebrew-foo-dependencies.pth») в каталог site-packages prefix.

Формулам с общими зависимостями от библиотек Python (например, setuptools, six) не следует использовать этот подход, поскольку он загрязнит системный site-packages всеми библиотеками, установленными в libexec/<vendor>.

Глубже в кроличью нору

Дополнительные пояснения о причинах некоторых решений Homebrew.

Setuptools, Distutils и pip

До удаления в Python 3.12 Distutils был модулем стандартной библиотеки Python, предоставлявшим разработчикам базовый API управления пакетами. Setuptools — модуль, распространяемый отдельно от стандартной библиотеки, который расширяет и заменяет Distutils. По соглашению пакеты Python предоставляют setup.py, вызывающий функцию setup() из Distutils или Setuptools.

Раньше Setuptools предоставлял команду easy_install — инструмент управления пакетами для конечных пользователей, который загружал и устанавливал пакеты из PyPI, индекса пакетов Python. Скрипт консоли easy_install был удалён из Setuptools v52.0.0, а его прямое использование считается устаревшим начиная с v58.3.0. pip — другой, более новый инструмент управления пакетами для конечных пользователей, который также распространяется отдельно от стандартной библиотеки. Хотя pip заменяет easy_install, он не заменяет остальные функции модуля Setuptools.

Distutils и pip используют «плоскую» иерархию установки, размещая модули отдельными файлами в site-packages, тогда как easy_install устанавливает zip-архивы egg в site-packages.

Distribute (не путать с Distutils) — устаревшее ответвление Setuptools. Distlib — пакет, поддерживаемый вне стандартной библиотеки и используемый pip для некоторых низкоуровневых операций с пакетами; для большинства пользователей setup.py он не имеет значения.

Запуск setup.py

Если формуле нужно взаимодействовать с setup.py, а не вызывать pip, Homebrew предоставляет вспомогательный метод Language::Python.setup_install_args, возвращающий полезные аргументы для вызова setup.py. В формуле следует использовать его вместо явного вызова setup.py. Синтаксис:

system formula_opt_bin("python@3.y")/"python3.y", *Language::Python.setup_install_args(prefix)

где prefix — целевой префикс (обычно libexec или prefix).

Что такое --single-version-externally-managed?

--single-version-externally-managed («SVEM») — аргумент, предназначенный только для Setuptools, команды setup.py install. Основной эффект SVEM — использование Distutils для установки вместо easy_install из Setuptools.

easy_install выполняет несколько действий, которых нам нужно избегать:

  • загружает и устанавливает зависимости
  • обновляет зависимости на месте в sys.path
  • создаёт файлы .pth и site.py, которые нам не нужны и вызывают конфликты при связывании

Setuptools требует использовать SVEM вместе с --record, который предоставляет список файлов, впоследствии используемый для удаления пакета. Нам это не нужно и не требуется, поскольку Homebrew может управлять удалением, но мы выполняем это требование Setuptools. Согласно соглашению Homebrew, файл записи называется «installed.txt».

Трудно определить, использует ли setup.py команду setup() из Setuptools или Distutils, но этот флаг всегда нужно передавать скриптам на основе Setuptools. pip сталкивается с той же проблемой, что и мы, и принудительно задаёт для setup() версию из Setuptools, загружая обёртку вокруг setup.py, которая импортирует Setuptools до выполнения любых других действий. Поскольку Setuptools исправляет Distutils с помощью monkey patching и заменяет его функцию setup, это обеспечивает единый и последовательный интерфейс. Мы позаимствовали этот код и используем его в Language::Python.setup_install_args.

--prefix и --root

setup.py принимает довольно запутанный набор параметров установки. Для Homebrew подходит параметр --prefix, который автоматически задаёт семейство параметров --install-foo с разумными значениями в стиле POSIX.

--root используется при установке в префикс, который не станет частью конечного расположения установленных файлов, например при сборке RPM или бинарного дистрибутива. При использовании Setuptools на основе setup.py параметр --root побочно активирует --single-version-externally-managed. Небезопасно использовать --root с пустым --prefix, поскольку при компиляции модулей в байт-код из путей удаляется root.

Вероятно, безопасно использовать --prefix вместе с --root=/: это должно работать с setup.py на основе Setuptools или Distutils, хотя выглядит не очень изящно.

pip и setup.py

PEP 453 рекомендует дистрибутивам, распространяющим пакеты (нам), устанавливать архивы sdist с помощью pip, а не вызывать напрямую setup.py. По историческим причинам мы не следовали PEP 453, поэтому некоторые формулы всё ещё используют установку через setup.py. Сейчас в большинстве основных формул используется pip, поскольку мы перевели их на этот предпочтительный способ установки.

© 2009–present Homebrew contributors
Licensed under the BSD 2-Clause License.
https://docs.brew.sh/Python-for-Formula-Authors

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API