fastapi/fastapi
Input arrives as an ASGI scope. Static declaration time happens first: at decoration, `get_dependant()` walks the endpoint signature with `analyze_param()` and sorts every parameter into `path_params`/`query_params`/`header_params`/`cookie_params`/`body_params`/`dependencies` on a `Dependant` dataclass, each field wrapped in a `ModelField` holding a Pydantic `TypeAdapter`. At request time the handler reads and decodes the body (form vs JSON vs raw bytes), then `solve_dependencies()` recursively resolves sub-dependencies, coerces each parameter group against its `ModelField`, and returns a `SolvedDependency` bundle of values, errors, background tasks and a mutable `Response`. Output flows back through `serialize_response()` (or the streaming serializers) and out as a Starlette `Response`. State owners are the router's route list, the memoised `app.openapi_schema`, and the per-request `AsyncExitStack`s in the ASGI scope.
- Body decoding branches on content type: form bodies go through `await request.form()` with `body.close` pushed onto the middleware exit stack; otherwise raw bytes are parsed as JSON only when the Content-Type maintype is `application` and the subtype is `json` or ends in `+json`, else passed through as bytes.
- A `json.JSONDecodeError` during body parsing is converted into a `RequestValidationError` with a synthetic `json_invalid` error at loc `("body", e.pos)`; any other parse exception becomes a 400 `HTTPException` with detail 'There was an error parsing the body'.
- `solve_dependencies()` resolves sub-dependencies depth-first before extracting path/query/header/cookie values, and memoises each result in `dependency_cache` keyed by `_get_cache_key` — a tuple of `(call, sorted oauth scopes, computed scope)` — so the same dependency called twice in one request runs once unless `use_cache=False`.
- Generator dependencies are entered as context managers by `_solve_generator`: async generators via `asynccontextmanager`, sync generators via `contextmanager_in_threadpool`, and pushed onto `fastapi_inner_astack` unless the dependency declares `scope="function"`, in which case `fastapi_function_astack` is used so it tears down before the response is sent.
- `contextmanager_in_threadpool` deliberately gives `__exit__` its own `CapacityLimiter(1)` so a blocking teardown cannot deadlock against the shared AnyIO threadpool used by a connection pool inside the dependency.
- Form bodies flow through `_extract_form_body`, which reads `UploadFile` values into `bytes` when the declared annotation is bytes, expands sequence-of-bytes fields, and passes through any unmatched form keys as single values or lists.
- `request_body_to_args` collapses to a single un-embedded field when `len(body_fields) == 1 and not embed_body_fields`, and in that case a `BaseModel`-annotated form body is exploded into its model fields before validation.
- Response serialization has a fast path: when a `response_field` exists and no custom `response_class` was supplied, `serialize_response(dump_json=True)` calls `ModelField.serialize_json` and the bytes are wrapped in a bare `Response(media_type="application/json")`, skipping the dict → `json.dumps` round trip.
- Headers set on the injected `Response` object by dependencies are merged into the final response via `response.headers.raw.extend(solved_result.response.headers.raw)` on every branch (SSE, JSONL, raw stream, and normal).
- When `is_body_allowed_for_status_code(status_code)` is false (1xx, 204, 205, 304), the serialized body is discarded by setting `response.body = b""`.
- Validation failures never reach the endpoint: `errors` accumulated by `solve_dependencies` short-circuit the handler and raise `RequestValidationError`, which the default handler renders as HTTP 422 with `{"detail": jsonable_encoder(exc.errors())}`.
- The OpenAPI document is produced once and cached on `app.openapi_schema`, invalidated by comparing `self.router._get_routes_version()` — a recursive counter that sums `_routes_version` across all `_IncludedRouter` children — against `_openapi_routes_version`.
- The SSE output path decouples generator iteration from the keepalive timer using two `anyio` memory object streams with `max_buffer_size=1`, a `_producer` task and a `_keepalive_inserter` task in a task group, so `anyio.fail_after` never wraps the user generator's `__anext__` directly.