Browse Source

🎨 Auto format

pull/15210/head
pre-commit-ci-lite[bot] 4 months ago
committed by GitHub
parent
commit
813742492e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 8
      docs/en/docs/advanced/routing-architecture.md
  2. 7
      fastapi/routing.py
  3. 25
      fastapi/routing_handlers.py
  4. 99
      fastapi/routing_router.py
  5. 38
      fastapi/routing_routes.py
  6. 15
      fastapi/routing_utils.py
  7. 1
      tests/test_routing_facade_contract.py

8
docs/en/docs/advanced/routing-architecture.md

@ -1,15 +1,15 @@
# Routing Architecture
# Routing Architecture { #routing-architecture }
FastAPI keeps public routing imports stable at `fastapi.routing`, but the
implementation is split into focused internal modules for maintainability.
## Public facade
## Public facade { #public-facade }
- `fastapi.routing`
- compatibility facade and stable imports
- re-exports core symbols such as `APIRouter`, `APIRoute`, and helper hooks
## Internal modules
## Internal modules { #internal-modules }
- `fastapi.routing_router`
- `APIRouter` implementation
@ -20,7 +20,7 @@ implementation is split into focused internal modules for maintainability.
- `fastapi.routing_utils`
- low-level shared utilities (response serialization, lifespan helpers, wrappers)
## Stability contract
## Stability contract { #stability-contract }
- Application code should import from `fastapi.routing` (or top-level `fastapi`).
- Internal modules are implementation details and may evolve.

7
fastapi/routing.py

