diff --git a/fastapi/applications.py b/fastapi/applications.py index 340cabfc29..9ea62577c5 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -1553,7 +1553,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -1872,6 +1872,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP GET operation. @@ -1890,6 +1891,7 @@ class FastAPI(Starlette): """ return self.router.get( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -1912,6 +1914,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def put( @@ -1926,7 +1929,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -2245,6 +2248,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP PUT operation. @@ -2268,6 +2272,7 @@ class FastAPI(Starlette): """ return self.router.put( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -2290,6 +2295,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def post( @@ -2304,7 +2310,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -2623,6 +2629,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP POST operation. @@ -2646,6 +2653,7 @@ class FastAPI(Starlette): """ return self.router.post( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -2668,6 +2676,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def delete( @@ -2682,7 +2691,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -3001,6 +3010,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP DELETE operation. @@ -3019,6 +3029,7 @@ class FastAPI(Starlette): """ return self.router.delete( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3041,6 +3052,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def options( @@ -3055,7 +3067,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -3374,6 +3386,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP OPTIONS operation. @@ -3392,6 +3405,7 @@ class FastAPI(Starlette): """ return self.router.options( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3414,6 +3428,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def head( @@ -3428,7 +3443,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -3747,6 +3762,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP HEAD operation. @@ -3765,6 +3781,7 @@ class FastAPI(Starlette): """ return self.router.head( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3787,6 +3804,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def patch( @@ -3801,7 +3819,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -4120,6 +4138,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP PATCH operation. @@ -4165,6 +4184,8 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + # *args, + **kwargs, ) def trace( @@ -4179,7 +4200,7 @@ class FastAPI(Starlette): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -4498,6 +4519,7 @@ class FastAPI(Starlette): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP TRACE operation. @@ -4516,6 +4538,7 @@ class FastAPI(Starlette): """ return self.router.trace( path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -4538,6 +4561,7 @@ class FastAPI(Starlette): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def websocket_route( diff --git a/fastapi/routing.py b/fastapi/routing.py index fe8d886093..1736f44926 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -560,7 +560,7 @@ class APIRoute(routing.Route): self, path: str, endpoint: Callable[..., Any], - *, + *args: Any, response_model: Any = Default(None), status_code: Optional[int] = None, tags: Optional[list[Union[str, Enum]]] = None, @@ -589,6 +589,7 @@ class APIRoute(routing.Route): generate_unique_id_function: Union[ Callable[["APIRoute"], str], DefaultPlaceholder ] = Default(generate_unique_id), + **kwargs: Any, ) -> None: self.path = path self.endpoint = endpoint @@ -751,7 +752,7 @@ class APIRouter(routing.Router): def __init__( self, - *, + *args: Any, prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", tags: Annotated[ Optional[list[Union[str, Enum]]], @@ -963,6 +964,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> None: # Handle on_startup/on_shutdown locally since Starlette removed support # Ref: https://github.com/Kludex/starlette/pull/3117 @@ -1032,7 +1034,7 @@ class APIRouter(routing.Router): self, path: str, endpoint: Callable[..., Any], - *, + *args: Any, response_model: Any = Default(None), status_code: Optional[int] = None, tags: Optional[list[Union[str, Enum]]] = None, @@ -1061,6 +1063,7 @@ class APIRouter(routing.Router): generate_unique_id_function: Union[ Callable[[APIRoute], str], DefaultPlaceholder ] = Default(generate_unique_id), + **kwargs: Any, ) -> None: route_class = route_class_override or self.route_class responses = responses or {} @@ -1082,7 +1085,8 @@ class APIRouter(routing.Router): ) route = route_class( self.prefix + path, - endpoint=endpoint, + endpoint, + *args, response_model=response_model, status_code=status_code, tags=current_tags, @@ -1107,13 +1111,14 @@ class APIRouter(routing.Router): callbacks=current_callbacks, openapi_extra=openapi_extra, generate_unique_id_function=current_generate_unique_id, + **kwargs, ) self.routes.append(route) def api_route( self, path: str, - *, + *args: Any, response_model: Any = Default(None), status_code: Optional[int] = None, tags: Optional[list[Union[str, Enum]]] = None, @@ -1139,11 +1144,13 @@ class APIRouter(routing.Router): generate_unique_id_function: Callable[[APIRoute], str] = Default( generate_unique_id ), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_api_route( path, func, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -1167,6 +1174,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) return func @@ -1272,7 +1280,7 @@ class APIRouter(routing.Router): def include_router( self, router: Annotated["APIRouter", Doc("The `APIRouter` to include.")], - *, + *args: Any, prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", tags: Annotated[ Optional[list[Union[str, Enum]]], @@ -1380,6 +1388,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> None: """ Include another `APIRouter` in the same current `APIRouter`. @@ -1452,6 +1461,7 @@ class APIRouter(routing.Router): self.add_api_route( prefix + route.path, route.endpoint, + *args, response_model=route.response_model, status_code=route.status_code, tags=current_tags, @@ -1478,6 +1488,7 @@ class APIRouter(routing.Router): callbacks=current_callbacks, openapi_extra=route.openapi_extra, generate_unique_id_function=current_generate_unique_id, + **kwargs, ) elif isinstance(route, routing.Route): methods = list(route.methods or []) @@ -1525,7 +1536,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -1844,6 +1855,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP GET operation. @@ -1864,7 +1876,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -1888,6 +1901,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def put( @@ -1902,7 +1916,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -2221,6 +2235,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP PUT operation. @@ -2246,7 +2261,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -2270,6 +2286,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def post( @@ -2284,7 +2301,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -2603,6 +2620,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP POST operation. @@ -2628,7 +2646,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -2652,6 +2671,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def delete( @@ -2666,7 +2686,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -2985,6 +3005,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP DELETE operation. @@ -3005,7 +3026,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3029,6 +3051,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def options( @@ -3043,7 +3066,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -3362,6 +3385,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP OPTIONS operation. @@ -3382,7 +3406,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3406,6 +3431,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def head( @@ -3420,7 +3446,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -3739,6 +3765,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP HEAD operation. @@ -3764,7 +3791,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -3788,6 +3816,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def patch( @@ -3802,7 +3831,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -4121,6 +4150,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP PATCH operation. @@ -4146,7 +4176,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -4170,6 +4201,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) def trace( @@ -4184,7 +4216,7 @@ class APIRouter(routing.Router): """ ), ], - *, + *args: Any, response_model: Annotated[ Any, Doc( @@ -4503,6 +4535,7 @@ class APIRouter(routing.Router): """ ), ] = Default(generate_unique_id), + **kwargs: Any, ) -> Callable[[DecoratedCallable], DecoratedCallable]: """ Add a *path operation* using an HTTP TRACE operation. @@ -4528,7 +4561,8 @@ class APIRouter(routing.Router): ``` """ return self.api_route( - path=path, + path, + *args, response_model=response_model, status_code=status_code, tags=tags, @@ -4552,6 +4586,7 @@ class APIRouter(routing.Router): callbacks=callbacks, openapi_extra=openapi_extra, generate_unique_id_function=generate_unique_id_function, + **kwargs, ) # TODO: remove this once the lifespan (or alternative) interface is improved diff --git a/tests/test_operations_signatures.py b/tests/test_operations_signatures.py index 1a749651df..832f9a0c95 100644 --- a/tests/test_operations_signatures.py +++ b/tests/test_operations_signatures.py @@ -1,22 +1,25 @@ import inspect +import pytest from fastapi import APIRouter, FastAPI -method_names = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] - -def test_signatures_consistency(): - base_sig = inspect.signature(APIRouter.get) - for method_name in method_names: - router_method = getattr(APIRouter, method_name) - app_method = getattr(FastAPI, method_name) - router_sig = inspect.signature(router_method) - app_sig = inspect.signature(app_method) - param: inspect.Parameter - for key, param in base_sig.parameters.items(): - router_param: inspect.Parameter = router_sig.parameters[key] - app_param: inspect.Parameter = app_sig.parameters[key] - assert param.annotation == router_param.annotation - assert param.annotation == app_param.annotation - assert param.default == router_param.default - assert param.default == app_param.default +@pytest.mark.parametrize( + "method_name", ["get", "put", "post", "delete", "options", "head", "patch", "trace"] +) +@pytest.mark.parametrize( + "sig_param", inspect.signature(APIRouter.get).parameters.items() +) +def test_signatures_consistency(method_name, sig_param): + router_method = getattr(APIRouter, method_name) + app_method = getattr(FastAPI, method_name) + router_sig = inspect.signature(router_method) + app_sig = inspect.signature(app_method) + param: inspect.Parameter + key, param = sig_param + router_param: inspect.Parameter = router_sig.parameters[key] + app_param: inspect.Parameter = app_sig.parameters[key] + assert param.annotation == router_param.annotation + assert param.annotation == app_param.annotation + assert param.default == router_param.default + assert param.default == app_param.default