Перейти к основному содержимому

Зависимости между плагинами

Плагин может объявить, что его инициализация зависит от других плагинов — например, order_processor требует, чтобы payment_provider был уже готов. Реестр учитывает декларации в топологической сортировке и инициализирует плагины в правильном порядке.

Декларация в манифесте

Секция depends_on в манифесте — список плагинов, от которых зависит текущий. Запись — это либо строка (только name, kind подразумевается), либо таблица с явным kind и name.

plugins/order_processor/default/dagstack.toml
[plugin]
name = "default"
kind = "order_processor"
runtime = "in_process"
core_version = ">=0.1.0,<1.0.0"

# Простая форма — имя плагина-зависимости.
depends_on = ["stripe"]

# Или полная форма — если имя неуникально в разных видах.
[[plugin.depends_on]]
kind = "payment_provider"
name = "stripe"

Порядок инициализации

При вызове registry.setup_all():

  1. Реестр строит граф зависимостей из depends_on всех зарегистрированных плагинов.
  2. Топологическая сортировка topo_sort определяет порядок: если B зависит от A, то setup(A) вызывается раньше setup(B).
  3. Плагины одного уровня (без взаимных зависимостей) инициализируются параллельно, с per-plugin таймаутом startup_timeout_sec (по умолчанию 30 секунд).

Результат: к моменту вызова setup(order_processor) плагин payment_provider уже инициализирован и доступен через context.registry.

Использование зависимости в setup

В setup() получите зависимый плагин через context.registry:

plugins/order_processor/default/plugin.py
from dagstack.plugin_system import PluginContext


class DefaultOrderProcessor:
async def setup(self, context: PluginContext) -> None:
# payment_provider is guaranteed to be initialised by now.
self._payment = context.registry.get_plugin(
"payment_provider",
name="stripe",
)

async def process(self, order):
total = sum(item.price * item.quantity for item in order.items)
result = await self._payment.charge(
amount_cents=int(total * 100),
currency=order.currency,
source=order.payment_token,
)
return {"order_id": order.id, "transaction_id": result.transaction_id}

Циклы зависимостей

Если A → B → A (прямой или транзитивный цикл) — setup_all() бросает DependencyCycle с указанием всей цепочки. Ядро не стартует.

Типичный сценарий цикла: два плагина хотят обращаться друг к другу («A вызывает B, B вызывает A»). Разорвите через:

  • Общий младший плагин: вынести общую логику в третий плагин X, оба зависят от X.
  • Deferred lookup: не делать registry.get() в setup, а откладывать до первого вызова хука (lazy) — тогда граф зависимостей пуст.

Partial failure при падении зависимости

Если setup(payment_provider) падает (исключение или таймаут), setup_all(ctx) пробрасывает ошибку, и весь старт прерывается. Реестр в 0.1.0-rc.2 сохраняет fail-fast-семантику — плагины не запускаются частично.

:::info Phase 2 scope Режим «continue-on-failure» (unavailable_plugins(), рекурсивная маркировка зависимых, старт в деградировавшем режиме) — в дорожной карте Phase 2. До этого момента приложение анализирует исключение из setup_all(ctx) и решает, продолжать ли запуск. :::

Приоритет vs зависимости

priority и depends_onдве разных оси:

  • depends_on — жёсткая зависимость: B не может инициализироваться до A.
  • priority — порядок внутри одной топологической группы (плагины без взаимных зависимостей) + tiebreak для singleton/capability-dispatch.

В большинстве случаев используйте depends_on, когда есть реальная зависимость в setup. priority — только для управления порядком вызовов в broadcast/chain-dispatch.

Как проверить граф зависимостей локально

from dagstack.plugin_system import PluginRegistry

registry = PluginRegistry()
registry.discover("plugins/")
for manifest in registry.list_manifests():
deps = ", ".join(manifest.depends_on) or "(none)"
print(f"{manifest.name:30s} depends on: {deps}")
stripe depends on: (none)
tax_calculator depends on: (none)
order_processor depends on: stripe
invoice_generator depends on: order_processor, tax_calculator

Отладка

СимптомВозможная причина
DependencyCycle: A → B → A на стартеПрямой или транзитивный цикл в depends_on.
Plugin X unavailable: dependency Y failedY упал в setup() или превысил startup_timeout_sec. Смотреть логи Y.
KindUnknown: payment_provider в setup() order_processorВ depends_on указан payment_provider, но такого плагина в реестре нет.
AmbiguousPlugin: embedderВ реестре два плагина вида embedder и неоднозначность выбора. Указать name= в registry.get() явно.

См. также