@ -9,8 +9,6 @@ split across internal modules:
- `fastapi.routing_utils`
"""
from starlette.routing import Mount as Mount
from fastapi.routing_handlers import (
RouteHandlerConfig as RouteHandlerConfig,
)
@ -21,7 +19,9 @@ from fastapi.routing_routes import APIRoute as APIRoute
from fastapi.routing_routes import APIWebSocketRoute as APIWebSocketRoute
from fastapi.routing_utils import _DefaultLifespan as _DefaultLifespan
from fastapi.routing_utils import _merge_lifespan_context as _merge_lifespan_context
from fastapi.routing_utils import _wrap_gen_lifespan_context as _wrap_gen_lifespan_context
from fastapi.routing_utils import (
_wrap_gen_lifespan_context as _wrap_gen_lifespan_context,
)
from fastapi.routing_utils import build_response_args as _build_response_args
from fastapi.routing_utils import extract_endpoint_context as _extract_endpoint_context
from fastapi.routing_utils import request_response as request_response
@ -33,6 +33,7 @@ from fastapi.sse import KEEPALIVE_COMMENT as KEEPALIVE_COMMENT
from fastapi.sse import EventSourceResponse as EventSourceResponse
from fastapi.sse import ServerSentEvent as ServerSentEvent
from fastapi.sse import format_sse_event as format_sse_event
from starlette.routing import Mount as Mount
__all__ = [
"Mount",

25
fastapi/routing_handlers.py

@ -223,9 +223,12 @@ def get_request_handler(
errors = solved_result.errors
assert dependant.call
if not errors:
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(
@ -266,7 +269,9 @@ def get_request_handler(
retry=item.retry,
comment=item.comment,
)
return format_sse_event(data_str=_serialize_data(item).decode("utf-8"))
return format_sse_event(
data_str=_serialize_data(item).decode("utf-8")
)
if dependant.is_async_gen_callable:
sse_aiter: AsyncIterator[Any] = gen.__aiter__()
@ -274,19 +279,21 @@ def get_request_handler(
sse_aiter = iterate_in_threadpool(gen)
@asynccontextmanager
async def _sse_producer_cm() -> AsyncIterator[ObjectReceiveStream[bytes]]:
send_stream, receive_stream = anyio.create_memory_object_stream[bytes](
max_buffer_size=1
)
async def _sse_producer_cm() -> AsyncIterator[
ObjectReceiveStream[bytes]
]:
send_stream, receive_stream = anyio.create_memory_object_stream[
bytes
](max_buffer_size=1)
async def _producer() -> None:
async with send_stream:
async for raw_item in sse_aiter:
await send_stream.send(_serialize_sse_item(raw_item))
send_keepalive, receive_keepalive = anyio.create_memory_object_stream[
bytes
](max_buffer_size=1)
send_keepalive, receive_keepalive = (
anyio.create_memory_object_stream[bytes](max_buffer_size=1)
)
async def _keepalive_inserter() -> None:
async with send_keepalive, receive_stream:

99
fastapi/routing_router.py

@ -1,122 +1,53 @@
import contextlib
import email.message
import functools
import inspect
import json
import types
from collections.abc import (
AsyncIterator,
Awaitable,
Callable,
Collection,
Coroutine,
Generator,
Iterator,
Mapping,
Sequence,
)
from contextlib import (
AbstractAsyncContextManager,
AbstractContextManager,
AsyncExitStack,
asynccontextmanager,
)
from enum import Enum, IntEnum
from enum import Enum
from typing import (
Annotated,
Any,
TypeVar,
cast,
)
import anyio
from annotated_doc import Doc
from anyio.abc import ObjectReceiveStream
from fastapi import params
from fastapi._compat import (
ModelField,
Undefined,
lenient_issubclass,
)
from fastapi.datastructures import Default, DefaultPlaceholder
from fastapi.dependencies.models import Dependant
from fastapi.dependencies.utils import (
_should_embed_body_fields,
get_body_field,
get_dependant,
get_flat_dependant,
get_parameterless_sub_dependant,
get_stream_item_type,
get_typed_return_annotation,
solve_dependencies,
)
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import (
EndpointContext,
FastAPIError,
RequestValidationError,
ResponseValidationError,
WebSocketRequestValidationError,
)
from fastapi.sse import (
_PING_INTERVAL,
KEEPALIVE_COMMENT,
EventSourceResponse,
ServerSentEvent,
format_sse_event,
from fastapi.routing_handlers import (
get_request_handler as get_request_handler,
)
from fastapi.routing_handlers import (
get_websocket_app as get_websocket_app,
)
from fastapi.routing_routes import APIRoute as APIRoute
from fastapi.routing_routes import APIWebSocketRoute as APIWebSocketRoute
from fastapi.routing_utils import (
_DefaultLifespan,
_merge_lifespan_context,
_wrap_gen_lifespan_context,
)
from fastapi.types import DecoratedCallable, IncEx
from fastapi.utils import (
create_model_field,
generate_unique_id,
get_value_or_default,
is_body_allowed_for_status_code,
)
from starlette import routing
from starlette._exception_handler import wrap_app_handling_exceptions
from starlette._utils import is_async_callable
from starlette.concurrency import iterate_in_threadpool, run_in_threadpool
from starlette.datastructures import FormData
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse, Response, StreamingResponse
from starlette.responses import JSONResponse, Response
from starlette.routing import (
BaseRoute,
Match,
compile_path,
get_name,
)
from starlette.routing import Mount as Mount # noqa
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
from starlette.websockets import WebSocket
from starlette.types import ASGIApp, Lifespan
from typing_extensions import deprecated
from fastapi.routing_utils import (
_DefaultLifespan,
_merge_lifespan_context,
_wrap_gen_lifespan_context,
build_response_args as _build_response_args,
extract_endpoint_context as _extract_endpoint_context,
request_response,
run_endpoint_function,
serialize_response,
websocket_session,
)
from fastapi.routing_handlers import (
get_request_handler as get_request_handler,
)
from fastapi.routing_handlers import (
get_websocket_app as get_websocket_app,
)
from fastapi.routing_routes import APIRoute as APIRoute
from fastapi.routing_routes import APIWebSocketRoute as APIWebSocketRoute
class APIRouter(routing.Router):
"""
`APIRouter` class, used to group *path operations*, for example to structure

38
fastapi/routing_routes.py

