Injection¶
This guide covers the different ways to inject dependencies using dijay.
Automatic Constructor Injection¶
The container inspects __init__ type hints and resolves dependencies automatically:
from dijay import injectable
@injectable()
class UserService:
def __init__(self, repo: UserRepository, email: EmailService):
self.repo = repo
self.email = email
When UserService is resolved, the container will:
- Look up
UserRepositoryandEmailServicein the registry. - Resolve them (recursively resolving their dependencies).
- Pass them to
UserService.__init__.
Token Injection with Inject¶
When the type hint alone isn't enough (e.g., injecting a str value), use Annotated with Inject:
from typing import Annotated
from dijay import Inject, injectable
@injectable()
class DatabaseConnection:
def __init__(
self,
url: Annotated[str, Inject("DB_URL")],
pool_size: Annotated[int, Inject("DB_POOL_SIZE")],
):
self.url = url
self.pool_size = pool_size
The Inject("DB_URL") marker tells the container to resolve the "DB_URL" token instead of trying to resolve str.
Optional Dependencies¶
Dependencies with Optional (or X | None) type hints are treated as optional. If the token is not registered, None is injected instead of raising an error:
@injectable()
class NotificationService:
def __init__(self, sms: SmsGateway | None = None):
self.sms = sms
Request-Scoped Injection¶
When using REQUEST scope, pass an id parameter to resolve to group dependencies by request:
All REQUEST-scoped dependencies resolved with the same id will share instances within that request.
Circular Dependency Detection¶
dijay monitors the resolution stack. If a cycle is detected (e.g., A → B → A), a RuntimeError is raised immediately:
@injectable()
class A:
def __init__(self, b: B): ...
@injectable()
class B:
def __init__(self, a: A): ...
# RuntimeError: Circular dependency: <class 'A'>
Tip
To break circular dependencies, consider using factory functions or restructuring your modules.