Container¶
The Container is the low-level DI container. It stores registrations and resolves dependencies by inspecting __init__ type hints.
Most applications use ApplicationContext (which manages containers internally), but Container is useful for tests and simple setups.
from spryx_di import Container
container = Container()
container.singleton(UserReader, PgUserReader)
container.instance(Database, my_db)
handler = container.resolve(ListHandler) # auto-wired
handler = container[ListHandler] # same thing
Registration Methods¶
| Method | Behavior |
|---|---|
register(iface, impl) |
New instance every resolve() (transient) |
singleton(iface, impl) |
One instance per container lifetime |
instance(type, obj) |
Pre-built object, always the same |
factory(type, fn) |
Factory receives the container: fn(container) |
Resolution¶
resolve(Type) follows this order:
- Instance registered? Return it.
- Factory registered? Call it.
- Singleton cache hit? Return it.
- Find implementation (singleton/transient mapping or auto-wire the type itself).
- Inspect
__init__type hints, resolve each dependency recursively. - Cache if singleton.
Scoped Containers¶
scope = container.create_scope()
scope.instance(Transaction, current_tx)
# Resolves Transaction from scope, Database from parent
handler = scope.resolve(Handler)
ScopedContainer inherits all parent registrations. Scope-local registrations take priority.