11 декабря 2016 г. 12:21 (ред. 11 декабря 2016 г. 12:37)
О новых возможностях, предлагаемых setuptools, для декларативного конфигурирования пакетов ваших приложений на Питоне.
Рад сообщить, что, начиная с версии 30.4.0 setuptools поддерживает конфигурирование пакетов приложений в файлах конфигурации, например, setup.cfg.


Краткое введение в тему, для тех, кто пока не понимает о чём идёт речь

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

В сети доступен центральный репозиторий с пакетами Python Package Index (PyPI), с которого их можно загрузить (вручную, или при помощи автоматизации, например, приложения pip) и произвести установку.

Пакет приложения обычно компонуется из набора файлов. Основным файлом является setup.py, в котором используется функция setup(), производящая базовое конфигурирование. Этот файл можно рассматривать в качестве единой точки входа при обращении с пакетом. Пример простого setup.py.

Изначально функция setup() предоставлялась встроенным в стандартную библиотеку пакетом distutils, однако, некоторое время спустя появился setuptools тоже реализующий упомянутую функцию, предлагающий большее количество возможностей и развивающийся более динамично, чем первый.

Некоторые параметры запуска python setup.py можно было сохранить в файл конфигурации (например setup.cfg), чтобы в последующем не задавать их каждый раз в командной строке. При этом параметры, передаваемые в функцию setup() хранить там было нельзя.

Наверное, первым, кто позволил хранение метаданных в файле конфигурации, был ныне почивший в бозе distutils2, идею которого потом подхватывали. Наиболее известными продолжателями его дела явились d2to1 и pbr.


Описание нововведений

На прошлой неделе я реализовал поддержку декларативного конфигурирования в setuptools. Далее несколько слов о том, что это нам даёт.

Описательное конфигурирование может быть полезно не только для различного рода автоматизаций, оно также позволяет избавиться от необходимости набора зачастую шаблонного кода.

Поддерживаемые правила конфигурирования я постарался довольно полно описать в официальной документации, ниже приведу лишь некоторые основные пункты.

Пример файла setup.cfg:

    [metadata]
name = my_package
version = attr: src.VERSION
description = My package description
long_description = file: README.rst
keywords = one, two
license = BSD 3-Clause License

[metadata.classifiers]
Framework :: Django
Programming Language :: Python :: 3.5

[options]
zip_safe = False
include_package_data = True
packages = find:
scripts =
bin/first.py
bin/second.py

[options.extras_require]
pdf =
ReportLab>=1.2
RXP
rest =
docutils>=0.3
pack ==1.1, ==1.3

[options.packages.find]
exclude =
src.subpackage1
src.subpackage2

Можно заметить, что метаданные пакета должны быть указаны в секции metadata, а опции в секции options. Ключи в секциях совпадают с именами аргументов, которые обычно передаются в setup(). Для некоторых опций есть возможность конфигурирования в подсекциях: metadata.classifiers, options.extras_require и др.. Это сделано для облегчения восприятия файла.

Обратите внимание, что некоторые опции поддерживают так называемые директивы. Директивы указываются вместо значений и имеют частью своёго имени двоеточие: attr:, file:, find:.

Функциональность директивы может варьироваться в зависимости от опции, для которой она используется. Так директива attr: для опции version вернёт значение из указанного атрибута, причём в качестве атрибута можен выступать и строка, и объект поддерживающий итерирование, и объект поддерживающий вызов.

На заметку
Cложные директивы типа find: могут поддерживать настройку в отдельных подсекциях — см. options.packages.find.

Ещё одной отличительной чертой реализации является наличие программного интерфейса для чтения данных из файлов конфигурации.

    from setuptools.config import read_configuration

conf_dict = read_configuration('/home/user/dev/package/setup.cfg', ignore_option_errors=True)

При помощи вышеозначенного кода мы можем считать setup.cfg из указанного места и получить на выходе словарь. Аргумент ignore_option_errors позволяет нам игнорировать ошибки, которые могут возникать, например, в случае использования упомянутых ранее директив. Тем самым мы можем получить базовые метаданные имя только конфигурационный файл. Подобное может пригодиться для сбора данных, например, с GitHub (по тому же принципу, по которому работает Bower).


Заключение

В конце не могу не обратить ваше внимание на существование PEP-518, в котором принято решение о постепенном переводе системы сборки пакетов Питона на использование файла pyproject.toml. То есть, существует вероятность, что в будущем метаданные и опции будут храниться именно в toml формате. Впрочем, когда это будет неизвестно, а конфигурировать в setup.cfg можно уже сейчас.

Приятного вам конфигурирования.