https://github.com/alexeybond/ab_plugin_manager_py
Менеджер плагинов для приложений на Python
https://github.com/alexeybond/ab_plugin_manager_py
plugin-architecture plugin-loader plugin-system python python3
Last synced: 8 days ago
JSON representation
Менеджер плагинов для приложений на Python
- Host: GitHub
- URL: https://github.com/alexeybond/ab_plugin_manager_py
- Owner: AlexeyBond
- License: mit
- Created: 2025-03-10T16:09:36.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2026-05-14T14:29:11.000Z (2 months ago)
- Last Synced: 2026-05-14T16:39:21.001Z (2 months ago)
- Topics: plugin-architecture, plugin-loader, plugin-system, python, python3
- Language: Python
- Homepage:
- Size: 276 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ab_plugin_manager - менеджер плагинов для приложений на Python
## Пример
Точка входа приложения, использующего этот загрузчик плагинов обычно выглядит следующим образом:
```python
# main.py
import os
from ab_plugin_manager.core_plugins import PluginDiscoveryPlugin
from ab_plugin_manager.file_patterns import register_variable
from ab_plugin_manager.launcher import launch_application
# Регистрируем переменную корня пакета.
# Она будет в дальнейшем использоваться для построения путей, в т.ч. тех, по которым будут искаться плагины.
register_variable("package_root", os.path.dirname(__file__))
# Плагин, осуществляющий динамическую загрузку плагинов.
# В данном случае, просто PluginDiscoveryPlugin с изменёнными настройками.
class MyPluginDiscoveryPlugin(PluginDiscoveryPlugin):
name = "discover_plugins"
config = {
**PluginDiscoveryPlugin.config,
"pluginPaths": [
# Ищем плагины внутри пакета приложения
"{package_root}/**/plugin_*.py",
# и среди других питоновских пакетов
"{python_path}/myapp_plugin_*/**/plugin_*.py",
],
}
if __name__ == '__main__':
# launch_application получает на вход набор предварительно загруженных плагинов ядра (как минимум - плагин для загрузки других плагинов)
# и блокирует поток выполнения до завершения работы приложения.
launch_application([MyPluginDiscoveryPlugin()])
```
Вся логика приложения располагается в плагинах.
В случае если нужна более жёсткая структура приложения (например, ядро приложения не меняется, а плагины могут
использоваться только для некоторых точек расширения), следует использовать [
`PluginManager`](./ab_plugin_manager/abc.py) напрямую.
Хотя даже в таком случае стоит рассмотреть возможность вынести ядро приложения в отдельный плагин, передав его в списке
плагинов ядра при запуске приложения.
Рассмотрим пример "hello world", в котором логика вывода сообщения отделена от вызывающего её кода.
Для начала нужно определить операцию вывода сообщения:
```python
# operations.py
from ab_plugin_manager.magic_operation import CallAllOperation
# Операция типа CallAll вызывает все шаги синхронно и последовательно.
# Единственный параметр конструктора - имя операции - используется для идентификации шагов, принадлежащих этой операции
# и должен быть уникален для неё, чтобы избежать коллизий с другими операциями.
greet_op = CallAllOperation[str]("greet")
```
Затем можно создать плагин, вызывающий эту операцию:
```python
# plugin_greetings_runner.py
from operations import greet_op
# Имя и версия плагина.
# Без этих полей загрузчик плагинов не сможет распознать плагин и не загрузит его.
name = 'greetings_runner'
version = '0.1.0'
# run - одна из встроенных операций, поддерживаемых функцией launch_application
# она выполняется после инициализации приложения.
# Когда все реализации этой операции завершают работу (или хотя бы одна из них завершается с ошибкой или приложение
# получает сигнал прерывания), выполняется операция terminate и работа приложения завершается.
def run(*_args, **_kwargs):
# CallAllOperation может быть вызвана как обычная функция.
# Внутри вызова операция запросит у менеджера плагинов все свои шаги и выполнит их последовательно.
greet_op("world")
```
И плагин, реализующий её:
```python
# plugin_greeter.py
from operations import greet_op
name = 'greeter'
version = '0.1.0'
# Декоратор .implementation не обязателен если имя функции совпадает с именем операции (в т.ч. в этом примере)
@greet_op.implementation
def greet(who: str):
print(f"Hello {who}!")
```
## История
Изначально этот пакет разрабатывался как
часть [проекта голосового ассистента](https://github.com/AlexeyBond/Irene-Voice-Assistant).