@ -23,7 +23,11 @@ from fastapi.routing_handlers import (
from fastapi.routing_utils import request_response, websocket_session
from fastapi.sse import EventSourceResponse, ServerSentEvent
from fastapi.types import IncEx
from fastapi.utils import create_model_field, generate_unique_id, is_body_allowed_for_status_code
from fastapi.utils import (
create_model_field,
generate_unique_id,
is_body_allowed_for_status_code,
)
from starlette import routing
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
@ -233,22 +237,22 @@ class APIRoute(routing.Route):
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
return get_request_handler(
RouteHandlerConfig(
dependant=self.dependant,
body_field=self.body_field,
status_code=self.status_code,
response_class=self.response_class,
response_field=self.response_field,
response_model_include=self.response_model_include,
response_model_exclude=self.response_model_exclude,
response_model_by_alias=self.response_model_by_alias,
response_model_exclude_unset=self.response_model_exclude_unset,
response_model_exclude_defaults=self.response_model_exclude_defaults,
response_model_exclude_none=self.response_model_exclude_none,
dependency_overrides_provider=self.dependency_overrides_provider,
embed_body_fields=self._embed_body_fields,
strict_content_type=self.strict_content_type,
stream_item_field=self.stream_item_field,
is_json_stream=self.is_json_stream,
dependant=self.dependant,
body_field=self.body_field,
status_code=self.status_code,
response_class=self.response_class,
response_field=self.response_field,
response_model_include=self.response_model_include,
response_model_exclude=self.response_model_exclude,
response_model_by_alias=self.response_model_by_alias,
response_model_exclude_unset=self.response_model_exclude_unset,
response_model_exclude_defaults=self.response_model_exclude_defaults,
response_model_exclude_none=self.response_model_exclude_none,
dependency_overrides_provider=self.dependency_overrides_provider,
embed_body_fields=self._embed_body_fields,
strict_content_type=self.strict_content_type,
stream_item_field=self.stream_item_field,
is_json_stream=self.is_json_stream,
)
)

15
fastapi/routing_utils.py

@ -6,7 +6,6 @@ from collections.abc import (
AsyncIterator,
Awaitable,
Callable,
Coroutine,
Generator,
Mapping,
)
@ -32,7 +31,7 @@ from starlette._utils import is_async_callable
from starlette.concurrency import run_in_threadpool
from starlette.requests import Request
from starlette.responses import Response
from starlette.types import ASGIApp, AppType, Lifespan, Receive, Scope, Send
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
from starlette.websockets import WebSocket
_T = TypeVar("_T")
@ -42,9 +41,7 @@ def request_response(
func: Callable[[Request], Awaitable[Response] | Response],
) -> ASGIApp:
f: Callable[[Request], Awaitable[Response]] = (
func
if is_async_callable(func)
else functools.partial(run_in_threadpool, func) # type: ignore[call-arg]
func if is_async_callable(func) else functools.partial(run_in_threadpool, func) # type: ignore[call-arg]
)
async def app(scope: Scope, receive: Receive, send: Send) -> None:
@ -221,9 +218,13 @@ async def run_endpoint_function(
return await run_in_threadpool(dependant.call, **values)
def build_response_args(*, status_code: int | None, solved_result: Any) -> dict[str, Any]:
def build_response_args(
*, status_code: int | None, solved_result: Any
) -> dict[str, Any]:
response_args: dict[str, Any] = {"background": solved_result.background_tasks}
current_status_code = status_code if status_code else solved_result.response.status_code
current_status_code = (
status_code if status_code else solved_result.response.status_code
)
if current_status_code is not None:
response_args["status_code"] = current_status_code
if solved_result.response.status_code:

1
tests/test_routing_facade_contract.py

@ -1,5 +1,4 @@
import fastapi.routing as routing
from fastapi.routing_handlers import RouteHandlerConfig
from fastapi.routing_router import APIRouter
from fastapi.routing_routes import APIRoute, APIWebSocketRoute

Loading…
Cancel
Save