Python для авторов формул
Этот документ объясняет, как успешно использовать Python в формуле Homebrew.
Homebrew различает Python-приложения и Python-библиотеки. Разница в том, что пользователи обычно не заботятся о том, что приложения написаны на Python; маловероятно, что пользователь ожидает возможности import foo после установки приложения. Примеры приложений — ansible и jrnl.
Python-библиотеки существуют для импорта другими Python-модулями; они часто являются зависимостями Python-приложений. Они обычно не более чем случайным образом полезны в терминале. Примеры библиотек — py2cairo и связи, которые устанавливаются protobuf.
Связи — это особый случай библиотек, позволяющий коду Python взаимодействовать с библиотекой или приложением, реализованным на другом языке.
Homebrew с удовольствием принимает приложения, построенные на Python, вне зависимости от того, доступны ли они из PyPI. Homebrew, как правило, не будет принимать библиотеки, которые можно корректно установить с помощью pip install foo. Связи могут быть установлены для пакетов, которые их предоставляют, особенно если аналогичная функциональность недоступна через pip.
Приложения должны безусловно включать все свои зависимости и библиотеки языка Python и должны устанавливать любые неудовлетворённые зависимости; эти стратегии подробно обсуждаются в следующих разделах.
Приложения
Объявления Python
Формулы для приложений, которым требуется Python 3, должны объявлять безусловную зависимость от "python@3.x". Эти приложения должны работать с текущей формулой Homebrew Python 3.x.
Приложения, совместимые с Python 2, должны использовать системный Python от Apple в /usr/bin на системах, которые предоставляют Python 2.7. Явное указание на зависимость от Python не требуется, поскольку /usr/bin всегда включено в PATH для формул Homebrew.
Установка
Приложения должны устанавливаться в среду Python virtualenv с корнем в libexec. Это предотвращает загрязнение системного site-packages модулями Python приложения и наоборот.
Все зависимости модулей Python приложения (и их зависимости рекурсивно) должны быть объявлены как resource в формуле и установлены в virtualenv. Каждая зависимость должна быть явно указана; не полагайтесь на 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, чтобы помочь в написании разделов ресурсов. Для его использования настройте virtualenv, установите пакет и все его зависимости. Затем выполните pip install homebrew-pypi-poet в том же virtualenv. Выполнение 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 предоставляет вспомогательные методы для создания и заполнения virtualenv. Вы можете использовать их, поместив include Language::Python::Virtualenv в начало определения класса Formula.
Для большинства приложений вам нужно будет написать только:
def install virtualenv_install_with_resources end
Это полностью соответствует написанию:
def install # Create a virtualenv in `libexec`. If your app needs Python 3, make sure that # `depends_on "python"` is declared, and use `virtualenv_create(libexec, "python3")`. venv = virtualenv_create(libexec) # 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
Пример
Установка формулы с зависимостями будет выглядеть так:
class Foo < Formula
include Language::Python::Virtualenv
url "..."
resource "six" do
url "https://pypi.python.org/packages/source/s/six/six-1.9.0.tar.gz"
sha256 "e24052411fc4fbd1f672635537c3fc2330d9481b18c0317695b46259512c91d5"
end
resource "parsedatetime" do
url "https://pypi.python.org/packages/source/p/parsedatetime/parsedatetime-1.4.tar.gz"
sha256 "09bfcd8f3c239c75e77b3ff05d782ab2c1aed0892f250ce2adf948d4308fe9dc"
end
def install
virtualenv_install_with_resources
end
end Вы также можете использовать более подробную форму и запросить установку конкретных ресурсов:
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 в случае необходимости выполнения различных действий для различных ресурсов.
Связи
Для добавления связей для Python 3 добавьте depends_on "python@3.x", чтобы работать с текущей формулой Homebrew Python 3.x.
Связи Python 2 строятся по умолчанию с системным Python (не добавляйте параметр), и они должны быть работоспособны с любым бинарно совместимым Python. Если это не так, это ошибка upstream; вот несколько советов по её решению.
Зависимости
Связи должны следовать тем же рекомендациям по зависимостям модулей Python, что и библиотеки; см. ниже.
Установка связей
Если связи устанавливаются вызовом setup.py, сделайте что-то вроде:
cd "source/python" do system Formula["python@3.x"].opt_bin/"python3", *Language::Python.setup_install_args(prefix) end
Если скрипт конфигурации принимает параметр --with-python, обычно ему не нужна дополнительная помощь в поиске Python.
Если скрипты configure и make не хотят устанавливать в Cellar, иногда вы можете:
- вызвать
./configure --without-python(или похожий параметр) -
cdв каталог, содержащий Python-связи - вызвать
setup.pyс параметрамиsystemиLanguage::Python.setup_install_args(как описано выше)
Иногда нам нужно изменить Makefile на лету, чтобы использовать наш префикс для Python-связей, используя вспомогательный метод Homebrew inreplace.
Библиотеки
Объявления Python
Библиотеки, построенные для Python 3, должны включать depends_on "python@3.x", что позволит их разлить с Python 3.x Homebrew. Библиотеки Python 2.x должны функционировать при установке как с системным Python, так и с установленным через Homebrew.
Библиотеки Python 2 нуждаются в объявлении uses_from_macos "python@2"; они будут построены с системным Python, но должны оставаться работоспособными с любым другим Python 2.7. Если это не так, это ошибка upstream; вот несколько советов по её решению.
Установка
Библиотеки могут быть установлены в libexec и добавлены в sys.path путём записи файла .pth (называемого «homebrew-foo.pth») в каталог prefix site-packages. Это упрощает последующие проблемы, если pip случайно используется для обновления пакета, установленного через Homebrew, и предотвращает накопление устаревших файлов .pyc в site-packages Homebrew.
Большинство формул в настоящее время просто устанавливаются в prefix.
Зависимости
Зависимости библиотек должны быть установлены так, чтобы они были доступны для импорта. Чтобы минимизировать потенциальные конфликты при связывании, зависимости должны быть установлены в libexec/<vendor> и добавлены в sys.path путём записи второго файла .pth (называемого «homebrew-foo-dependencies.pth») в каталог prefix site-packages.
Ещё глубже в кроличью нору
Дополнительные комментарии, объясняющие, почему Homebrew делает некоторые вещи именно так.
setuptools против distutils против pip
Distutils — это модуль в стандартной библиотеке Python, который предоставляет разработчикам базовый API управления пакетами. Setuptools — это модуль, распространяемый за пределами стандартной библиотеки, который расширяет distutils. Принято, что Python-пакеты предоставляют функцию setup.py, которая вызывает функцию setup() из distutils или setuptools.
Setuptools предоставляет команду easy_install, которая является инструментом управления пакетами для конечного пользователя, получающим и устанавливающим пакеты из PyPI, Python Package Index. pip — это другой, более новый инструмент управления пакетами для конечного пользователя, также предоставляемый за пределами стандартной библиотеки. Хотя pip заменяет easy_install, pip не заменяет другие функции модуля setuptools.
Distutils и pip используют «плоскую» иерархию установки, устанавливая модули как отдельные файлы в site-packages, в то время как easy_install устанавливает сжатые яйца в 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["python@3.x"].opt_bin/"python3", *Language::Python.setup_install_args(prefix)
где prefix — это префикс назначения (обычно libexec или prefix).
Что такое --single-version-externally-managed?
--single-version-externally-managed («SVEM») — это специфический для setuptools аргумент для setup.py install. Основной эффект SVEM — использование distutils для выполнения установки вместо использования setuptools’ easy_install.
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 и заменяет его функцию setup, это обеспечивает единый и согласованный интерфейс. Мы позаимствовали этот код и используем его в Language::Python.setup_install_args.
--prefix vs --root
setup.py принимает слегка озадачивающий набор параметров установки. Правильный переключатель для Homebrew — --prefix, который автоматически устанавливает семейство параметров --install-foo с использованием разумных значений POSIX.
--root используется при установке в префикс, который не станет частью конечного места установки файлов, например, при построении .rpm или двоичного дистрибутива. При использовании setup.py на основе setuptools, --root имеет побочный эффект активации --single-version-externally-managed. Не безопасно использовать --root с пустым --prefix, так как root удаляется из путей при компиляции модулей в байт-код.
Вероятно, безопасно использовать --prefix с --root=/, что должно работать как с setuptools, так и с setup.py на основе distutils, но это немного некрасиво.
pip против setup.py
PEP 453 рекомендует дистрибуторам (нам) устанавливать sdist-архивы с помощью pip вместо вызова setup.py напрямую. Мы этого не делаем, потому что дистрибутив Python от Apple не включает pip, поэтому мы не можем предполагать, что pip доступен. Мы могли бы сделать что-то хитрое, чтобы обойти отсутствие pip у Apple, но ценность такого подхода пока не ясна.
© 2009–present Homebrew contributors
Licensed under the BSD 2-Clause License.
https://docs.brew.sh/Python-for-Formula-Authors