Core¶
Building and inspecting¶
clientwright: one resilience and observability core, many HTTP clients.
build() returns the REAL native client of the chosen adapter - a genuine
httpx.AsyncClient, not a wrapper - with retries, circuit breaking, owned
redirects, deadlines and telemetry wired UNDER its public API. inspect()
returns the handle with the config-application report and runtime state.
A bare install is a working install: the core has zero dependencies; adapters and observability backends load lazily behind extras.
build(adapter, config, deps=None)
¶
Build an ASYNC native client (e.g. a genuine httpx.AsyncClient).
build_sync(adapter, config, deps=None)
¶
Build a SYNC native client (e.g. a genuine httpx.Client).
build_handle(adapter, config, deps=None)
¶
Build an ASYNC native client and return its full handle.
Source code in clientwright/__init__.py
build_sync_handle(adapter, config, deps=None)
¶
Build a SYNC native client and return its full handle.
Source code in clientwright/__init__.py
inspect_client(client)
¶
The handle of a built client, or None for foreign objects.
Source code in clientwright/core/plan.py
client_config_from_settings(settings, service_name)
¶
Build a ClientConfig from the structural settings shape used by services.
Source code in clientwright/core/contracts/settings.py
capabilities_matrix()
¶
Capability records of every registered adapter.
Adapter capability modules are zero-dependency and are imported dynamically by registry path, so the matrix builds in an environment without a single extra installed (and the core->adapters import-linter contract holds: there are no static imports).
Source code in clientwright/core/capabilities.py
register_adapter(name, adapter_target, capabilities_target)
¶
Register a third-party adapter: "module.path:Class" strings, lazily loaded.
Source code in clientwright/core/registry.py
Configuration¶
Universal, transport-free client configuration.
Three rules keep this config honest:
UNSETis not "disabled": an unset knob defers to the adapter's native default and the deferral is visible in theConfigApplicationReport.- No transport types appear here, so the same config is readable by any adapter.
- Anything set but inexpressible on the chosen adapter lands in
report.dropped; underUnsupportedPolicy.STRICTthat fails the build.
CircuitBreakerConfig
dataclass
¶
Circuit breaker keyed per CircuitKey; one signal per logical call.
A STATUS outcome trips the breaker only for 5xx responses.
Source code in clientwright/core/config.py
ClientConfig
dataclass
¶
The whole client, described once, readable by every adapter.
Source code in clientwright/core/config.py
NativeOptions
dataclass
¶
Raw passthrough to the native client, grouped by adapter-declared slots.
Validated at build time: unknown slots, reserved keys, typos and collisions with explicitly set config fields are all errors, not silent behavior.
Source code in clientwright/core/config.py
ObservabilityConfig
dataclass
¶
Which telemetry channels are active; only knobs that actually work exist here.
Two-stage URL scrubbing before anything reaches a log line or a span:
sensitive_query_params redacts by parameter name, then url_masker
(if set) sees the whole redacted URL and may scrub PII by value - the
email in a path segment that no name list can catch. See
:class:~clientwright.core.contracts.observability.MaskerProtocol for the
failure contract.
Source code in clientwright/core/config.py
PoolConfig
dataclass
¶
Connection pool shape; unset knobs defer to native defaults.
Source code in clientwright/core/config.py
ProxyConfig
dataclass
¶
Explicit proxy or environment-driven proxies (mutually exclusive).
Source code in clientwright/core/config.py
RetryConfig
dataclass
¶
Owned retry policy; the engine runs the loop, adapters only send.
Source code in clientwright/core/config.py
TimeoutConfig
dataclass
¶
Timeout budget of a logical call.
total is guaranteed everywhere: the engine enforces it with a monotonic
deadline shared by all attempts, backoff sleeps and redirect hops.
Phase knobs left UNSET defer to the adapter's native defaults.
Source code in clientwright/core/config.py
is_set(value)
¶
Data model¶
Transport-neutral data model shared by engines, policies and adapters.
Attempt
dataclass
¶
One physical attempt inside a logical call.
Source code in clientwright/core/model.py
CircuitKey
¶
ConnMetrics
dataclass
¶
Optional per-connection timings; None fields mean the adapter cannot see them.
Source code in clientwright/core/model.py
FailureKind
¶
Bases: StrEnum
Shared alphabet of call outcomes.
An alphabet, not a shared partition: each adapter declares which kinds it
can actually emit (AdapterCapabilities.emits) and which finer kinds
collapse into coarser ones (AdapterCapabilities.collapses).
Source code in clientwright/core/model.py
Outcome
dataclass
¶
Result of a single attempt. kind is None means success.
Source code in clientwright/core/model.py
RequestInfo
dataclass
¶
Low-cardinality identity of a logical call.
Source code in clientwright/core/model.py
ResolvedTimeouts
dataclass
¶
Per-attempt timeout plan handed to the adapter before each send.
attempt is the hard ceiling of the whole attempt; async engines enforce
it with task cancellation, sync engines can only clamp the phases below.
Source code in clientwright/core/model.py
origin_of(url)
¶
Normalize a URL to its origin: scheme://host:port lowercased, default ports explicit.
Source code in clientwright/core/model.py
Errors¶
Kernel error hierarchy.
Errors raised on the call path (CallError subclasses) are translated by each
adapter into classes that ALSO inherit the native client's error family, so a
user's except httpx.HTTPError keeps working. Errors raised at build time are
plain kernel errors.
CallError
¶
Bases: ClientwrightError
Base for errors raised while executing a call; adapters dual-inherit these.
CircuitOpenError
¶
Bases: CallError
Local refusal: the circuit for this key is open.
Source code in clientwright/core/errors.py
ClientwrightError
¶
DeadlineExceededError
¶
Bases: CallError
The total deadline of the logical call was exhausted.
Source code in clientwright/core/errors.py
NativeConfigError
¶
Bases: ClientwrightError
Base for invalid native passthrough configuration.
NotReplayableError
¶
UnknownAdapterError
¶
Bases: ClientwrightError
Requested adapter name is not registered.
Source code in clientwright/core/errors.py
UnsupportedCapabilityError
¶
Bases: ClientwrightError
Config asks for something the chosen adapter cannot express (STRICT mode).
Capabilities¶
Machine-readable declarations of what each adapter can and cannot do.
Uniformity is sold by policy and telemetry schema, not by pretending semantics
match: every divergence is declared here, validated at build time, and visible
in the ConfigApplicationReport instead of a README paragraph.
AdapterCapabilities
dataclass
¶
Everything an adapter admits about itself, in one frozen record.
Source code in clientwright/core/capabilities.py
ConfigApplicationReport
dataclass
¶
What actually happened when a config met an adapter.
Source code in clientwright/core/capabilities.py
enforce(policy)
¶
Apply the on_unsupported policy: raise, warn or stay silent.
Source code in clientwright/core/capabilities.py
capabilities_matrix()
¶
Capability records of every registered adapter.
Adapter capability modules are zero-dependency and are imported dynamically by registry path, so the matrix builds in an environment without a single extra installed (and the core->adapters import-linter contract holds: there are no static imports).
Source code in clientwright/core/capabilities.py
dead_retryable_kinds(requested, capabilities)
¶
Kinds the retry policy waits for but the adapter can never produce.
Collapsed kinds count as reachable through their coarser target.
Source code in clientwright/core/capabilities.py
Plans, runtime and handles¶
Compiled call plans, the APP-scope runtime and client handles.
CallPlan
dataclass
¶
Everything the engine needs, compiled once at build time.
Source code in clientwright/core/plan.py
ClientHandle
dataclass
¶
The real native client plus everything clientwright knows about it.
Source code in clientwright/core/plan.py
ClientRuntime
¶
State that must outlive REQUEST-scoped clients: circuits, budgets, limiters.
Give this APP scope in DI. A fresh runtime per client makes the circuit breaker and retry budget decorative.
Source code in clientwright/core/plan.py
compile_plan(config, capabilities, *, native_timeout_defaults, applied_natively=frozenset(), emulated=frozenset(), dropped=None, native_overrides=None)
¶
Compile config against capabilities; the caller enforces the report.
Source code in clientwright/core/plan.py
inspect_client(client)
¶
The handle of a built client, or None for foreign objects.
Source code in clientwright/core/plan.py
Per-call options¶
Ambient per-call options for adapters without a native per-request channel.
httpx carries route/idempotency in request.extensions; aiohttp, requests
and urllib3 have no such container, so the options travel through a ContextVar
set by the caller around the call. A task or thread-local context started
under call_options inherits it; siblings do not.
with call_options(route="/users/{id}", idempotent=True):
session.post(f"/users/{user_id}")
One shared channel on purpose: the block applies to whichever clientwright client sends inside it, uniformly across adapters.