diff --git a/starlette/applications.py b/starlette/applications.py index c3daade5..a46cbaa0 100644 --- a/starlette/applications.py +++ b/starlette/applications.py @@ -1,4 +1,5 @@ import typing +import warnings from starlette.datastructures import State, URLPath from starlette.middleware import Middleware @@ -123,43 +124,17 @@ class Starlette: scope["app"] = self await self.middleware_stack(scope, receive, send) - # The following usages are now discouraged in favour of configuration - # during Starlette.__init__(...) def on_event(self, event_type: str) -> typing.Callable: # pragma: nocover return self.router.on_event(event_type) def mount( self, path: str, app: ASGIApp, name: typing.Optional[str] = None ) -> None: # pragma: nocover - """ - We no longer document this API, and its usage is discouraged. - Instead you should use the following approach: - - routes = [ - Mount(path, ...), - ... - ] - - app = Starlette(routes=routes) - """ - self.router.mount(path, app=app, name=name) def host( self, host: str, app: ASGIApp, name: typing.Optional[str] = None ) -> None: # pragma: no cover - """ - We no longer document this API, and its usage is discouraged. - Instead you should use the following approach: - - routes = [ - Host(path, ...), - ... - ] - - app = Starlette(routes=routes) - """ - self.router.host(host, app=app, name=name) def add_middleware( @@ -200,7 +175,13 @@ class Starlette: def exception_handler( self, exc_class_or_status_code: typing.Union[int, typing.Type[Exception]] - ) -> typing.Callable: # pragma: nocover + ) -> typing.Callable: + warnings.warn( + "The `exception_handler` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501 + "Refer to https://www.starlette.io/exceptions/ for the recommended approach.", # noqa: E501 + DeprecationWarning, + ) + def decorator(func: typing.Callable) -> typing.Callable: self.add_exception_handler(exc_class_or_status_code, func) return func @@ -213,18 +194,19 @@ class Starlette: methods: typing.Optional[typing.List[str]] = None, name: typing.Optional[str] = None, include_in_schema: bool = True, - ) -> typing.Callable: # pragma: nocover + ) -> typing.Callable: """ We no longer document this decorator style API, and its usage is discouraged. Instead you should use the following approach: - routes = [ - Route(path, endpoint=..., ...), - ... - ] - - app = Starlette(routes=routes) + >>> routes = [Route(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) """ + warnings.warn( + "The `route` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501 + "Refer to https://www.starlette.io/routing/ for the recommended approach.", # noqa: E501 + DeprecationWarning, + ) def decorator(func: typing.Callable) -> typing.Callable: self.router.add_route( @@ -240,18 +222,19 @@ class Starlette: def websocket_route( self, path: str, name: typing.Optional[str] = None - ) -> typing.Callable: # pragma: nocover + ) -> typing.Callable: """ We no longer document this decorator style API, and its usage is discouraged. Instead you should use the following approach: - routes = [ - WebSocketRoute(path, endpoint=..., ...), - ... - ] - - app = Starlette(routes=routes) + >>> routes = [WebSocketRoute(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) """ + warnings.warn( + "The `websocket_route` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501 + "Refer to https://www.starlette.io/routing/#websocket-routing for the recommended approach.", # noqa: E501 + DeprecationWarning, + ) def decorator(func: typing.Callable) -> typing.Callable: self.router.add_websocket_route(path, func, name=name) @@ -259,19 +242,19 @@ class Starlette: return decorator - def middleware(self, middleware_type: str) -> typing.Callable: # pragma: nocover + def middleware(self, middleware_type: str) -> typing.Callable: """ We no longer document this decorator style API, and its usage is discouraged. Instead you should use the following approach: - middleware = [ - Middleware(...), - ... - ] - - app = Starlette(middleware=middleware) + >>> middleware = [Middleware(...), ...] + >>> app = Starlette(middleware=middleware) """ - + warnings.warn( + "The `middleware` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501 + "Refer to https://www.starlette.io/middleware/#using-middleware for recommended approach.", # noqa: E501 + DeprecationWarning, + ) assert ( middleware_type == "http" ), 'Currently only middleware("http") is supported.' diff --git a/starlette/routing.py b/starlette/routing.py index 479d4ae6..0aa90aa2 100644 --- a/starlette/routing.py +++ b/starlette/routing.py @@ -737,41 +737,15 @@ class Router: def __eq__(self, other: typing.Any) -> bool: return isinstance(other, Router) and self.routes == other.routes - # The following usages are now discouraged in favour of configuration - #  during Router.__init__(...) def mount( self, path: str, app: ASGIApp, name: typing.Optional[str] = None ) -> None: # pragma: nocover - """ - We no longer document this API, and its usage is discouraged. - Instead you should use the following approach: - - routes = [ - Mount(path, ...), - ... - ] - - app = Starlette(routes=routes) - """ - route = Mount(path, app=app, name=name) self.routes.append(route) def host( self, host: str, app: ASGIApp, name: typing.Optional[str] = None ) -> None: # pragma: no cover - """ - We no longer document this API, and its usage is discouraged. - Instead you should use the following approach: - - routes = [ - Host(path, ...), - ... - ] - - app = Starlette(routes=routes) - """ - route = Host(host, app=app, name=name) self.routes.append(route) @@ -804,18 +778,19 @@ class Router: methods: typing.Optional[typing.List[str]] = None, name: typing.Optional[str] = None, include_in_schema: bool = True, - ) -> typing.Callable: # pragma: nocover + ) -> typing.Callable: """ We no longer document this decorator style API, and its usage is discouraged. Instead you should use the following approach: - routes = [ - Route(path, endpoint=..., ...), - ... - ] - - app = Starlette(routes=routes) + >>> routes = [Route(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) """ + warnings.warn( + "The `route` decorator is deprecated, and will be removed in version 1.0.0." + "Refer to https://www.starlette.io/routing/#http-routing for the recommended approach.", # noqa: E501 + DeprecationWarning, + ) def decorator(func: typing.Callable) -> typing.Callable: self.add_route( @@ -831,18 +806,19 @@ class Router: def websocket_route( self, path: str, name: typing.Optional[str] = None - ) -> typing.Callable: # pragma: nocover + ) -> typing.Callable: """ We no longer document this decorator style API, and its usage is discouraged. Instead you should use the following approach: - routes = [ - WebSocketRoute(path, endpoint=..., ...), - ... - ] - - app = Starlette(routes=routes) + >>> routes = [WebSocketRoute(path, endpoint=...), ...] + >>> app = Starlette(routes=routes) """ + warnings.warn( + "The `websocket_route` decorator is deprecated, and will be removed in version 1.0.0. Refer to " # noqa: E501 + "https://www.starlette.io/routing/#websocket-routing for the recommended approach.", # noqa: E501 + DeprecationWarning, + ) def decorator(func: typing.Callable) -> typing.Callable: self.add_websocket_route(path, func, name=name) @@ -860,7 +836,13 @@ class Router: else: self.on_shutdown.append(func) - def on_event(self, event_type: str) -> typing.Callable: # pragma: nocover + def on_event(self, event_type: str) -> typing.Callable: + warnings.warn( + "The `on_event` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501 + "Refer to https://www.starlette.io/events/#registering-events for recommended approach.", # noqa: E501 + DeprecationWarning, + ) + def decorator(func: typing.Callable) -> typing.Callable: self.add_event_handler(event_type, func) return func diff --git a/tests/test_applications.py b/tests/test_applications.py index 2cee601b..fcacbe63 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -429,3 +429,60 @@ def test_app_sync_gen_lifespan(test_client_factory): assert not cleanup_complete assert startup_complete assert cleanup_complete + + +def test_decorator_deprecations() -> None: + app = Starlette() + + with pytest.deprecated_call( + match=( + "The `exception_handler` decorator is deprecated, " + "and will be removed in version 1.0.0." + ) + ) as record: + app.exception_handler(500)(http_exception) + assert len(record) == 1 + + with pytest.deprecated_call( + match=( + "The `middleware` decorator is deprecated, " + "and will be removed in version 1.0.0." + ) + ) as record: + + async def middleware(request, call_next): + ... # pragma: no cover + + app.middleware("http")(middleware) + assert len(record) == 1 + + with pytest.deprecated_call( + match=( + "The `route` decorator is deprecated, " + "and will be removed in version 1.0.0." + ) + ) as record: + app.route("/")(async_homepage) + assert len(record) == 1 + + with pytest.deprecated_call( + match=( + "The `websocket_route` decorator is deprecated, " + "and will be removed in version 1.0.0." + ) + ) as record: + app.websocket_route("/ws")(websocket_endpoint) + assert len(record) == 1 + + with pytest.deprecated_call( + match=( + "The `on_event` decorator is deprecated, " + "and will be removed in version 1.0.0." + ) + ) as record: + + async def startup(): + ... # pragma: no cover + + app.on_event("startup")(startup) + assert len(record) == 1 diff --git a/tests/test_routing.py b/tests/test_routing.py index e2c2eb2c..09beb8bb 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1020,3 +1020,20 @@ def test_host_named_repr() -> None: ) # test for substring because repr(Router) returns unique object ID assert repr(route).startswith("Host(host='example.com', name='app', app=") + + +def test_decorator_deprecations() -> None: + router = Router() + + with pytest.deprecated_call(): + router.route("/")(homepage) + + with pytest.deprecated_call(): + router.websocket_route("/ws")(websocket_endpoint) + + with pytest.deprecated_call(): + + async def startup() -> None: + ... # pragma: nocover + + router.on_event("startup")(startup)