diff --git a/requirements.txt b/requirements.txt index faeec926..b15a3572 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ aiofiles httptools ujson; sys_platform != "win32" and implementation_name == "cpython" uvloop; sys_platform != "win32" and implementation_name == "cpython" -websockets +websockets>=4.0,<5.0 diff --git a/sanic/app.py b/sanic/app.py index 06540088..ca50edc1 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -303,7 +303,8 @@ class Sanic: await fut except (CancelledError, ConnectionClosed): pass - self.websocket_tasks.remove(fut) + finally: + self.websocket_tasks.remove(fut) await ws.close() self.router.add(uri=uri, handler=websocket_handler, diff --git a/sanic/request.py b/sanic/request.py index 22d74a64..7ce7620d 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -78,6 +78,11 @@ class Request(dict): self.method, self.path) + def __bool__(self): + if self.transport: + return True + return False + @property def json(self): if self.parsed_json is None: diff --git a/sanic/router.py b/sanic/router.py index 8b3fd3dc..2864bf84 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -1,4 +1,5 @@ import re +import uuid from collections import defaultdict, namedtuple from collections.abc import Iterable from functools import lru_cache @@ -18,6 +19,8 @@ REGEX_TYPES = { 'number': (float, r'[0-9\\.]+'), 'alpha': (str, r'[A-Za-z]+'), 'path': (str, r'[^/].*?'), + 'uuid': (uuid.UUID, r'[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-' + r'[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}') } ROUTER_CACHE_SIZE = 1024 diff --git a/setup.py b/setup.py index 3f7812a2..5954b926 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ requirements = [ uvloop, ujson, 'aiofiles>=0.3.0', - 'websockets>=4.0', + 'websockets>=4.0,<5.0', ] if strtobool(os.environ.get("SANIC_NO_UJSON", "no")): print("Installing without uJSON") diff --git a/tests/test_routes.py b/tests/test_routes.py index ed35ea48..146db97c 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -422,6 +422,28 @@ def test_dynamic_route_regex(): assert response.status == 200 +def test_dynamic_route_uuid(): + import uuid + app = Sanic('test_dynamic_route_uuid') + + results = [] + + @app.route('/quirky/') + async def handler(request, unique_id): + results.append(unique_id) + return text('OK') + + request, response = app.test_client.get('/quirky/123e4567-e89b-12d3-a456-426655440000') + assert response.text == 'OK' + assert type(results[0]) is uuid.UUID + + request, response = app.test_client.get('/quirky/{}'.format(uuid.uuid4())) + assert response.status == 200 + + request, response = app.test_client.get('/quirky/non-existing') + assert response.status == 404 + + def test_dynamic_route_path(): app = Sanic('test_dynamic_route_path') diff --git a/tox.ini b/tox.ini index be984b4e..a62368e2 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ deps = pytest-cov pytest-sanic pytest-sugar - aiohttp>=2.3 + aiohttp>=2.3,<=3.2.1 chardet<=2.3.0 beautifulsoup4 gunicorn