Browse Source

🔨 refactoring in fastapi/routing

pull/14232/head
Evgeny Bokshitsky 9 months ago
parent
commit
3cd1ba3c14
  1. 50
      fastapi/routing.py

50
fastapi/routing.py

@ -380,7 +380,6 @@ def get_request_handler(
actual_strict_content_type = strict_content_type
async def app(request: Request) -> Response:
response: Response | None = None
file_stack = request.scope.get("fastapi_middleware_astack")
assert isinstance(file_stack, AsyncExitStack), (
"fastapi_middleware_astack not found in request scope"
@ -449,7 +448,6 @@ def get_request_handler(
raise http_error from e
# Solve dependencies and run path operation function, auto-closing dependencies
errors: list[Any] = []
async_exit_stack = request.scope.get("fastapi_inner_astack")
assert isinstance(async_exit_stack, AsyncExitStack), (
"fastapi_inner_astack not found in request scope"
@ -464,15 +462,17 @@ def get_request_handler(
)
errors = solved_result.errors
assert dependant.call # For types
if not errors:
if errors:
raise RequestValidationError(
errors, body=body, endpoint_ctx=endpoint_ctx
)
# Shared serializer for stream items (JSONL and SSE).
# Validates against stream_item_field when set, then
# serializes to JSON bytes.
def _serialize_data(data: Any) -> bytes:
if stream_item_field:
value, errors_ = stream_item_field.validate(
data, {}, loc=("response",)
)
value, errors_ = stream_item_field.validate(data, {}, loc=("response",))
if errors_:
ctx = endpoint_ctx or EndpointContext()
raise ResponseValidationError(
@ -489,7 +489,7 @@ def get_request_handler(
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
)
else:
data = jsonable_encoder(data)
return json.dumps(data).encode("utf-8")
@ -519,9 +519,7 @@ def get_request_handler(
retry=item.retry,
comment=item.comment,
)
else:
# Plain object: validate + serialize via
# stream_item_field (if set) and wrap in data field
return format_sse_event(
data_str=_serialize_data(item).decode("utf-8")
)
@ -583,15 +581,9 @@ def get_request_handler(
yield receive_keepalive
tg.cancel_scope.cancel()
# Enter the SSE context manager on the request-scoped
# exit stack. The stack outlives the streaming response,
# so __aexit__ runs via proper structured teardown, not
# via GeneratorExit thrown into an async generator.
sse_receive_stream = await async_exit_stack.enter_async_context(
_sse_producer_cm()
)
# Ensure the receive stream is closed when the exit stack
# unwinds, preventing ResourceWarning from __del__.
async_exit_stack.push_async_callback(sse_receive_stream.aclose)
async def _sse_with_checkpoints(
@ -607,7 +599,6 @@ def get_request_handler(
sse_stream_content: AsyncIterator[bytes] | Iterator[bytes] = (
_sse_with_checkpoints(sse_receive_stream)
)
response = StreamingResponse(
sse_stream_content,
media_type="text/event-stream",
@ -617,7 +608,9 @@ def get_request_handler(
# For Nginx proxies to not buffer server sent events
response.headers["X-Accel-Buffering"] = "no"
response.headers.raw.extend(solved_result.response.headers.raw)
elif is_json_stream:
return response
if is_json_stream:
# Generator endpoint: stream as JSONL
gen = dependant.call(**solved_result.values)
@ -650,8 +643,10 @@ def get_request_handler(
background=solved_result.background_tasks,
)
response.headers.raw.extend(solved_result.response.headers.raw)
elif dependant.is_async_gen_callable or dependant.is_gen_callable:
# Raw streaming with explicit response_class (e.g. StreamingResponse)
return response
if dependant.is_async_gen_callable or dependant.is_gen_callable:
# Raw streaming with explicit response_class
gen = dependant.call(**solved_result.values)
if dependant.is_async_gen_callable:
@ -670,7 +665,8 @@ def get_request_handler(
)
response = actual_response_class(content=gen, **response_args)
response.headers.raw.extend(solved_result.response.headers.raw)
else:
return response
raw_response = await run_endpoint_function(
dependant=dependant,
values=solved_result.values,
@ -679,8 +675,8 @@ def get_request_handler(
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = solved_result.background_tasks
response = raw_response
else:
return raw_response
response_args = _build_response_args(
status_code=status_code, solved_result=solved_result
)
@ -716,14 +712,6 @@ def get_request_handler(
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(solved_result.response.headers.raw)
if errors:
validation_error = RequestValidationError(
errors, body=body, endpoint_ctx=endpoint_ctx
)
raise validation_error
# Return response
assert response
return response
return app

Loading…
Cancel
Save