windlass.core.container¶
container
¶
Dependency injection container.
Builders never construct their collaborators directly — they ask the container. That is what makes every part of a pipeline swappable without touching Windlass source: bind a different factory (or a ready-made instance) and every consumer downstream picks it up.
The container understands three ways to name a dependency, which is what lets the fluent API accept all of these:
.llm("openai") # a registry name
.llm(MyCustomLLM(model="x")) # an instance
.llm(lambda: MyCustomLLM(model="x")) # a factory
Containers nest. :meth:Container.scope returns a child that inherits every
binding and can override any of them without disturbing the parent — that is how
a sub-agent gets its own LLM while sharing the parent's tracer.
Binding
dataclass
¶
Binding(
factory: Callable[..., Any],
singleton: bool = True,
value: Any = None,
produced: bool = False,
)
How to produce one dependency.
Attributes:
| Name | Type | Description |
|---|---|---|
factory |
Callable[..., Any]
|
Callable producing the value. May take zero arguments or a
single :class: |
singleton |
bool
|
When True the value is produced once and reused. |
value |
Any
|
The memoised singleton value, once produced. |
produced |
bool
|
Whether |
Provide
dataclass
¶
Marker describing a dependency to be resolved later.
Use it as a default value to declare that a parameter should come from the container::
def build(llm = Provide("llm", "openai")):
...
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
Component kind, e.g. |
name |
str | None
|
Registry name to fall back on when the container has no binding. |
config |
dict[str, Any]
|
Keyword arguments for construction. |
Container
¶
A hierarchical, thread-safe dependency container.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent
|
Container | None
|
Optional parent whose bindings are inherited. |
None
|
registry
|
Registry | None
|
Component registry used to resolve string names. Defaults to
the global :data: |
None
|
Example
c = Container() _ = c.bind("greeting", lambda: "hello") c.resolve("greeting") 'hello' child = c.scope() _ = child.bind("greeting", lambda: "hi") c.resolve("greeting"), child.resolve("greeting") ('hello', 'hi')
Source code in src\windlass\core\container.py
bind
¶
bind(
key: Any,
factory: Callable[..., Any],
*,
singleton: bool = True,
override: bool = True
) -> Container
Bind key to a factory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Any
|
A string, a type, or any hashable token. |
required |
factory
|
Callable[..., Any]
|
Zero-argument callable, or one accepting the container. |
required |
singleton
|
bool
|
Reuse the first produced value. |
True
|
override
|
bool
|
Replace an existing binding. When False, an existing binding is kept and the call is a no-op. |
True
|
Returns:
| Type | Description |
|---|---|
Container
|
|
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If |
Source code in src\windlass\core\container.py
bind_instance
¶
Bind key to an already-constructed object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Any
|
Lookup key. |
required |
instance
|
Any
|
The object to return on resolution. |
required |
override
|
bool
|
Replace an existing binding. |
True
|
Returns:
| Type | Description |
|---|---|
Container
|
|
Source code in src\windlass\core\container.py
unbind
¶
has
¶
Return whether key resolves here or in any ancestor.
resolve
¶
Produce the value bound to key.
Singletons are produced at most once, under a lock, so concurrent first resolutions do not both run the factory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Any
|
Lookup key. |
required |
default
|
Any
|
Returned instead of raising when nothing is bound. |
_UNSET
|
Returns:
| Type | Description |
|---|---|
Any
|
The resolved value. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
When nothing is bound and no |
Source code in src\windlass\core\container.py
keys
¶
Return every key visible from here, including inherited ones.
Source code in src\windlass\core\container.py
component
¶
Resolve a component from a name, an instance, or a factory.
This is the single funnel through which every builder turns user input
into a live component, and the reason .llm(...) accepts three
different kinds of argument.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
Component kind, e.g. |
required |
spec
|
Any
|
One of:
|
None
|
**config
|
Any
|
Constructor keyword arguments for the string form. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The live component. |
Raises:
| Type | Description |
|---|---|
ComponentNotFoundError
|
For an unknown registry name. |
ConfigurationError
|
For an unusable |
Example
c = Container() llm = c.component("llm", "fake", responses=["hi"]) llm.complete("x").content 'hi'
Source code in src\windlass\core\container.py
bind_component
¶
bind_component(
kind: str, spec: Any = None, /, *, singleton: bool = True, **config: Any
) -> Container
Bind kind to a lazily resolved component.
The component is not constructed until something resolves it, which keeps builder chains cheap and lets configuration keep changing right up until the first call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
Component kind, used as the binding key. |
required |
spec
|
Any
|
Name, instance or factory — see :meth: |
None
|
singleton
|
bool
|
Reuse the constructed component. |
True
|
**config
|
Any
|
Constructor keyword arguments. |
{}
|
Returns:
| Type | Description |
|---|---|
Container
|
|
Source code in src\windlass\core\container.py
scope
¶
root_container
¶
Return the process-wide root container.
Bindings placed here are inherited by every builder created afterwards, which makes it the right place for application-wide wiring::
root_container().bind_instance("tracer", MyTracer())
Returns:
| Type | Description |
|---|---|
Container
|
The shared root :class: |