Scopes — Aquilia Documentation
Comprehensive guide and documentation for Scopes in the Aquilia framework. View API reference, examples, and implementation patterns.
Dependency Injection / Scopes Service Scopes & Lifetimes Scopes define instance lifetimes and validation constraints. Aquilia enforces strict boundary checking to prevent memory leaks and concurrency race conditions. Scope String Literals Scopes are plain string literals. Pass them anywhere a scope is expected — @service(scope="request"), provider constructors, or manifest declarations. The canonical type hint is ServiceScopeLiteral, defined in aquilia/di/scopes.py: from typing import Literal from aquilia.di import ServiceScopeLiteral ServiceScopeLiteral = Literal[ "singleton", # Process-wide lifetime "app", # Application container lifetime (alias of singleton) "request", # Isolated request lifetime "transient", # Uncached, new instance per resolution "pooled", # Managed by asyncio.Queue instance pool "ephemeral", # Request-scoped temporary lifetime ] Deprecated: the ServiceScope Enum. Accessing any member (ServiceScope.SINGLETON) or calling the Enum emits a DeprecationWarning and will be removed in a future version. Replace ServiceScope.SINGLETON with the string "singleton", ServiceScope.REQUEST with "request", and so on. String literals skip import-time namespace scanning and runtime attribute lookups. Scope Lifetime Cached Use Case ))} Choosing the Right Scope Scope is a lifetime decision. Match the instance lifetime to the data it holds: — ))} Caching & ownership. Only singleton, app, and request are cacheable. Singleton/app instances are cached at the owning (root) container and delegated upward — one instance for the process. Request instances are cached in the request child container and cleared at request shutdown. Transient and pooled are never cached in the container. Under parallel_resolution, in-flight dedup guarantees concurrent resolvers of the same uncached cacheable token still share one instance. Injection Validation To enforce structural safety, Aquilia checks scope compatibility at startup: Longer-lived scopes can always inject into shorter-lived scopes. Shorter-lived scopes CANNOT inject into longer-lived scopes (prevents memory leak state capture). Injection Compatibility Matrix Provider ↓ / Consumer → singleton app request transient ephemeral ))} ))} Scope Violation Example @service(scope="request") class RequestLogger: def __init__(self, req: Request): self.req = req @service(scope="singleton") class GlobalAnalytics: # ❌ ScopeViolationError raised at startup: # Singleton cannot depend on short-lived request scope! def __init__(self, logger: RequestLogger): self.logger = logger # Option A: Make the consumer request-scoped: @service(scope="request") class GlobalAnalytics: def __init__(self, logger: RequestLogger): self.logger = logger # Option B: Access lazily via the context container @service(scope="singleton") class GlobalAnalytics: def __init__(self): pass async def track(self, ctx_container, event: str): logger = await ctx_container.resolve_async(RequestLogger) logger.info(event) Enforcement is settings-driven. The scope_enforcement DI setting controls the outcome: "warn" (default) logs a warning, "raise" raises ScopeViolationError at startup, and "off" skips the check entirely. Configure it in your workspace.py di block — see Advanced DI. Providers Decorators )
Go to Homepage