This release is a pre-release and may not be stable for production use.
Aeterna DI
aeterna-di is an explicit, annotation-driven asynchronous dependency-injection
container. Applications compose a ServiceCollection, build a provider, and resolve
services through that provider or an explicit scope.
Features
- Register existing instances, implementation types, and synchronous or asynchronous factories.
- Resolve constructor dependencies from type annotations.
- Choose singleton, scoped, or transient lifetimes.
- Validate duplicate registrations, missing services, dependency cycles, constructor annotations, and singleton graphs that capture scoped services.
- Opt into ownership and close supported resources in reverse acquisition order.
- Detect cross-task singleton cycles and report cleanup cancellation as a framework error.
Requirements
- Python 3.12 or later.
- No runtime dependencies outside the Python standard library.
Installation
With pip:
pip install aeterna-di
With uv:
uv add aeterna-di
Basic usage
Constructor dependencies must have resolvable type annotations. Resolution and provider cleanup are asynchronous.
import asyncio
from aeterna.di import ServiceCollection, ServiceLifetime
class Clock:
def now(self) -> str:
return "12:00"
class GreetingService:
def __init__(self, clock: Clock) -> None:
self._clock = clock
def greet(self) -> str:
return f"Hello at {self._clock.now()}"
async def main() -> None:
services = ServiceCollection()
services.add_type(Clock, lifetime=ServiceLifetime.SINGLETON)
services.add_type(GreetingService)
async with services.build_provider() as provider:
greeting = await provider.get(GreetingService)
print(greeting.greet()) # Hello at 12:00
asyncio.run(main())
Registration, scopes, and ownership
add_type() creates instances from annotated constructor dependencies. add_factory()
and add_async_factory() receive a ServiceResolver; their optional dependencies=(...)
sequence enables build-time validation of services the factory will resolve. Without that
declaration, factory resolution failures are deferred until first use.
add_instance() registers an existing singleton. add_type() and factory registrations
accept lifetime and owns_instance; ownership is opt-in. Owned singleton resources belong
to the provider, owned scoped resources belong to their scope, and transient services may
not be owned. Supported cleanup includes sync/async context management, close(), and
aclose().
Scoped services require an explicit scope:
async with provider.create_scope() as scope:
service = await scope.get(RequestService)
ServiceCollection is immutable after build_provider(). Provider and scope closing is
idempotent and prevents new resolutions.
Public API
Classes, protocol, and enum:
ServiceCollection: mutable registration builder.ServiceDescriptor: immutable description of one registration.ServiceLifetime:SINGLETON,SCOPED, andTRANSIENTlifetime policies.ServiceProvider: root resolver and singleton ownership boundary.ServiceScope: scoped resolver and scoped ownership boundary.ServiceResolver: protocol providing asynchronousget()andtry_get()methods.
Exceptions:
ResolutionError: base class for service-resolution failures.RegistrationError: an invalid registration or mutation after build.MissingRegistrationError: no registration exists for a required service.AmbiguousRegistrationError: more than one registration uses the same key.CircularDependencyError: the dependency graph contains a cycle.CaptiveDependencyError: a singleton graph depends on a scoped service.ActivationError: a registered service could not be created.ScopeClosedError: a provider or scope was used after it closed.ServiceCancellationError: cleanup could not complete normally after cancellation.
Tests
From the repository root:
uv sync --locked
uv run pytest tests/test_di.py
Release files for aeterna-di 1.0.0.dev1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aeterna_di-1.0.0.dev1.tar.gz | 11.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aeterna_di-1.0.0.dev1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.8 kB
Release files / aeterna_di-1.0.0.dev1.tar.gz
| Download URL | aeterna_di-1.0.0.dev1.tar.gz |
|---|---|
| Size | 11.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
13358882a4d5f25992dbee159ce2f229cac27f87efcab78f128aeb0f7eeea178
|
|
BLAKE2b-256 checksum How to use checksums |
baae9108a79acaaf71967d2616d17236d9787ed9c3113e745d85a10fc5353acc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / aeterna_di-1.0.0.dev1-py3-none-any.whl
| Download URL | aeterna_di-1.0.0.dev1-py3-none-any.whl |
|---|---|
| Size | 11.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e12c7c2d9275dbd9373923a8d0faef338c534e98b634d862202c60aafbda28b9
|
|
BLAKE2b-256 checksum How to use checksums |
51769424d61ba09afb2abe71998f3f398e087baf509c7f5ba978e2cf72a2cfa8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log