Configuration: TOML loader¶
Container.from_toml(path) loads a sealed Container from a
TOML configuration file. Tokens, factories, and modules are
named through dotted Python paths that the loader resolves via
importlib.import_module plus getattr, so the wiring lives
entirely in declarative text - the application code is just the
objects pointed at.
modules = ["my_app.modules.CacheModule"]
[[bindings]]
token = "my_app.services.Clock"
factory = "my_app.factories.make_clock"
lifecycle = "singleton"
[[bindings]]
token = "my_app.services.Session"
factory = "my_app.factories.make_session_async"
lifecycle = "scoped"
async_factory = true
[[bindings]]
token = "my_app.services.App"
factory = "my_app.factories.make_app"
lifecycle = "transient"
auto_inject = true
Validation¶
Every loader validates the parsed dict against the
ContainerConfig TypedDict shape before any binding hits the
builder, so a structurally invalid file raises
ConfigurationError without producing a half-wired container.
The validator checks:
- The top-level value is a table / object.
- A
bindingskey exists and is a list. - Each binding has
token,factory,lifecyclekeys. tokenandfactoryare strings.lifecycleis one of"transient","singleton","scoped".- The optional
async_factoryandauto_injectflags are bools. - The optional
moduleskey is a list of strings.
Imports¶
Tokens and factories are resolved at load time. Both the
"unknown module" and "missing attribute" cases surface as a
single ConfigurationError with a clear message, so consumers
can catch one exception type for every kind of declarative
failure.
A name without a dot is rejected on principle: the loader needs
the module path to call importlib.import_module. A bare
identifier ("Clock") raises ConfigurationError rather than
silently failing.
Modules¶
The optional modules = [...] array names Module-shaped
classes (or already-instantiated module instances). For each
qualified name, the loader imports it; if it resolves to a
class, it instantiates with no args; then it calls
builder.install on the result. A name that does not resolve
to something with a callable register method is rejected at
load time.
Why TOML?¶
TOML lives in the standard library since Python 3.11 via
tomllib, so the TOML loader adds zero runtime
dependency. It is the recommended format for application
configuration in modern Python and the most natural fit for
pyproject.toml-adjacent ecosystems.
The JSON loader (4.10) is parser-equivalent and also has zero
new dependency. The YAML loader (4.11) requires the optional
tripack-container[yaml] extra.