From 52bb04f69f72814dffcd7e5a8b5ee4855bafc691 Mon Sep 17 00:00:00 2001 From: Ashley Sommer Date: Fri, 10 Jan 2020 13:53:28 +1000 Subject: [PATCH 1/2] Allow route decorators to stack up without causing a function signature inspection crash Fix #1742 --- sanic/app.py | 7 ++++++- tests/test_routes.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/sanic/app.py b/sanic/app.py index 65e8480916..e18843e5a5 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -194,6 +194,10 @@ def route( strict_slashes = self.strict_slashes def response(handler): + if isinstance(handler, (tuple, list)): + routes, handler = handler + else: + routes = [] args = list(signature(handler).parameters.keys()) if not args: @@ -205,7 +209,7 @@ def response(handler): if stream: handler.is_stream = stream - routes = self.router.add( + new_routes = self.router.add( uri=uri, methods=methods, handler=handler, @@ -214,6 +218,7 @@ def response(handler): version=version, name=name, ) + routes.extend(new_routes) return routes, handler return response diff --git a/tests/test_routes.py b/tests/test_routes.py index 3b24389ff0..517dc0fb80 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -551,6 +551,18 @@ async def handler4(request, dynamic): pass +def test_double_stack_route(app): + @app.route("/test/1") + @app.route("/test/2") + async def handler1(request): + return text("OK") + + request, response = app.test_client.get("/test/1") + assert response.status == 200 + request, response = app.test_client.get("/test/2") + assert response.status == 200 + + def test_method_not_allowed(app): @app.route("/test", methods=["GET"]) async def handler(request): From fdaf70bde59858be96f1412e20dd83a0b9fdd975 Mon Sep 17 00:00:00 2001 From: Ashley Sommer Date: Sat, 11 Jan 2020 15:15:27 +1000 Subject: [PATCH 2/2] Apply fix to @websocket routes docorator too Add test for double-stacked websocket decorator remove introduction of new variable in route wrapper, extend routes in-place. Add explanation of why a handler will be a tuple in the case of a double-stacked route decorator --- sanic/app.py | 46 ++++++++++++++++++++++++++++---------------- tests/test_routes.py | 17 ++++++++++++++++ 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/sanic/app.py b/sanic/app.py index e18843e5a5..abdd36fbf6 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -194,7 +194,9 @@ def route( strict_slashes = self.strict_slashes def response(handler): - if isinstance(handler, (tuple, list)): + if isinstance(handler, tuple): + # if a handler fn is already wrapped in a route, the handler + # variable will be a tuple of (existing routes, handler fn) routes, handler = handler else: routes = [] @@ -209,16 +211,17 @@ def response(handler): if stream: handler.is_stream = stream - new_routes = self.router.add( - uri=uri, - methods=methods, - handler=handler, - host=host, - strict_slashes=strict_slashes, - version=version, - name=name, + routes.extend( + self.router.add( + uri=uri, + methods=methods, + handler=handler, + host=host, + strict_slashes=strict_slashes, + version=version, + name=name, + ) ) - routes.extend(new_routes) return routes, handler return response @@ -481,6 +484,13 @@ def websocket( strict_slashes = self.strict_slashes def response(handler): + if isinstance(handler, tuple): + # if a handler fn is already wrapped in a route, the handler + # variable will be a tuple of (existing routes, handler fn) + routes, handler = handler + else: + routes = [] + async def websocket_handler(request, *args, **kwargs): request.app = self if not getattr(handler, "__blueprintname__", False): @@ -521,13 +531,15 @@ async def websocket_handler(request, *args, **kwargs): self.websocket_tasks.remove(fut) await ws.close() - routes = self.router.add( - uri=uri, - handler=websocket_handler, - methods=frozenset({"GET"}), - host=host, - strict_slashes=strict_slashes, - name=name, + routes.extend( + self.router.add( + uri=uri, + handler=websocket_handler, + methods=frozenset({"GET"}), + host=host, + strict_slashes=strict_slashes, + name=name, + ) ) return routes, handler diff --git a/tests/test_routes.py b/tests/test_routes.py index 517dc0fb80..31fa1a56ed 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -563,6 +563,23 @@ async def handler1(request): assert response.status == 200 +@pytest.mark.asyncio +async def test_websocket_route_asgi(app): + ev = asyncio.Event() + + @app.websocket("/test/1") + @app.websocket("/test/2") + async def handler(request, ws): + ev.set() + + request, response = await app.asgi_client.websocket("/test/1") + first_set = ev.is_set() + ev.clear() + request, response = await app.asgi_client.websocket("/test/1") + second_set = ev.is_set() + assert(first_set and second_set) + + def test_method_not_allowed(app): @app.route("/test", methods=["GET"]) async def handler(request):