Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Starlette and Router decorators #1897

Merged
merged 15 commits into from Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 32 additions & 49 deletions starlette/applications.py
@@ -1,4 +1,5 @@
import typing
import warnings

from starlette.datastructures import State, URLPath
from starlette.middleware import Middleware
Expand Down Expand Up @@ -123,43 +124,17 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
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(
Expand Down Expand Up @@ -200,7 +175,13 @@ def add_websocket_route(

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
Expand All @@ -213,18 +194,19 @@ def route(
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(
Expand All @@ -240,38 +222,39 @@ def decorator(func: typing.Callable) -> typing.Callable:

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)
return func

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.'
Expand Down
64 changes: 23 additions & 41 deletions starlette/routing.py
Expand Up @@ -737,41 +737,15 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
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)

Expand Down Expand Up @@ -804,18 +778,19 @@ def route(
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(
Expand All @@ -831,18 +806,19 @@ def decorator(func: typing.Callable) -> typing.Callable:

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)
Expand All @@ -860,7 +836,13 @@ def add_event_handler(
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
Expand Down
57 changes: 57 additions & 0 deletions tests/test_applications.py
Expand Up @@ -429,3 +429,60 @@ def lifespan(app):
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
17 changes: 17 additions & 0 deletions tests/test_routing.py
Expand Up @@ -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)