Представляем pristan: плагины на основе типизированных функций.
Превращает типизированную Python-функцию в готовую точку расширения с поиском и регистрацией плагинов, поведением по умолчанию и сбором результатов.
На этой странице 4 раздела
Плагин позволяет дополнительному пакету расширить библиотеку, не меняя её основной код. Но библиотеке всё равно нужно описать требования к плагину, найти установленные реализации, решить, что делать при их отсутствии, и объединить результаты. Для одной простой точки расширения эта инфраструктура легко становится сложнее самой функции.
Точки входа Python — метаданные пакета, через которые обычно объявляют установленные плагины, — помогают их найти, но не задают сигнатуру функций и способ объединения результатов. Pluggy предоставляет зрелую систему плагинов, однако для одного обычного вызова многие её возможности могут быть лишними.
pristan — компактная плагинная Python-библиотека на основе обычной функции с аннотациями типов. Одна и та же функция задаёт требования к плагинам, хранит их регистрации, предоставляет поведение по умолчанию и запускает плагины. Отдельно установленные расширения находятся через стандартные точки входа Python только при первом обращении. Релиз доступен на GitHub.
Как работают слоты на основе функций
Добавьте к основной функции @slot, и она станет контрактом, реестром, реализацией по умолчанию и точкой вызова. Плагин — другая обычная функция, зарегистрированная через этот объект:
from pristan import slot
@slot
def serializers(value) -> dict[str, bytes]:
...
@serializers.plugin
def text_serializer(value) -> bytes:
return str(value).encode()
print(serializers(42))
#> {'text_serializer': b'42'}
Вызов основной функции последовательно запускает все плагины; если их нет, выполняется её исходное тело. Каждый плагин остаётся доступен для отдельного тестирования.
Аннотация результата основной функции также определяет, что делать с ответами плагинов. Без аннотации они отбрасываются, list[T] собирает значения в список, а dict[str, T] — в словарь под именами плагинов. pristan проверяет сигнатуры функций при регистрации, а возвращаемые значения — во время выполнения.
Установленные расширения находятся через точки входа Python. Их модули загружаются только при первом обращении к соответствующей основной функции.
Альтернативы и область применения
По сравнению с Pluggy, pristan требует меньше настройки для более узкого случая: обычной функции, вызывающей каждый зарегистрированный плагин. Если важнее управление жизненным циклом, порядок вызовов, дополнительные обёртки или развитие протокола, Pluggy остаётся лучшим выбором.
Stevedore предлагает несколько видов менеджеров, а Pluginlib использует классовые контракты. Закрытому приложению может быть достаточно словаря и регистрационного декоратора.
Ограничения
Проверки во время выполнения ловят многие ошибки контракта, но не доказывают, что каждый вызов основной функции подойдёт каждому плагину. Плагины выполняются последовательно, одна ошибка останавливает их вызов, а API не управляет приоритетами, дополнительными обёртками, особым объединением результатов, асинхронным запуском или удалением плагинов.
Поиск и регистрация защищены от одновременных изменений из разных потоков, но сам вызов плагинов защищён не полностью. Поэтому pristan не гарантирует полную потокобезопасность.
pristan нужен авторам библиотек, которым требуются независимо распространяемые расширения без отдельного управляющего класса для каждой точки подключения. Он позволяет держать необязательные возможности вне ядра, не навязывая архитектуру всей программы.
Что дальше
Проект поддержит больше плагинных контрактов и сделает жизненный цикл и конкурентное поведение предсказуемее, не усложняя компактный API на основе функций.