You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Open discussion/proposal for a cohesive extensibility layer to support upcoming runtime features: tracing / instrumentation, metrics, retries (#102), rate limiting (#103), pagination helpers (#104), configurable timeouts (#105), richer auth & pluggable security schemes (#106), and enhanced HTTPException context (#100). Several of these have individual issues already; this proposal focuses on the unifying mechanism rather than each feature’s business logic.
Motivation
Right now generated clients perform a straightforward request → status check → parse path. As we add resilience and observability features we risk:
Bloated per-endpoint templates (harder to maintain & test)
Divergent behavior between libraries (httpx / requests / aiohttp)
after_response(context) (inspect response before parsing)
on_error(context) (enrich / transform exception)
build_retry_state(context) (optional state container reused across attempts)
Context would carry:
operation_id, tag, path_template
request: method, url, headers, query params, path params, raw body (pre-serialization & post-serialization?)
attempt: current attempt number
client_config: reference to APIConfig
(after response) response: status, headers, body bytes (lazy streamed?)
Pros: Minimal, explicit. Cons: Harder to chain multiple independent concerns unless user wraps them manually.
Option B: Ordered Policy / Middleware Pipeline
Modelled loosely after Azure SDK or HTTPX custom dispatchers: a list of policies each exposing async/sync send(context, next).
Generator wraps core transport into final policy.
Policies can short-circuit, retry, mutate request & response.
Retries, rate limiting, timeouts become first-class reusable policies.
Pros: Composable, scalable for future features. Cons: Slightly more abstraction; more boilerplate for simple customization.
Option C: Event Bus + Subscribers
Emit simple events (request.start, request.finish, request.error, retry.scheduled, etc.) with a lightweight dispatcher.
Pros: Very low friction to listen; multiple subscribers easy. Cons: Mutating request/response safely needs conventions; ordering ambiguities.
Recommended Direction
Adopt Option B (Policy Pipeline) internally, while exposing a convenience layer that auto-builds policies from simple callbacks (covering Option A ergonomics). This hybrid keeps advanced extensibility without forcing complexity on casual consumers.
Minimal Initial Scope
Introduce a BasePolicy interface (sync & async variants) and a Pipeline orchestrator generated once per library.
Keep current public surface (e.g., method signatures). Policies internal unless user opts in by supplying a list through APIConfig (new field policies: list[Policy] | None).
Default behavior identical; Authorization header omission when token is None is benign improvement, documented in CHANGELOG.
Open Questions / Feedback Sought
Does the community prefer a callback-only model (simpler) vs full pipeline? Any strong reasons against the pipeline?
Is an event emission mechanism still desired in addition (for passive metrics) or should that be layered later via a TracingPolicy emitting events/spans?
Any edge cases in existing generators that would be harder to port into a pipeline abstraction we should call out early?
Summary
Open discussion/proposal for a cohesive extensibility layer to support upcoming runtime features: tracing / instrumentation, metrics, retries (#102), rate limiting (#103), pagination helpers (#104), configurable timeouts (#105), richer auth & pluggable security schemes (#106), and enhanced HTTPException context (#100). Several of these have individual issues already; this proposal focuses on the unifying mechanism rather than each feature’s business logic.
Motivation
Right now generated clients perform a straightforward request → status check → parse path. As we add resilience and observability features we risk:
A small, well-defined lifecycle contract can unlock these without turning the generator into a framework.
Goals
Non-Goals (initially)
Proposed Design Options
Option A: Lifecycle Callback Set
Generate an
OperationHooks(orClientHooks) dataclass with optional callables:before_request(context)(inspect/modify request – method, url, headers, body)after_response(context)(inspect response before parsing)on_error(context)(enrich / transform exception)build_retry_state(context)(optional state container reused across attempts)Context would carry:
operation_id,tag,path_templaterequest: method, url, headers, query params, path params, raw body (pre-serialization & post-serialization?)attempt: current attempt numberclient_config: reference toAPIConfigresponse: status, headers, body bytes (lazy streamed?)Pros: Minimal, explicit. Cons: Harder to chain multiple independent concerns unless user wraps them manually.
Option B: Ordered Policy / Middleware Pipeline
Modelled loosely after Azure SDK or HTTPX custom dispatchers: a list of policies each exposing async/sync
send(context, next).Pros: Composable, scalable for future features. Cons: Slightly more abstraction; more boilerplate for simple customization.
Option C: Event Bus + Subscribers
Emit simple events (
request.start,request.finish,request.error,retry.scheduled, etc.) with a lightweight dispatcher.Pros: Very low friction to listen; multiple subscribers easy. Cons: Mutating request/response safely needs conventions; ordering ambiguities.
Recommended Direction
Adopt Option B (Policy Pipeline) internally, while exposing a convenience layer that auto-builds policies from simple callbacks (covering Option A ergonomics). This hybrid keeps advanced extensibility without forcing complexity on casual consumers.
Minimal Initial Scope
BasePolicyinterface (sync & async variants) and aPipelineorchestrator generated once per library.requestis a small object (method, url, headers, query, json/data, expected_statuses, parse_json=bool, model parser callback).ResponseParsingPolicyto keep templates tiny.Performance Considerations
Testing Strategy
Migration / Backwards Compatibility
APIConfig(new fieldpolicies: list[Policy] | None).Open Questions / Feedback Sought
Alternatives Considered
Next Steps (if accepted)
Please share thoughts, concerns, preferences (Option A/B/C, hybrid, naming) and any must-have hooks we’ve overlooked before we lock in the baseline.