API reference: adapters¶
Every adapter lives behind an extra. Importing one
without its extra raises an ImportError naming what to install.
adapters.builtin¶
Zero-dependency entrypoints, also re-exported from the top-level package.
First-party zero-dependency entrypoint adapters (no extra required).
DaemonEntrypoint
¶
Bases: ScopedEntrypoint
Runs func(scope, stop) in one long-lived unit scope.
The function is expected to loop until stop is set.
Source code in servicewright/adapters/builtin/daemon.py
bind(ctx)
async
¶
drain(grace)
async
¶
serve(*, stop)
async
¶
Open one unit scope and hand control to the user loop.
stop()
async
¶
unit_scope(context=None)
¶
Open a per-unit-of-work DI scope.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before :meth: |
Source code in servicewright/core/contracts/bases.py
OneShotEntrypoint
¶
Bases: ScopedEntrypoint
Runs func exactly once inside a fresh unit scope, then returns.
Being essential by default, its return stops the whole service.
Source code in servicewright/adapters/builtin/oneshot.py
bind(ctx)
async
¶
drain(grace)
async
¶
serve(*, stop)
async
¶
Open a unit scope, run the function once, then return.
stop()
async
¶
unit_scope(context=None)
¶
Open a per-unit-of-work DI scope.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before :meth: |
Source code in servicewright/core/contracts/bases.py
adapters.fastapi¶
FastAPI entrypoint adapter ([fastapi] extra).
CORSMiddlewareConfig
dataclass
¶
Configuration for Starlette's CORSMiddleware.
Source code in servicewright/adapters/fastapi/config.py
__post_init__()
¶
Reject the insecure allow_credentials + wildcard-origin combo.
Source code in servicewright/adapters/fastapi/config.py
CorrelationIdMiddlewareConfig
dataclass
¶
How the request id is returned to the client.
The id itself is owned by :class:ContextMiddleware — the same value that
lands in the context store, the logs and outbound propagation. Echoing it
is what lets a caller quote an id that actually appears in your logs.
Source code in servicewright/adapters/fastapi/config.py
FastApiEntrypoint
¶
Bases: ServerEntrypoint
A FastAPI server entrypoint driven by the :class:Host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
HttpConfig | None
|
Self-contained server configuration (NOT read from settings). |
None
|
routers
|
tuple[Any, ...]
|
APIRouter instances to |
()
|
routes_registerer
|
RoutesRegisterer | None
|
Optional callback to register routes imperatively,
resolved at |
None
|
middlewares
|
MiddlewareConfig | None
|
Middleware stack configuration. |
None
|
exception_handlers
|
dict[type[Exception], ExceptionHandler] | None
|
Extra |
None
|
default_exception_handlers
|
bool
|
Install the default handlers
(validation/HTTP/ServiceError/deadline/unhandled). Default |
True
|
error_renderer
|
HttpErrorRendererProtocol | None
|
Wire-format renderer used by the default handlers;
|
None
|
metrics
|
bool
|
Expose in-app Prometheus metrics at |
False
|
configure_app
|
ConfigureApp | None
|
Final hook called with |
None
|
kind
|
str
|
Telemetry label (default |
'http'
|
essential
|
bool
|
Whether the entrypoint's exit/failure stops the process. |
True
|
Source code in servicewright/adapters/fastapi/entrypoint.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
app
property
¶
The built FastAPI application (None before :meth:bind).
bound_port
property
¶
The actually bound port (useful when config.port == 0).
config
property
¶
The server configuration.
bind(ctx)
async
¶
Build the FastAPI app and open the listening socket.
The socket is opened here, not in :meth:serve, so a port clash aborts
startup with an OSError while readiness is still false — instead of
a process that reports ready and serves nothing. It also makes
port=0 usable: :attr:bound_port reports what the OS picked.
Source code in servicewright/adapters/fastapi/entrypoint.py
build_app(ctx)
async
¶
Construct a fully-configured FastAPI app from the :class:ServiceContext.
Exposed for testability: callers can build the app without driving the serve loop. The app deliberately has NO container-managing lifespan — the Host owns the application scope.
Source code in servicewright/adapters/fastapi/entrypoint.py
drain(grace)
async
¶
serve(*, stop)
async
¶
Serve on the bound socket until the host's stop event is set.
Returns while the server is still accepting connections: the Host flips
readiness to false first and only then calls :meth:drain, which is
what closes the listener. Shutting uvicorn down here instead would make
the readiness endpoint die before the load balancer stops routing, and
would render the Host's drain grace meaningless.
A server that dies on its own (a fatal uvicorn error) ends the wait and the failure is re-raised, so an essential entrypoint cannot leave the process alive and idle.
Source code in servicewright/adapters/fastapi/entrypoint.py
FastApiPlugin
¶
Declarative wiring: register a :class:FastApiEntrypoint on the host.
Pass the same arguments as :class:FastApiEntrypoint; on_register builds
it and adds it to the host.
Source code in servicewright/adapters/fastapi/entrypoint.py
entrypoint
property
¶
The entrypoint that will be registered on the host.
GZipMiddlewareConfig
dataclass
¶
HealthConfig
dataclass
¶
Configuration for the /system health probe routes.
Source code in servicewright/adapters/fastapi/config.py
HttpConfig
dataclass
¶
Self-contained configuration for an HTTP server entrypoint.
Taken at construction, never read from global settings, so the AppSpec stays transport-neutral.
Source code in servicewright/adapters/fastapi/config.py
address
property
¶
Return the host:port bind address.
LivenessResponse
¶
LoggingMiddlewareConfig
dataclass
¶
Configuration for the request-logging middleware.
Source code in servicewright/adapters/fastapi/config.py
MetricsInstrumentatorConfig
dataclass
¶
Configuration for prometheus-fastapi-instrumentator.
Source code in servicewright/adapters/fastapi/config.py
MiddlewareConfig
dataclass
¶
Configuration for the standard platform middleware stack.
The simple booleans toggle the parameter-less platform middlewares; the
complex configs carry their own options. custom lets callers append
extra ASGI middleware classes (added first so they run last in the stack).
Source code in servicewright/adapters/fastapi/config.py
unit_scope = True
class-attribute
instance-attribute
¶
Open one UnitScope per request (UnitScopeMiddleware, outermost).
Set False when the framework's own DI integration already owns the
request scope — dishka's setup_dishka for instance — so the two never
open two scopes per request. UnitScopeDep / current_unit_scope()
then raise LookupError; resolve through that integration instead.
OtelBaggageSetter
¶
Bases: ContextSetter
Put request/user/trace identifiers into OpenTelemetry Baggage.
Baggage rides the W3C baggage header, so OTel-instrumented clients
(httpx, grpc, kafka) propagate the values to downstream services with no
custom code. Filter propagators on clients calling third parties if these
identifiers must not leave your system.
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
Source code in servicewright/adapters/fastapi/context.py
set(context_data)
¶
Attach one OTel context carrying the values; return the detacher.
Source code in servicewright/adapters/fastapi/context.py
ProblemDetails
¶
Bases: BaseModel
RFC 9457 Problem Details document (application/problem+json).
The shape produced by the default
:class:~servicewright.core.errors.ProblemDetailsRenderer; extra members
are allowed per the RFC (custom renderers may add their own extensions).
Source code in servicewright/adapters/fastapi/schemas.py
ReadinessResponse
¶
StructlogSetter
¶
Bases: ContextSetter
Bind request/user/trace identifiers into structlog contextvars.
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
Source code in servicewright/adapters/fastapi/context.py
set(context_data)
¶
Bind the contextvars and return a remover.
Source code in servicewright/adapters/fastapi/context.py
UnitScopeMiddleware
¶
Open one UnitScope per request and expose it three ways.
Installed by the :class:FastApiEntrypoint as the outermost wrapper around
the handler unless MiddlewareConfig.unit_scope is off. The scope
carries the Request as its context and stays open until the
response is fully delivered.
This is deliberately a raw ASGI middleware rather than a
BaseHTTPMiddleware: the latter hands control back as soon as the
response starts, so the scope — and with it every REQUEST-scoped
dependency, e.g. a database session — would be finalized while a streaming
body is still being produced and before BackgroundTasks run, truncating
responses that the client already received a 200 for. Awaiting the inner app
to completion keeps the scope alive for the whole exchange, which is also
what the Litestar adapter does.
Source code in servicewright/adapters/fastapi/unit_scope.py
__call__(scope, receive, send)
async
¶
Wrap the request in a fresh unit scope bound to request.state + a contextvar.
Source code in servicewright/adapters/fastapi/unit_scope.py
current_unit_scope()
¶
Return the :class:UnitScopeProtocol for the in-flight HTTP request.
Raises:
| Type | Description |
|---|---|
LookupError
|
If called outside a request handled by
:class: |
Source code in servicewright/adapters/fastapi/unit_scope.py
get_default_context_setters()
¶
Return the default context setters.
Both are SOFT capabilities, activated by what is installed: the structlog
setter (log correlation out of the box) and the OTel Baggage setter (so the
identifiers propagate to downstream services via instrumented clients).
Both ship with servicewright[observability]; without them the request
context still lands in the transport-neutral store.
Source code in servicewright/adapters/fastapi/context.py
get_unit_scope(request)
¶
FastAPI dependency returning the per-request unit scope.
Resolves from request.state (set by :class:UnitScopeMiddleware).
Example
from typing import Annotated from fastapi import Depends async def handler(scope: Annotated[UnitScopeProtocol, Depends(get_unit_scope)]): ... use_case = await scope.get(MyUseCase)
Source code in servicewright/adapters/fastapi/unit_scope.py
setup_default_exception_handlers(app, *, renderer=None)
¶
Register the default exception handlers on app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
FastAPI
|
The FastAPI application. |
required |
renderer
|
HttpErrorRendererProtocol | None
|
The wire-format renderer; defaults to RFC 9457
:class: |
None
|
Source code in servicewright/adapters/fastapi/exceptions.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
setup_metrics_instrumentator(app, *, config=None, metrics_path=DEFAULT_METRICS_PATH)
¶
Instrument app and expose Prometheus metrics at metrics_path.
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
Source code in servicewright/adapters/fastapi/metrics.py
adapters.litestar¶
Litestar entrypoint adapter ([litestar] extra).
HealthConfig
dataclass
¶
Configuration for the /system health probe routes.
Source code in servicewright/adapters/litestar/config.py
LitestarConfig
dataclass
¶
Self-contained configuration for a Litestar HTTP server entrypoint.
Taken at construction, never read from global settings, so the AppSpec stays transport-neutral.
Source code in servicewright/adapters/litestar/config.py
address
property
¶
Return the host:port bind address.
unit_scope = True
class-attribute
instance-attribute
¶
Open one UnitScope per request (UnitScopeMiddleware, outermost) and
provide it app-wide as the reserved unit_scope dependency.
Set False when the framework's own DI integration already owns the
request scope — dishka's setup_dishka for instance — so the two never
open two scopes per request. Neither the middleware nor the dependency is
installed then, and current_unit_scope() raises LookupError.
LitestarEntrypoint
¶
Bases: ServerEntrypoint
A Litestar server entrypoint driven by the :class:Host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
LitestarConfig | None
|
Self-contained server configuration (NOT read from settings). |
None
|
route_handlers
|
tuple[Any, ...]
|
Litestar route handlers / routers to register on the app. |
()
|
route_registerer
|
RouteRegisterer | None
|
Optional callback returning extra route handlers,
resolved at |
None
|
configure_app
|
ConfigureApp | None
|
Final hook called with |
None
|
kind
|
str
|
Telemetry label (default |
'http'
|
essential
|
bool
|
Whether the entrypoint's exit/failure stops the process. |
True
|
Source code in servicewright/adapters/litestar/entrypoint.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
app
property
¶
The built Litestar application (None before :meth:bind).
bound_port
property
¶
The actually bound port (useful when config.port == 0).
config
property
¶
The server configuration.
bind(ctx)
async
¶
Build the Litestar app and open the listening socket.
Binding here (not in :meth:serve) turns a port clash into an OSError
during startup instead of a process that reports ready and serves nothing.
Source code in servicewright/adapters/litestar/entrypoint.py
build_app(ctx)
async
¶
Construct a fully-configured Litestar app from the :class:ServiceContext.
Exposed for testability: callers can build the app without driving the serve loop. The app deliberately has NO container-managing lifespan — the Host owns the application scope.
Source code in servicewright/adapters/litestar/entrypoint.py
drain(grace)
async
¶
serve(*, stop)
async
¶
Serve on the bound socket until the host's stop event is set.
Returns while still accepting: the Host flips readiness to false first
and only then calls :meth:drain, which closes the listener.
Source code in servicewright/adapters/litestar/entrypoint.py
LitestarPlugin
¶
Declarative wiring: register a :class:LitestarEntrypoint on the host.
Pass the same arguments as :class:LitestarEntrypoint; on_register builds
it and adds it to the host.
Source code in servicewright/adapters/litestar/entrypoint.py
entrypoint
property
¶
The entrypoint that will be registered on the host.
UnitScopeMiddleware
¶
Bases: ASGIMiddleware
Open one UnitScope per request and expose it two ways.
Added to the Litestar app by the :class:LitestarEntrypoint unless
LitestarConfig.unit_scope is off. The scope carries the Request as
its context and is closed/reset after the response is produced.
Restricted to HTTP scopes (websockets/lifespan pass through untouched).
Source code in servicewright/adapters/litestar/unit_scope.py
handle(scope, receive, send, next_app)
async
¶
Wrap the request in a fresh unit scope bound to scope state + a contextvar.
Source code in servicewright/adapters/litestar/unit_scope.py
build_health_routes(health, *, liveness_path, readiness_path)
¶
Return /system liveness + readiness route handlers bound to health.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
health
|
HealthRegistry
|
The transport-agnostic registry to read state from. |
required |
liveness_path
|
str
|
Route path for the liveness probe. |
required |
readiness_path
|
str
|
Route path for the readiness probe. |
required |
Returns:
| Type | Description |
|---|---|
list[HTTPRouteHandler]
|
A list of Litestar route handlers ready to pass to |
Source code in servicewright/adapters/litestar/configurators.py
current_unit_scope()
¶
Return the :class:UnitScopeProtocol for the in-flight Litestar request.
Raises:
| Type | Description |
|---|---|
LookupError
|
If called outside a request handled by
:class: |
Source code in servicewright/adapters/litestar/unit_scope.py
get_unit_scope(request)
¶
Litestar dependency returning the per-request unit scope.
Resolves from the ASGI connection scope state (set by
:class:UnitScopeMiddleware).
Example
from litestar import get from litestar.di import Provide from servicewright.adapters.litestar import get_unit_scope @get("/users/{user_id:str}", dependencies={"unit_scope": Provide(get_unit_scope)}) ... async def handler(user_id: str, unit_scope: object) -> dict: ... use_case = await unit_scope.get(MyUseCase) ... return await use_case.execute(user_id)
Source code in servicewright/adapters/litestar/unit_scope.py
adapters.grpc¶
gRPC entrypoint adapter ([grpc] extra).
GrpcConfig
dataclass
¶
Configuration for a gRPC server entrypoint.
Satisfies grpc_server_kit.protocols.GrpcServerSettingsProtocol so it can
be consumed directly by the grpc-server-kit primitives.
Source code in servicewright/adapters/grpc/config.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
address
property
¶
Return the host:port bind address.
enable_channelz = False
class-attribute
instance-attribute
¶
Serve grpc.channelz.v1.Channelz (default: off).
Channelz is gRPC's debug/introspection service. It exposes GetServers /
GetServerSockets / GetSocket / GetTopChannels on the SAME port as
production traffic, revealing connected peers' remote addresses and
per-socket call and byte counters. It is unauthenticated unless an
interceptor of yours authenticates it, so it is opt-in.
enable_reflection = False
class-attribute
instance-attribute
¶
Serve grpc.reflection.v1alpha.ServerReflection (default: off).
Reflection lets tools such as grpcurl resolve symbols from the process's
descriptor pool without a local .proto. It is a development convenience
and is unauthenticated unless an interceptor of yours authenticates it, so
it is opt-in: turn it on for local/staging, leave it off in production.
grace_period = DEFAULT_GRACE_PERIOD_SECONDS
class-attribute
instance-attribute
¶
Seconds in-flight RPCs may finish in after intake stops, on drain.
This is the entrypoint's own drain budget. The Host also allots a grace when
it calls drain(grace) and aborts the drain shortly after that allowance
expires, so the effective budget is min(host_grace, grace_period):
lowering this value shortens shutdown, raising it above the Host's allowance
has no effect.
health_refresh_interval = DEFAULT_HEALTH_REFRESH_INTERVAL_SECONDS
class-attribute
instance-attribute
¶
Seconds between re-evaluations of readiness onto the health service.
The gRPC health service is a push API: something must re-evaluate the
:class:~servicewright.core.health.HealthRegistry and push the result. The
entrypoint polls at this interval while serving, so a failing check flips
the health service to NOT_SERVING within roughly one interval. Set 0 to
disable polling (the status is then only pushed once at startup).
health_service_names = ()
class-attribute
instance-attribute
¶
Concrete gRPC service names to report health for, besides the overall one.
The health servicer always reports the overall (empty-string) name — the one
a plain readinessProbe: {grpc: {port: ...}} checks. List your own
services here (e.g. ("my.pkg.Orders",)) to also answer
Check(service="my.pkg.Orders"); unlisted names are answered NOT_FOUND by
the standard health servicer.
__post_init__()
¶
Reject timings the gRPC stack cannot honour.
Source code in servicewright/adapters/grpc/config.py
GrpcEntrypoint
¶
Bases: ServerEntrypoint
A gRPC server entrypoint driven by the :class:Host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
GrpcConfig
|
Self-contained server configuration (NOT read from settings). |
required |
servicers
|
ServicerRegisterer
|
Callback registering servicers on the gRPC server. |
required |
interceptors
|
Sequence[ServerInterceptor]
|
Static interceptors. They wrap the servicer inside the
unit-scope and metrics interceptors but outside the service-error
mapper, so a generic exception handler of yours (e.g. the kit's
|
()
|
interceptors_factory
|
InterceptorFactory | None
|
Optional callback returning extra interceptors,
resolved at |
None
|
context_setters
|
Sequence[ContextSetter] | None
|
Bridges that push the per-RPC context (request id, user id, trace id) into systems keeping their own store — structlog contextvars, OTel Baggage. Defaults to whichever of those is installed; pass an explicit sequence (possibly empty) to override. |
None
|
map_service_errors
|
bool
|
Convert raised
:class: |
True
|
enable_metrics
|
bool
|
Add the RPC metrics interceptor, recording through the
app's configured metrics sink ( |
False
|
metrics_prefix
|
str | None
|
Optional metric name prefix. |
None
|
kind
|
str
|
Telemetry label (default |
'grpc'
|
essential
|
bool
|
Whether the entrypoint's exit/failure stops the process. |
True
|
Source code in servicewright/adapters/grpc/entrypoint.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
bound_port
property
¶
The actually bound port (useful when config.port == 0).
config
property
¶
The server configuration.
bind(ctx)
async
¶
Create the server, register servicers + health, and bind the port.
Source code in servicewright/adapters/grpc/entrypoint.py
drain(grace)
async
¶
Stop accepting RPCs and let in-flight ones finish within grace.
The effective budget is min(grace, config.grace_period): the Host's
allowance bounds the shutdown, and the entrypoint's own setting can only
shorten it.
Source code in servicewright/adapters/grpc/entrypoint.py
serve(*, stop)
async
¶
Start the server and run until the host's stop event is set.
Returns while the server is still accepting: the Host flips readiness to
false and only then calls :meth:drain. The health poller runs for
exactly this window, so a dependency that fails mid-life flips the gRPC
health service to NOT_SERVING without any traffic being refused.
Source code in servicewright/adapters/grpc/entrypoint.py
stop()
async
¶
Hard stop the server immediately (idempotent with drain).
Source code in servicewright/adapters/grpc/entrypoint.py
GrpcHealthBridge
¶
Owns the aio health servicer and syncs it with a :class:HealthRegistry.
Source code in servicewright/adapters/grpc/health.py
enter_graceful_shutdown()
async
¶
refresh()
async
¶
Evaluate readiness and push the verdict onto the health servicer.
Runs every registered health check (same contract as the HTTP readiness
route), so a dependency outage flips the health service to
NOT_SERVING.
Source code in servicewright/adapters/grpc/health.py
register(server)
¶
watch(interval)
async
¶
Re-evaluate readiness every interval seconds until cancelled.
A refresh that fails is logged and retried on the next tick: a health poller must never take the server down.
Source code in servicewright/adapters/grpc/health.py
GrpcPlugin
¶
Declarative wiring: register a :class:GrpcEntrypoint on the host.
Pass the same arguments as :class:GrpcEntrypoint; on_register builds it
and adds it to the host.
Source code in servicewright/adapters/grpc/entrypoint.py
entrypoint
property
¶
The entrypoint that will be registered on the host.
GrpcServerMetricsRecorder
¶
The frozen 5-arg gRPC-server request recorder over generic instruments.
Source code in servicewright/adapters/grpc/metrics.py
record_request(service, method, status, grpc_code, duration)
¶
Record one served RPC.
Source code in servicewright/adapters/grpc/metrics.py
ServiceErrorInterceptor
¶
Bases: AsyncServerInterceptor
Abort RPCs failing with :class:ServiceError using the mapped status.
Added automatically by :class:GrpcEntrypoint (inside the metrics
interceptor, so aborts are recorded with their real status). Any other
exception passes through untouched — compose grpc-server-kit's
AsyncExceptionHandlerInterceptor for generic exception mapping.
Source code in servicewright/adapters/grpc/errors.py
around_call(call)
async
¶
Convert a raised ServiceError into a mapped gRPC abort.
UnitScopeInterceptor
¶
Bases: AsyncServerInterceptor
Open one UnitScope per RPC and expose it via a context variable.
This is added first to the interceptor chain by :class:GrpcEntrypoint, so
every downstream interceptor and the servicer see a live unit scope. The
scope carries the RPC payload (method name, correlation ids, idempotency
key, client ip / user agent) as its context, mirrored into the core
context store. Correlation ids come from x-request-id / x-user-id /
x-tenant-id / x-trace-id metadata (like the HTTP middleware), with
the request id auto-generated when the client sent none.
The same values are pushed into the supplied
:class:~servicewright.core.context.ContextSetters, which is what bridges
them into systems that keep their OWN store — structlog contextvars (so
every log line emitted during the RPC carries request_id) and OTel
Baggage. :class:GrpcEntrypoint installs
:func:~servicewright.adapters.grpc.context.get_default_context_setters by
default; a setter raising is logged and never fails the RPC.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
container
|
DependencyContainerProtocol
|
The DI container the per-RPC unit scope is opened on. |
required |
context_setters
|
Sequence[ContextSetter]
|
Bridges pushing the RPC context into external systems. |
()
|
Source code in servicewright/adapters/grpc/interceptors.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | |
around_call(call)
async
¶
Wrap the RPC in a fresh unit scope bound to a context variable.
Source code in servicewright/adapters/grpc/interceptors.py
current_unit_scope()
¶
Return the :class:UnitScopeProtocol for the in-flight RPC.
Raises:
| Type | Description |
|---|---|
LookupError
|
If called outside an RPC handled by
:class: |
Source code in servicewright/adapters/grpc/interceptors.py
get_client_context(context)
¶
Return (client_ip, user_agent) extracted from gRPC metadata.
get_client_ip(context)
¶
Extract the client IP from gRPC metadata.
Looks for x-forwarded-for first (proxy / load balancer), then
x-real-ip (nginx / other reverse proxies).
Source code in servicewright/adapters/grpc/metadata.py
get_idempotency_key(context)
¶
Extract the idempotency key from gRPC metadata (case-insensitive).
Source code in servicewright/adapters/grpc/metadata.py
get_user_agent(context)
¶
Extract the user agent from gRPC metadata.
Prefers the custom x-user-agent header (to avoid the gRPC-reserved
user-agent header), falling back to user-agent.
Source code in servicewright/adapters/grpc/metadata.py
adapters.apscheduler4¶
APScheduler 4.x scheduler entrypoint adapter ([apscheduler4] extra).
DuplicateScheduleError
¶
Bases: SchedulerError
Raised when two or more scheduled jobs share the same id.
Source code in servicewright/adapters/apscheduler4/exceptions.py
ScheduledJob
dataclass
¶
Description of a single scheduled job.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique identifier for the job (also the APScheduler schedule id). |
func |
Callable[[UnitScopeProtocol], Awaitable[None]]
|
Async callable invoked as |
trigger |
Trigger
|
APScheduler :class: |
args |
Sequence[Any]
|
Positional arguments passed after |
kwargs |
Mapping[str, Any]
|
Keyword arguments passed to |
max_instances |
int | None
|
Maximum number of concurrent running jobs for this id
(maps to APScheduler v4 |
misfire_grace_time |
float | None
|
Seconds after which a misfired run is skipped. |
coalesce |
CoalescePolicy | None
|
APScheduler v4 :class: |
Source code in servicewright/adapters/apscheduler4/config.py
SchedulerEntrypoint
¶
Bases: ScopedEntrypoint
An APScheduler-driven scheduler entrypoint driven by the :class:Host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
jobs
|
Sequence[ScheduledJob]
|
The scheduled jobs to register. Each fires inside a fresh per-job
:class: |
required |
kind
|
str
|
Telemetry label (default |
'scheduler'
|
essential
|
bool
|
Whether the entrypoint's exit/failure stops the process. |
True
|
Source code in servicewright/adapters/apscheduler4/entrypoint.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | |
jobs
property
¶
The configured scheduled jobs.
bind(ctx)
async
¶
Capture the container, enter the scheduler, and register schedules.
The :class:AsyncScheduler is entered here (not in :meth:serve) so it
outlives serve() and is still alive when the Host calls
:meth:drain / :meth:stop. The scheduler is held open on
:attr:_stack; it is torn down only in :meth:stop.
Raises:
| Type | Description |
|---|---|
DuplicateScheduleError
|
If two jobs share the same |
Source code in servicewright/adapters/apscheduler4/entrypoint.py
drain(grace)
async
¶
Pause every schedule, then let in-flight jobs finish within grace.
No NEW jobs fire once the schedules are paused, but jobs already running
keep going until they finish or grace elapses. This intentionally
does NOT call AsyncScheduler.stop(): in APScheduler 4.0.0a6 stop()
cancels the scheduler's cancel scope and hard-cancels in-flight jobs with
zero grace. Teardown happens later, in :meth:stop.
Source code in servicewright/adapters/apscheduler4/entrypoint.py
serve(*, stop)
async
¶
Start the scheduler and run until the host's stop event is set.
This only starts the (already-entered) scheduler in the background and
waits on stop. It deliberately does NOT close or null the scheduler:
the Host calls :meth:drain / :meth:stop AFTER serve() returns and
needs a live scheduler to drain.
Source code in servicewright/adapters/apscheduler4/entrypoint.py
stop()
async
¶
Tear the scheduler down (idempotent; safe before bind / after stop).
This is the only place the scheduler is shut down: closing the held exit
stack runs AsyncScheduler.__aexit__, which cancels the scheduler's
cancel scope and releases its services task group.
Source code in servicewright/adapters/apscheduler4/entrypoint.py
unit_scope(context=None)
¶
Open a per-unit-of-work DI scope.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before :meth: |
Source code in servicewright/core/contracts/bases.py
SchedulerError
¶
SchedulerPlugin
¶
Declarative wiring: register a :class:SchedulerEntrypoint on the host.
Pass the same arguments as :class:SchedulerEntrypoint; on_register
builds it and adds it to the host.
Source code in servicewright/adapters/apscheduler4/entrypoint.py
entrypoint
property
¶
The entrypoint that will be registered on the host.
adapters.apscheduler3¶
The public surface is identical to apscheduler4; only ScheduledJob.coalesce differs
(bool instead of CoalescePolicy).
APScheduler 3.x scheduler entrypoint adapter ([apscheduler3] extra).
Public surface is identical to the apscheduler4 adapter (enforced by an AST
conformance test); only the implementation differs, against APScheduler 3.x.
DuplicateScheduleError
¶
Bases: SchedulerError
Raised when two or more scheduled jobs share the same id.
Source code in servicewright/adapters/apscheduler3/exceptions.py
ScheduledJob
dataclass
¶
Description of a single scheduled job (APScheduler 3.x).
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique identifier for the job (also the APScheduler job id). |
func |
Callable[[UnitScopeProtocol], Awaitable[None]]
|
Async callable invoked as |
trigger |
Trigger
|
APScheduler 3.x :class: |
args |
Sequence[Any]
|
Positional arguments passed after |
kwargs |
Mapping[str, Any]
|
Keyword arguments passed to |
max_instances |
int | None
|
Maximum number of concurrent running instances for this
job (maps to APScheduler 3.x |
misfire_grace_time |
float | None
|
Seconds after which a misfired run is skipped. |
coalesce |
bool | None
|
Whether missed runs are collapsed into one (APScheduler 3.x
|
Source code in servicewright/adapters/apscheduler3/config.py
SchedulerEntrypoint
¶
Bases: ScopedEntrypoint
An APScheduler 3.x-driven scheduler entrypoint driven by the :class:Host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
jobs
|
Sequence[ScheduledJob]
|
The scheduled jobs to register. Each fires inside a fresh per-job
:class: |
required |
kind
|
str
|
Telemetry label (default |
'scheduler'
|
essential
|
bool
|
Whether the entrypoint's exit/failure stops the process. |
True
|
Source code in servicewright/adapters/apscheduler3/entrypoint.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
jobs
property
¶
The configured scheduled jobs.
bind(ctx)
async
¶
Capture the container, build the scheduler, and register jobs.
The scheduler is created and the jobs are added here, but it is not
started until :meth:serve.
Raises:
| Type | Description |
|---|---|
DuplicateScheduleError
|
If two jobs share the same |
Source code in servicewright/adapters/apscheduler3/entrypoint.py
drain(grace)
async
¶
Pause new job runs and let in-flight ones finish within grace.
APScheduler 3.x exposes no in-flight-job set of its own — and its
AsyncIOExecutor.shutdown cancels every pending future regardless of
wait — so this adapter tracks its own runs in :meth:_dispatch.
Without that, stop() would cancel a running job mid-transaction and
the whole grace window would be silently discarded, which is exactly the
behaviour the v4 sibling avoids.
Source code in servicewright/adapters/apscheduler3/entrypoint.py
serve(*, stop)
async
¶
Start the scheduler and run until the host's stop event is set.
Source code in servicewright/adapters/apscheduler3/entrypoint.py
stop()
async
¶
Shut the scheduler down (idempotent; safe before bind / after stop).
Source code in servicewright/adapters/apscheduler3/entrypoint.py
unit_scope(context=None)
¶
Open a per-unit-of-work DI scope.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before :meth: |
Source code in servicewright/core/contracts/bases.py
SchedulerError
¶
SchedulerPlugin
¶
Declarative wiring: register a :class:SchedulerEntrypoint on the host.
Source code in servicewright/adapters/apscheduler3/entrypoint.py
entrypoint
property
¶
The entrypoint that will be registered on the host.
adapters.dishka¶
Dishka DI binding ([dishka] extra).
Maps servicewright's two-tier scope model onto dishka's Scope.APP /
Scope.REQUEST. Importing this package requires servicewright[dishka].
DishkaContainer
¶
Adapt a dishka AsyncContainer to :class:DependencyContainerProtocol.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
container
|
AsyncContainer
|
The APP-scoped |
required |
Source code in servicewright/adapters/dishka/container.py
container
property
¶
The underlying APP-scoped dishka container.
app_scope()
async
¶
Yield the APP scope; closing it finalizes APP-scoped dependencies.
The dishka container is already at Scope.APP after
make_async_container; this context manager simply guarantees that
container.close() runs on exit (the Host closes the app scope last).
Source code in servicewright/adapters/dishka/container.py
unit_scope(context=None)
async
¶
Enter dishka's Scope.REQUEST carrying context as request data.
Exiting the async with closes the REQUEST scope, letting dishka
finalize every REQUEST-scoped dependency.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the request in |
Source code in servicewright/adapters/dishka/container.py
DishkaScope
¶
Thin wrapper over a dishka AsyncContainer exposing get.
Satisfies both :class:~servicewright.core.contracts.AppScopeProtocol and
:class:~servicewright.core.contracts.UnitScopeProtocol: get resolves a
dependency by type or string key, delegating to the wrapped container.
Source code in servicewright/adapters/dishka/container.py
adapters.observability¶
The author-facing sink ABCs. Concrete backends are resolved lazily through the registry.
Pluggable observability add-on adapters (extra-gated implementation layer).
Side-effect-free: importing this package pulls in no SDK. The author-facing sink
ABCs are re-exported here from :mod:base; concrete backends live in _metrics
/ _tracing / _errors / _logging (each behind its own extra and import
guard) and are resolved lazily through
:mod:servicewright.core.observability.registry. The runtime seams they
implement are defined in :mod:servicewright.core.contracts.observability.
Backends are transport-neutral: they expose generic instruments (counter / histogram); transport adapters own their metric names and recorders.
ErrorTrackingSink
¶
Bases: ABC
An error-tracking backend (sentry).
Source code in servicewright/adapters/observability/base.py
LoggingSink
¶
MetricsSink
¶
Bases: ABC
A metrics backend (prometheus / datadog / otel).
A transport-neutral instrument factory: transport adapters compose their
recorders (and own their frozen metric names) from these instruments.
Repeated requests for the same name must return the same instrument.
Source code in servicewright/adapters/observability/base.py
TracingSink
¶
Bases: ABC
A tracing backend (otel / datadog).
Source code in servicewright/adapters/observability/base.py
adapters.warmers¶
Bases: AsyncWarmer
Warmer for Postgres connection pool.
Executes a simple query (default SELECT 1) to ensure connections are established and the database is responsive.
Source code in servicewright/adapters/warmers/postgres.py
priority
property
¶
Get warmer priority.
raise_on_failure
property
¶
Whether orchestrator should raise when this warmer fails.
__init__(session_manager, query=DEFAULT_WARMUP_QUERY, timeout=DEFAULT_WARMUP_TIMEOUT, priority=0, raise_on_failure=True)
¶
Initialize Postgres warmer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_manager
|
Any
|
Async Postgres manager instance. |
required |
query
|
str
|
SQL query to execute for warmup. |
DEFAULT_WARMUP_QUERY
|
timeout
|
float
|
Maximum time to wait for the warmup operation in seconds. |
DEFAULT_WARMUP_TIMEOUT
|
priority
|
int
|
Execution priority (lower value means higher priority). |
0
|
raise_on_failure
|
bool
|
Whether warmup failure should fail startup. |
True
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If timeout is not positive. |
PostgresWarmupError
|
If sqlalchemy is not installed. |
Source code in servicewright/adapters/warmers/postgres.py
warmup()
async
¶
Perform Postgres warmup.
Raises:
| Type | Description |
|---|---|
PostgresWarmupError
|
If warmup fails or times out. |
Source code in servicewright/adapters/warmers/postgres.py
Bases: AsyncWarmer
Warmer for Redis connection pool.
Performs concurrent pings to fill the connection pool and verifies
connectivity using an optional health check from redis-client-kit.
Source code in servicewright/adapters/warmers/redis.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
priority
property
¶
Get warmer priority.
raise_on_failure
property
¶
Whether orchestrator should raise when this warmer fails.
__init__(redis_client, max_connections=None, timeout=DEFAULT_WARMUP_TIMEOUT, priority=0, raise_on_failure=True)
¶
Initialize Redis warmer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redis_client
|
RedisClientProtocol
|
Async Redis client instance. |
required |
max_connections
|
int | None
|
Number of concurrent pings to fill the pool. Defaults to DEFAULT_WARMUP_POOL_SIZE if None. |
None
|
timeout
|
float
|
Maximum time to wait for the warmup operation in seconds. |
DEFAULT_WARMUP_TIMEOUT
|
priority
|
int
|
Execution priority (lower value means higher priority). |
0
|
raise_on_failure
|
bool
|
Whether warmup failure should fail startup. |
True
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If max_connections (if provided) or timeout is not positive. |
Source code in servicewright/adapters/warmers/redis.py
warmup()
async
¶
Perform Redis warmup.
Raises:
| Type | Description |
|---|---|
RedisWarmupError
|
If health check fails or pings timeout. |
Source code in servicewright/adapters/warmers/redis.py
Bases: AsyncWarmer
Warmer for Kafka producer.
Ensures the producer is ready to send messages by fetching metadata via the internal client.
Source code in servicewright/adapters/warmers/kafka.py
priority
property
¶
Get warmer priority.
raise_on_failure
property
¶
Whether orchestrator should raise when this warmer fails.
__init__(producer, timeout=DEFAULT_WARMUP_TIMEOUT, priority=0, raise_on_failure=True)
¶
Initialize Kafka producer warmer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
producer
|
Any
|
Async Kafka producer instance. |
required |
timeout
|
float
|
Maximum time to wait for the warmup operation in seconds. |
DEFAULT_WARMUP_TIMEOUT
|
priority
|
int
|
Execution priority (lower value means higher priority). |
0
|
raise_on_failure
|
bool
|
Whether warmup failure should fail startup. |
True
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If timeout is not positive. |
Source code in servicewright/adapters/warmers/kafka.py
warmup()
async
¶
Perform Kafka warmup.
Raises:
| Type | Description |
|---|---|
KafkaProducerWarmupError
|
If metadata fetch fails or times out. |
Source code in servicewright/adapters/warmers/kafka.py
adapters.health¶
Health check that runs SELECT 1 against a SQLAlchemy async database.
Satisfies :class:~servicewright.core.contracts.HealthCheckerProtocol, so it
can be registered via HealthRegistry.add_check("postgres", check).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_maker
|
SessionMakerProtocol
|
Async session maker (e.g. |
required |
timeout
|
float
|
Maximum seconds to wait for the probe query. |
DEFAULT_DB_CHECK_TIMEOUT
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
ImportError
|
At construction time if the |
Source code in servicewright/adapters/health/postgres.py
check()
async
¶
Return True when SELECT 1 succeeds within the timeout.
Source code in servicewright/adapters/health/postgres.py
Health check that pings a Redis server within a timeout.
Satisfies :class:~servicewright.core.contracts.HealthCheckerProtocol, so it
can be registered via HealthRegistry.add_check("redis", check).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
RedisClientProtocol
|
Async Redis client exposing an awaitable |
required |
timeout
|
float
|
Maximum seconds to wait for the |
DEFAULT_REDIS_CHECK_TIMEOUT
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
ImportError
|
At construction time if the |
Source code in servicewright/adapters/health/redis.py
check()
async
¶
Return True when PING returns a truthy result within timeout.