From 37f61dae8dd84d0a7cd3ae63a2e1e1b20a5a4cfd Mon Sep 17 00:00:00 2001 From: Amin Alaee Date: Thu, 10 Feb 2022 11:37:04 +0100 Subject: [PATCH] Remove routing decorators in `test_application.py` (#1489) * Remove routing decorators in test_application.py * Remove routing decorators in test_application.py * Remove deprecated API usage in test_application.py * Update starlette/applications.py * revert debug property setter change * Remove routing decorators in test_application.py * Remove deprecated API usage in test_application.py * Update starlette/applications.py * revert debug property setter change * add pragma nocover Co-authored-by: Tom Christie --- starlette/applications.py | 38 +++++++++++-- starlette/routing.py | 32 ++++++++++- tests/test_applications.py | 113 ++++++++++++++++++++++--------------- 3 files changed, 131 insertions(+), 52 deletions(-) diff --git a/starlette/applications.py b/starlette/applications.py index 1bbe5cae..1353e556 100644 --- a/starlette/applications.py +++ b/starlette/applications.py @@ -124,9 +124,35 @@ class Starlette: return self.router.on_event(event_type) def mount(self, path: str, app: ASGIApp, name: str = None) -> None: + """ + 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: str = None) -> None: + def host( + self, host: str, app: ASGIApp, name: 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(self, middleware_class: type, **options: typing.Any) -> None: @@ -137,11 +163,13 @@ class Starlette: self, exc_class_or_status_code: typing.Union[int, typing.Type[Exception]], handler: typing.Callable, - ) -> None: + ) -> None: # pragma: no cover self.exception_handlers[exc_class_or_status_code] = handler self.middleware_stack = self.build_middleware_stack() - def add_event_handler(self, event_type: str, func: typing.Callable) -> None: + def add_event_handler( + self, event_type: str, func: typing.Callable + ) -> None: # pragma: no cover self.router.add_event_handler(event_type, func) def add_route( @@ -151,14 +179,14 @@ class Starlette: methods: typing.List[str] = None, name: str = None, include_in_schema: bool = True, - ) -> None: + ) -> None: # pragma: no cover self.router.add_route( path, route, methods=methods, name=name, include_in_schema=include_in_schema ) def add_websocket_route( self, path: str, route: typing.Callable, name: str = None - ) -> None: + ) -> None: # pragma: no cover self.router.add_websocket_route(path, route, name=name) def exception_handler( diff --git a/starlette/routing.py b/starlette/routing.py index 6077a43d..647a75c5 100644 --- a/starlette/routing.py +++ b/starlette/routing.py @@ -696,10 +696,36 @@ class Router: # The following usages are now discouraged in favour of configuration #  during Router.__init__(...) def mount(self, path: str, app: ASGIApp, name: str = None) -> None: + """ + 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: str = None) -> None: + def host( + self, host: str, app: ASGIApp, name: 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) @@ -778,7 +804,9 @@ class Router: return decorator - def add_event_handler(self, event_type: str, func: typing.Callable) -> None: + def add_event_handler( + self, event_type: str, func: typing.Callable + ) -> None: # pragma: no cover assert event_type in ("startup", "shutdown") if event_type == "startup": diff --git a/tests/test_applications.py b/tests/test_applications.py index f5f4c7fb..62ddd760 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -6,6 +6,7 @@ import pytest from starlette.applications import Starlette from starlette.endpoints import HTTPEndpoint from starlette.exceptions import HTTPException +from starlette.middleware import Middleware from starlette.middleware.trustedhost import TrustedHostMiddleware from starlette.responses import JSONResponse, PlainTextResponse from starlette.routing import Host, Mount, Route, Router, WebSocketRoute @@ -16,83 +17,93 @@ if sys.version_info >= (3, 7): else: from contextlib2 import asynccontextmanager # pragma: no cover -app = Starlette() - -app.add_middleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.example.org"]) - - -@app.exception_handler(500) async def error_500(request, exc): return JSONResponse({"detail": "Server Error"}, status_code=500) -@app.exception_handler(405) async def method_not_allowed(request, exc): return JSONResponse({"detail": "Custom message"}, status_code=405) -@app.exception_handler(HTTPException) async def http_exception(request, exc): return JSONResponse({"detail": exc.detail}, status_code=exc.status_code) -@app.route("/func") def func_homepage(request): return PlainTextResponse("Hello, world!") -@app.route("/async") async def async_homepage(request): return PlainTextResponse("Hello, world!") -@app.route("/class") class Homepage(HTTPEndpoint): def get(self, request): return PlainTextResponse("Hello, world!") -users = Router() - - -@users.route("/") def all_users_page(request): return PlainTextResponse("Hello, everyone!") -@users.route("/{username}") def user_page(request): username = request.path_params["username"] return PlainTextResponse(f"Hello, {username}!") -app.mount("/users", users) - - -subdomain = Router() - - -@subdomain.route("/") def custom_subdomain(request): return PlainTextResponse("Subdomain: " + request.path_params["subdomain"]) -app.host("{subdomain}.example.org", subdomain) - - -@app.route("/500") def runtime_error(request): raise RuntimeError() -@app.websocket_route("/ws") async def websocket_endpoint(session): await session.accept() await session.send_text("Hello, world!") await session.close() +users = Router( + routes=[ + Route("/", endpoint=all_users_page), + Route("/{username}", endpoint=user_page), + ] +) + +subdomain = Router( + routes=[ + Route("/", custom_subdomain), + ] +) + +exception_handlers = { + 500: error_500, + 405: method_not_allowed, + HTTPException: http_exception, +} + +middleware = [ + Middleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.example.org"]) +] + +app = Starlette( + routes=[ + Route("/func", endpoint=func_homepage), + Route("/async", endpoint=async_homepage), + Route("/class", endpoint=Homepage), + Route("/500", endpoint=runtime_error), + WebSocketRoute("/ws", endpoint=websocket_endpoint), + Mount("/users", app=users), + Host("{subdomain}.example.org", app=subdomain), + ], + exception_handlers=exception_handlers, + middleware=middleware, +) + + @pytest.fixture def client(test_client_factory): with test_client_factory(app) as client: @@ -186,6 +197,8 @@ def test_routes(): Route("/func", endpoint=func_homepage, methods=["GET"]), Route("/async", endpoint=async_homepage, methods=["GET"]), Route("/class", endpoint=Homepage), + Route("/500", endpoint=runtime_error, methods=["GET"]), + WebSocketRoute("/ws", endpoint=websocket_endpoint), Mount( "/users", app=Router( @@ -199,8 +212,6 @@ def test_routes(): "{subdomain}.example.org", app=Router(routes=[Route("/", endpoint=custom_subdomain)]), ), - Route("/500", endpoint=runtime_error, methods=["GET"]), - WebSocketRoute("/ws", endpoint=websocket_endpoint), ] @@ -209,8 +220,11 @@ def test_app_mount(tmpdir, test_client_factory): with open(path, "w") as file: file.write("") - app = Starlette() - app.mount("/static", StaticFiles(directory=tmpdir)) + app = Starlette( + routes=[ + Mount("/static", StaticFiles(directory=tmpdir)), + ] + ) client = test_client_factory(app) @@ -224,13 +238,16 @@ def test_app_mount(tmpdir, test_client_factory): def test_app_debug(test_client_factory): - app = Starlette() - app.debug = True - - @app.route("/") async def homepage(request): raise RuntimeError() + app = Starlette( + routes=[ + Route("/", homepage), + ], + ) + app.debug = True + client = test_client_factory(app, raise_server_exceptions=False) response = client.get("/") assert response.status_code == 500 @@ -239,12 +256,15 @@ def test_app_debug(test_client_factory): def test_app_add_route(test_client_factory): - app = Starlette() - async def homepage(request): return PlainTextResponse("Hello, World!") - app.add_route("/", homepage) + app = Starlette( + routes=[ + Route("/", endpoint=homepage), + ] + ) + client = test_client_factory(app) response = client.get("/") assert response.status_code == 200 @@ -252,14 +272,16 @@ def test_app_add_route(test_client_factory): def test_app_add_websocket_route(test_client_factory): - app = Starlette() - async def websocket_endpoint(session): await session.accept() await session.send_text("Hello, world!") await session.close() - app.add_websocket_route("/ws", websocket_endpoint) + app = Starlette( + routes=[ + WebSocketRoute("/ws", endpoint=websocket_endpoint), + ] + ) client = test_client_factory(app) with client.websocket_connect("/ws") as session: @@ -270,7 +292,6 @@ def test_app_add_websocket_route(test_client_factory): def test_app_add_event_handler(test_client_factory): startup_complete = False cleanup_complete = False - app = Starlette() def run_startup(): nonlocal startup_complete @@ -280,8 +301,10 @@ def test_app_add_event_handler(test_client_factory): nonlocal cleanup_complete cleanup_complete = True - app.add_event_handler("startup", run_startup) - app.add_event_handler("shutdown", run_cleanup) + app = Starlette( + on_startup=[run_startup], + on_shutdown=[run_cleanup], + ) assert not startup_complete assert not cleanup_complete -- 2.47.3