Deprecate `Starlette` and `Router` decorators (#1897)

This commit is contained in:
Marcelo Trylesinski 2022-12-03 08:54:08 +01:00 committed by GitHub
parent b77a41db62
commit fd1e91a8de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 90 deletions

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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)