class AsyncHTTPExecutor:
"""Generic asynchronous HTTP executor for executing OpenAPI operations."""
def __init__(
self,
base_url: str | None = None,
client: httpx.AsyncClient | None = None,
provider: RequestProvider | None = None,
middleware: Sequence[Middleware] | None = None,
timeout: float = 30.0,
) -> None:
self.base_url = base_url
self._client = client
self.provider = provider if provider is not None else NoAuthProvider()
self.pipeline = MiddlewarePipeline(middleware)
self.timeout = timeout
self._builder = RequestBuilder(base_url=base_url)
self._parser = ResponseParser()
async def execute(
self,
operation: Operation,
arguments: dict[str, Any],
base_url_override: str | None = None,
) -> ResponseData:
built_req = self._builder.build(
operation=operation,
arguments=arguments,
base_url_override=base_url_override,
)
start_time = time.monotonic()
try:
if self._client is not None:
response = await self._send_request(self._client, built_req)
else:
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await self._send_request(client, built_req)
except httpx.TimeoutException as exc:
logger.error(
"Request timed out for %s after %.1fs",
sanitize_request_log(built_req.method, built_req.url),
self.timeout,
)
raise ExecutionTimeoutError(
f"HTTP request to '{built_req.url}' timed out after {self.timeout}s."
) from exc
except (RequestValidationError, ResponseParsingError):
raise
except httpx.HTTPError as exc:
logger.error(
"HTTP execution failed for %s: %s",
sanitize_request_log(built_req.method, built_req.url),
exc,
)
raise HTTPExecutionError(
f"HTTP request execution failed for '{built_req.url}': {exc}"
) from exc
elapsed = time.monotonic() - start_time
logger.info(
"Executed HTTP %s -> Status %d (%.3fs)",
sanitize_request_log(built_req.method, built_req.url),
response.status_code,
elapsed,
)
return self._parser.parse(response)
async def _send_request(
self, client: httpx.AsyncClient, req_data: BuiltRequest
) -> httpx.Response:
kwargs: dict[str, Any] = {
"method": req_data.method,
"url": req_data.url,
"headers": req_data.headers,
"params": req_data.params,
"json": req_data.json_body,
"timeout": self.timeout,
}
request = client.build_request(**kwargs)
if req_data.cookies:
cookie_hdr = "; ".join(f"{k}={v}" for k, v in req_data.cookies.items())
request.headers["Cookie"] = cookie_hdr
request = await self.provider.apply(request)
async def transport_call(req: httpx.Request) -> httpx.Response:
return await client.send(req)
return await self.pipeline.execute(request, transport_call)