mirror of https://github.com/encode/starlette.git
Deprecate `Starlette` and `Router` decorators (#1897)
This commit is contained in:
parent
b77a41db62
commit
fd1e91a8de
|
@ -1,4 +1,5 @@
|
||||||
import typing
|
import typing
|
||||||
|
import warnings
|
||||||
|
|
||||||
from starlette.datastructures import State, URLPath
|
from starlette.datastructures import State, URLPath
|
||||||
from starlette.middleware import Middleware
|
from starlette.middleware import Middleware
|
||||||
|
@ -123,43 +124,17 @@ class Starlette:
|
||||||
scope["app"] = self
|
scope["app"] = self
|
||||||
await self.middleware_stack(scope, receive, send)
|
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
|
def on_event(self, event_type: str) -> typing.Callable: # pragma: nocover
|
||||||
return self.router.on_event(event_type)
|
return self.router.on_event(event_type)
|
||||||
|
|
||||||
def mount(
|
def mount(
|
||||||
self, path: str, app: ASGIApp, name: typing.Optional[str] = None
|
self, path: str, app: ASGIApp, name: typing.Optional[str] = None
|
||||||
) -> None: # pragma: nocover
|
) -> 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)
|
self.router.mount(path, app=app, name=name)
|
||||||
|
|
||||||
def host(
|
def host(
|
||||||
self, host: str, app: ASGIApp, name: typing.Optional[str] = None
|
self, host: str, app: ASGIApp, name: typing.Optional[str] = None
|
||||||
) -> None: # pragma: no cover
|
) -> 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)
|
self.router.host(host, app=app, name=name)
|
||||||
|
|
||||||
def add_middleware(
|
def add_middleware(
|
||||||
|
@ -200,7 +175,13 @@ class Starlette:
|
||||||
|
|
||||||
def exception_handler(
|
def exception_handler(
|
||||||
self, exc_class_or_status_code: typing.Union[int, typing.Type[Exception]]
|
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.add_exception_handler(exc_class_or_status_code, func)
|
self.add_exception_handler(exc_class_or_status_code, func)
|
||||||
return func
|
return func
|
||||||
|
@ -213,18 +194,19 @@ class Starlette:
|
||||||
methods: typing.Optional[typing.List[str]] = None,
|
methods: typing.Optional[typing.List[str]] = None,
|
||||||
name: typing.Optional[str] = None,
|
name: typing.Optional[str] = None,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
) -> typing.Callable: # pragma: nocover
|
) -> typing.Callable:
|
||||||
"""
|
"""
|
||||||
We no longer document this decorator style API, and its usage is discouraged.
|
We no longer document this decorator style API, and its usage is discouraged.
|
||||||
Instead you should use the following approach:
|
Instead you should use the following approach:
|
||||||
|
|
||||||
routes = [
|
>>> routes = [Route(path, endpoint=...), ...]
|
||||||
Route(path, endpoint=..., ...),
|
>>> app = Starlette(routes=routes)
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.router.add_route(
|
self.router.add_route(
|
||||||
|
@ -240,18 +222,19 @@ class Starlette:
|
||||||
|
|
||||||
def websocket_route(
|
def websocket_route(
|
||||||
self, path: str, name: typing.Optional[str] = None
|
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.
|
We no longer document this decorator style API, and its usage is discouraged.
|
||||||
Instead you should use the following approach:
|
Instead you should use the following approach:
|
||||||
|
|
||||||
routes = [
|
>>> routes = [WebSocketRoute(path, endpoint=...), ...]
|
||||||
WebSocketRoute(path, endpoint=..., ...),
|
>>> app = Starlette(routes=routes)
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.router.add_websocket_route(path, func, name=name)
|
self.router.add_websocket_route(path, func, name=name)
|
||||||
|
@ -259,19 +242,19 @@ class Starlette:
|
||||||
|
|
||||||
return decorator
|
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.
|
We no longer document this decorator style API, and its usage is discouraged.
|
||||||
Instead you should use the following approach:
|
Instead you should use the following approach:
|
||||||
|
|
||||||
middleware = [
|
>>> middleware = [Middleware(...), ...]
|
||||||
Middleware(...),
|
>>> app = Starlette(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 (
|
assert (
|
||||||
middleware_type == "http"
|
middleware_type == "http"
|
||||||
), 'Currently only middleware("http") is supported.'
|
), 'Currently only middleware("http") is supported.'
|
||||||
|
|
|
@ -737,41 +737,15 @@ class Router:
|
||||||
def __eq__(self, other: typing.Any) -> bool:
|
def __eq__(self, other: typing.Any) -> bool:
|
||||||
return isinstance(other, Router) and self.routes == other.routes
|
return isinstance(other, Router) and self.routes == other.routes
|
||||||
|
|
||||||
# The following usages are now discouraged in favour of configuration
|
|
||||||
# during Router.__init__(...)
|
|
||||||
def mount(
|
def mount(
|
||||||
self, path: str, app: ASGIApp, name: typing.Optional[str] = None
|
self, path: str, app: ASGIApp, name: typing.Optional[str] = None
|
||||||
) -> None: # pragma: nocover
|
) -> 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)
|
route = Mount(path, app=app, name=name)
|
||||||
self.routes.append(route)
|
self.routes.append(route)
|
||||||
|
|
||||||
def host(
|
def host(
|
||||||
self, host: str, app: ASGIApp, name: typing.Optional[str] = None
|
self, host: str, app: ASGIApp, name: typing.Optional[str] = None
|
||||||
) -> None: # pragma: no cover
|
) -> 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)
|
route = Host(host, app=app, name=name)
|
||||||
self.routes.append(route)
|
self.routes.append(route)
|
||||||
|
|
||||||
|
@ -804,18 +778,19 @@ class Router:
|
||||||
methods: typing.Optional[typing.List[str]] = None,
|
methods: typing.Optional[typing.List[str]] = None,
|
||||||
name: typing.Optional[str] = None,
|
name: typing.Optional[str] = None,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
) -> typing.Callable: # pragma: nocover
|
) -> typing.Callable:
|
||||||
"""
|
"""
|
||||||
We no longer document this decorator style API, and its usage is discouraged.
|
We no longer document this decorator style API, and its usage is discouraged.
|
||||||
Instead you should use the following approach:
|
Instead you should use the following approach:
|
||||||
|
|
||||||
routes = [
|
>>> routes = [Route(path, endpoint=...), ...]
|
||||||
Route(path, endpoint=..., ...),
|
>>> app = Starlette(routes=routes)
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.add_route(
|
self.add_route(
|
||||||
|
@ -831,18 +806,19 @@ class Router:
|
||||||
|
|
||||||
def websocket_route(
|
def websocket_route(
|
||||||
self, path: str, name: typing.Optional[str] = None
|
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.
|
We no longer document this decorator style API, and its usage is discouraged.
|
||||||
Instead you should use the following approach:
|
Instead you should use the following approach:
|
||||||
|
|
||||||
routes = [
|
>>> routes = [WebSocketRoute(path, endpoint=...), ...]
|
||||||
WebSocketRoute(path, endpoint=..., ...),
|
>>> app = Starlette(routes=routes)
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.add_websocket_route(path, func, name=name)
|
self.add_websocket_route(path, func, name=name)
|
||||||
|
@ -860,7 +836,13 @@ class Router:
|
||||||
else:
|
else:
|
||||||
self.on_shutdown.append(func)
|
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:
|
def decorator(func: typing.Callable) -> typing.Callable:
|
||||||
self.add_event_handler(event_type, func)
|
self.add_event_handler(event_type, func)
|
||||||
return func
|
return func
|
||||||
|
|
|
@ -429,3 +429,60 @@ def test_app_sync_gen_lifespan(test_client_factory):
|
||||||
assert not cleanup_complete
|
assert not cleanup_complete
|
||||||
assert startup_complete
|
assert startup_complete
|
||||||
assert cleanup_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
|
||||||
|
|
|
@ -1020,3 +1020,20 @@ def test_host_named_repr() -> None:
|
||||||
)
|
)
|
||||||
# test for substring because repr(Router) returns unique object ID
|
# test for substring because repr(Router) returns unique object ID
|
||||||
assert repr(route).startswith("Host(host='example.com', name='app', app=")
|
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)
|
||||||
|
|
Loading…
Reference in New Issue