FAQ
Почему в точке входа нужны танцы с sys.path?
В archtool v2 не нужны. Передай project_root явно:
from pathlib import Path
from archtool.dependency_injector import DependencyInjector
ROOT = Path(__file__).parent.parent # где лежит pyproject.toml
injector = DependencyInjector(
modules_list=[...],
project_root=ROOT,
)
archtool использует этот путь для всего разрешения модулей и не трогает sys.path[0].
Аннотации через from __future__ import annotations работают?
Да. archtool v2 использует typing.get_type_hints() для разрешения аннотаций — строковые аннотации (PEP 563) обрабатываются прозрачно.
Где объявлять зависимость — на абстрактном классе или на конкретном?
На конкретном. archtool читает аннотации через vars(ConcreteClass).__annotations__ — только те что объявлены непосредственно на данном классе. Аннотации с базового класса не подхватываются.
# ✅ правильно
class UserService(UserServiceABC):
repo: UserRepoABC # archtool найдёт это
# ❌ неправильно — archtool не подхватит аннотацию из ABC
class UserServiceABC(ABCService):
repo: UserRepoABC
Нужен ли __init__ с параметрами?
Нет. archtool вызывает ConcreteClass() без аргументов. Если у конкретного класса есть обязательные параметры конструктора, предварительно зарегистрируй готовый экземпляр:
repo = UserRepo(db_url=os.environ["DATABASE_URL"])
injector = DependencyInjector(modules_list=APPS, project_root=ROOT)
injector.register(key=UserRepoABC, value=repo)
injector.inject() # пропускает авто-обнаружение UserRepoABC, использует твой экземпляр
Без register() archtool бросит InstantiationError с сообщением, которое укажет именно на этот фикс.
Как зарегистрировать то, что archtool не может авто-обнаружить?
Используй injector.register() до вызова inject():
# async-ресурс, сторонний объект или что угодно другое
pool = await asyncpg.create_pool(DATABASE_URL)
injector = DependencyInjector(modules_list=APPS, project_root=ROOT)
injector.register(key=DBPoolABC, value=pool)
injector.inject() # авто-обнаруживает остальное; пул уже зарегистрирован
archtool уважает предзарегистрированные зависимости и пропускает авто-обнаружение для них. После inject() пул внедряется в любой компонент, объявивший pool: DBPoolABC.
Словарь injector.dependencies тоже доступен напрямую — это обычный dict с ключами в виде полного dotted-пути до класса интерфейса.
Как условно подменить реализацию (например, в тестах)?
Предзарегистрируй альтернативу до inject():
# в тестах: меняем реальный репо на заглушку
injector = DependencyInjector(modules_list=APPS, project_root=ROOT)
injector.register(key=UserRepoABC, value=StubUserRepo())
injector.inject() # находит заглушку уже зарегистрированной, пропускает UserRepo
Можно иметь несколько реализаций одного интерфейса?
Нет — archtool требует одну реализацию на интерфейс на модуль. Если два конкретных класса наследуют один ABC в одном модуле, при сборке бросается MultipleRealizationsException.
Что будет, если есть циклическая зависимость?
Ничего не сломается. В двухпроходной схеме archtool все объекты создаются в проходе 1 до начала разводки в проходе 2, поэтому ServiceA.dep = service_b и ServiceB.dep = service_a — это валидные setattr-вызовы на уже существующих объектах.
archtool выводит WARNING один раз за вызов inject(), чтобы сигнализировать о цикле:
[archtool] WARNING Circular dependency detected: ServiceA → ServiceB → ServiceA.
Wiring will succeed because all objects are already instantiated, but mutual
method recursion may cause infinite loops at runtime.
Это сигнал о дизайне: если ServiceA.method() вызывает self.dep.method(), который в свою очередь вызывает обратно в ServiceA — будет бесконечный цикл в рантайме. Warning нужен чтобы ты заметил цикл — не чтобы заблокировать разводку.
Включи предупреждение через verbose=True или ARCHTOOL_VERBOSE=1.
Как посмотреть что было внедрено?
После inject() проверь injector.dependencies:
injector.inject()
for key, obj in injector.dependencies.items():
print(key, "→", type(obj).__name__)
Или через CLI:
Как включить логи сборки?
Или:
archtool никогда не вызывает logging.basicConfig() — добавляет StreamHandler только когда verbose-режим явно запрошен.
Поддерживается ли async?
archtool — инструмент разводки DI, не async-фреймворк. Он связывает объекты синхронно при старте. Являются ли эти объекты async или нет — твой выбор, archtool не знает и не заботится об этом.