diff --git a/src/mock_vws/_requests_mock_server/mock_web_query_api.py b/src/mock_vws/_requests_mock_server/mock_web_query_api.py index f22cd9c69..545ecfe03 100644 --- a/src/mock_vws/_requests_mock_server/mock_web_query_api.py +++ b/src/mock_vws/_requests_mock_server/mock_web_query_api.py @@ -7,6 +7,7 @@ import email.utils from collections.abc import Callable, Iterable, Mapping from http import HTTPMethod, HTTPStatus +from typing import ParamSpec, Protocol, runtime_checkable from beartype import beartype from requests.models import PreparedRequest @@ -25,13 +26,29 @@ _ROUTES: set[Route] = set() _ResponseType = tuple[int, Mapping[str, str], str] +_P = ParamSpec("_P") + + +@runtime_checkable +class _RouteMethod(Protocol[_P]): + """ + Callable used for routing which also exposes ``__name__``. + """ + + __name__: str + + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _ResponseType: + """ + Return a mock response. + """ + ... # pylint: disable=unnecessary-ellipsis @beartype def route( path_pattern: str, http_methods: Iterable[str], -) -> Callable[[Callable[..., _ResponseType]], Callable[..., _ResponseType]]: +) -> Callable[[_RouteMethod[_P]], _RouteMethod[_P]]: """Register a decorated method so that it can be recognized as a route. Args: @@ -44,8 +61,8 @@ def route( """ def decorator( - method: Callable[..., _ResponseType], - ) -> Callable[..., _ResponseType]: + method: _RouteMethod[_P], + ) -> _RouteMethod[_P]: """Register a decorated method so that it can be recognized as a route. Returns: diff --git a/src/mock_vws/_requests_mock_server/mock_web_services_api.py b/src/mock_vws/_requests_mock_server/mock_web_services_api.py index 9a6b30a8d..13570ed9b 100644 --- a/src/mock_vws/_requests_mock_server/mock_web_services_api.py +++ b/src/mock_vws/_requests_mock_server/mock_web_services_api.py @@ -12,7 +12,7 @@ import uuid from collections.abc import Callable, Iterable, Mapping from http import HTTPMethod, HTTPStatus -from typing import Any +from typing import Any, ParamSpec, Protocol, runtime_checkable from zoneinfo import ZoneInfo from beartype import BeartypeConf, beartype @@ -39,13 +39,29 @@ _ROUTES: set[Route] = set() _ResponseType = tuple[int, Mapping[str, str], str] +_P = ParamSpec("_P") + + +@runtime_checkable +class _RouteMethod(Protocol[_P]): + """ + Callable used for routing which also exposes ``__name__``. + """ + + __name__: str + + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _ResponseType: + """ + Return a mock response. + """ + ... # pylint: disable=unnecessary-ellipsis @beartype def route( path_pattern: str, http_methods: Iterable[HTTPMethod], -) -> Callable[[Callable[..., _ResponseType]], Callable[..., _ResponseType]]: +) -> Callable[[_RouteMethod[_P]], _RouteMethod[_P]]: """Register a decorated method so that it can be recognized as a route. Args: @@ -59,8 +75,8 @@ def route( @beartype def decorator( - method: Callable[..., _ResponseType], - ) -> Callable[..., _ResponseType]: + method: _RouteMethod[_P], + ) -> _RouteMethod[_P]: """Register a decorated method so that it can be recognized as a route. Returns: