Contrib¶
Deadline budgets¶
deadline-budget integration ([deadline] extra).
The core stays zero-dependency: the engine consumes the structural
DeadlineSource protocol, and this module supplies sources backed by a
deadline-budget BudgetContext - plus the ambient channel that carries one.
deadline-budget deliberately has no implicit context: a BudgetContext is
handed from call site to call site by argument. A client engine sits far below
the code that knows the budget, so the ambient channel lives here::
from clientwright.contrib.deadline import AmbientDeadlineSource, use_budget
deps = AdapterDeps(deadline_source=AmbientDeadlineSource())
client = build("httpx", config, deps)
with use_budget(BudgetContext.create(total_seconds=5.0)):
await client.get("/users") # runs with what is left of those 5 seconds
Without the block every call behaves exactly as before: the ambient source finds no budget and the engine falls back to the configured total.
Typed structurally on purpose - this module never imports deadline-budget, so
any object with remaining()/expired() fits; the [deadline] extra
exists to pin the library for services that use the real one. A ContextVar is
per-task: a task started under use_budget inherits the budget (a fan-out
shares one deadline), siblings do not.
AmbientDeadlineSource
¶
DeadlineSource reading whatever use_budget installed in this task.
The right default for a long-lived client: each call picks up the budget of the request being served, and calls outside any budget run unbounded by it (only the configured total applies).
Source code in clientwright/contrib/deadline.py
BudgetDeadlineSource
¶
DeadlineSource over ONE fixed budget - for a client scoped to a request.
Source code in clientwright/contrib/deadline.py
DeadlineBudgetProtocol
¶
Bases: Protocol
What a source needs of a request budget: the shape of deadline_budget.BudgetContext.
Source code in clientwright/contrib/deadline.py
current_budget()
¶
use_budget(budget)
¶
Install budget as the current one for the duration of the block.
The previous value is restored on the way out, so nesting works and an inner budget cannot outlive its block. Passing None detaches an inherited budget - for background work that must not die with the request that spawned it.
Source code in clientwright/contrib/deadline.py
Dishka¶
clientwright.contrib.dishka imports dishka at module import time (by design —
a DI integration without the DI library is meaningless), so it is documented
here rather than auto-rendered.
ClientwrightProvider(adapter, config, deps=None) — a dishka.Provider
with scope=Scope.APP providing:
ClientRuntime— the injecteddeps.runtimeif given, elseClientRuntime.for_config(config); one per container, shared.ClientHandle[Any]— an async generator provide that builds the native client with the shared runtime and closes it (aclose()/close()) infinallywhen the container shuts down.
Usage, scope rules and multi-upstream patterns: Guide → Dependency injection.