Перейти к содержанию

Depends

QTasks поддерживает управляемые зависимости для задач через механизм Depends. Он позволяет объявлять параметры задачи так, чтобы их значения автоматически создавались и корректно очищались в нужном контексте.


Пример использования Depends

from typing import Annotated
from contextlib import asynccontextmanager

from qtasks.plugins.depends import Depends, ScopeEnum


@asynccontextmanager
async def test_dep():
    print("Open")
    yield 123
    print("Close")


@app.task
async def test(dep: Annotated[int, Depends(test_dep, scope=ScopeEnum.TASK)]):
    print(dep)

При выполнении задачи вывод будет таким:

Open
123
Close

Что происходит:

  • при старте задачи открывается контекст test_dep();
  • в тело задачи попадает значение 123;
  • при завершении задачи контекст закрывается, и печатается Close.

Как работает Depends "под капотом"

Механизм Depends реализован во встроенном плагине воркера — (A)syncDependsPlugin. Ниже — упрощённое описание шагов.

1. Поиск Depends в параметрах задачи

При подготовке к выполнению задачи плагин:

  • проверяет наличие Depends как явного значения параметра (dep = Depends(...));
  • либо ищет его внутри Annotated[..., Depends(...)] (берёт последний элемент Annotated).

Если Depends найден, задача помечается как имеющая зависимость.

2. Создание контекста через (Async)ExitStack

Для управления жизненным циклом зависимости используется AsyncExitStack (или ExitStack для синхронных задач):

  1. Плагин входит в контекст test_dep() через стек контекстных менеджеров.
  2. Полученное из yield значение (в примере — 123) сохраняется как значение параметра.

3. Обработка scope (области жизни зависимости)

Зависимость живёт в определённой "области" (scope), задаваемой ScopeEnum:

class ScopeEnum(Enum):
    TASK = "task"
    WORKER = "worker"
    BROKER = "broker"
    STORAGE = "storage"
    GLOBAL_CONFIG = "global_config"

Логика:

  • TASK — контекст открывается при старте задачи и закрывается при её завершении.
  • остальные варианты (WORKER, BROKER, STORAGE, GLOBAL_CONFIG) привязывают контекст к ресурсу компонента.

Технически это реализовано через триггеры:

  • TASK — закрывается на триггере task_executor_task_close;
  • остальные — на триггерах завершения компонента: "{component}_stop".

4. Подмена аргумента задачи

После получения значения из функции зависимости плагин:

  • заменяет параметр задачи (dep) на результат test_dep();
  • задача получает уже готовое значение (например, открытое подключение, объект сессии, число, конфиг и т.д.).

Зачем нужен Depends

Depends решает задачу управляемых зависимостей:

  • создание и закрытие ресурсов в нужном контексте (на уровне задачи или компонента);
  • отсутствие необходимости вручную работать с контекстными менеджерами внутри тела задач;
  • единая точка описания зависимостей для разных задач.

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