forward: override auth_flow (generic) instead of async_auth_flow
The async_auth_flow override was being driven via 'await' in httpx's async dispatcher, which yielded 'NoneType is not awaitable' because a plain generator yielding a Request doesn't produce an awaitable. httpx.Auth has three hooks: sync_auth_flow, async_auth_flow, and the generic auth_flow. The default sync/async implementations delegate to auth_flow when subclasses override only that one, which is exactly the behaviour we want: one plain-generator implementation shared across sync and async clients. Override auth_flow, drop sync/async overrides.
This commit is contained in:
@@ -82,7 +82,7 @@ class _DynamicBearerAuth(httpx.Auth):
|
|||||||
def _current_token(self) -> str | None:
|
def _current_token(self) -> str | None:
|
||||||
return _lookup_bearer(self._server_config)
|
return _lookup_bearer(self._server_config)
|
||||||
|
|
||||||
def sync_auth_flow(self, request: httpx.Request):
|
def _inject(self, request: httpx.Request) -> None:
|
||||||
token = self._current_token()
|
token = self._current_token()
|
||||||
if token:
|
if token:
|
||||||
request.headers["Authorization"] = f"Bearer {token}"
|
request.headers["Authorization"] = f"Bearer {token}"
|
||||||
@@ -97,13 +97,18 @@ class _DynamicBearerAuth(httpx.Auth):
|
|||||||
"forward.skipped server=%s reason=no_inbound_bearer via=auth_flow",
|
"forward.skipped server=%s reason=no_inbound_bearer via=auth_flow",
|
||||||
self._server_name,
|
self._server_name,
|
||||||
)
|
)
|
||||||
yield request
|
|
||||||
|
|
||||||
async def async_auth_flow(self, request: httpx.Request):
|
def auth_flow(self, request: httpx.Request):
|
||||||
# httpx calls this generator per-request; all our work is
|
# Both ``sync_auth_flow`` and ``async_auth_flow`` on httpx.Auth
|
||||||
# pre-response so we yield once and we're done.
|
# delegate to ``auth_flow`` when subclasses override only the
|
||||||
for item in self.sync_auth_flow(request):
|
# generic path, which is exactly what we want: one implementation
|
||||||
yield item
|
# that works for both sync and async clients. httpx drives this
|
||||||
|
# as a *plain* generator (the async side resolves the yielded
|
||||||
|
# request via its own await machinery), so do NOT mark this
|
||||||
|
# ``async def`` — that triggers
|
||||||
|
# ``object NoneType can't be used in 'await' expression``.
|
||||||
|
self._inject(request)
|
||||||
|
yield request
|
||||||
|
|
||||||
# ── Opt-in server names discovered from raw YAML ──────────────────────────────
|
# ── Opt-in server names discovered from raw YAML ──────────────────────────────
|
||||||
# Fast-agent's ``Settings(**merged)`` pipeline silently discards unknown keys
|
# Fast-agent's ``Settings(**merged)`` pipeline silently discards unknown keys
|
||||||
|
|||||||
Reference in New Issue
Block a user