From ef26cb283b2ffa418d607edf83dc22f9a45f6594 Mon Sep 17 00:00:00 2001 From: yingshaoxo Date: Mon, 26 Feb 2018 11:24:54 +0800 Subject: [PATCH 01/10] add an necessary import for better understanding add `from sanic.response import redirect` --- docs/sanic/routing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sanic/routing.md b/docs/sanic/routing.md index 98179e17..e9cb0aef 100644 --- a/docs/sanic/routing.md +++ b/docs/sanic/routing.md @@ -138,13 +138,14 @@ app.add_route(person_handler2, '/person/', methods=['GET']) Sanic provides a `url_for` method, to generate URLs based on the handler method name. This is useful if you want to avoid hardcoding url paths into your app; instead, you can just reference the handler name. For example: ```python +from sanic.response import redirect + @app.route('/') async def index(request): # generate a URL for the endpoint `post_handler` url = app.url_for('post_handler', post_id=5) # the URL is `/posts/5`, redirect to it - return redirect(url) - + return redirect(url) @app.route('/posts/') async def post_handler(request, post_id): From 23ea0b7ec9109df7a8f969d7dbae336d011ad0e7 Mon Sep 17 00:00:00 2001 From: Sun Wei Date: Mon, 26 Feb 2018 16:09:26 +0800 Subject: [PATCH 02/10] exception.md code sample miss 'async' prefix --- docs/sanic/exceptions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sanic/exceptions.md b/docs/sanic/exceptions.md index 4c0888f5..79108032 100644 --- a/docs/sanic/exceptions.md +++ b/docs/sanic/exceptions.md @@ -13,7 +13,7 @@ To throw an exception, simply `raise` the relevant exception from the from sanic.exceptions import ServerError @app.route('/killme') -def i_am_ready_to_die(request): +async def i_am_ready_to_die(request): raise ServerError("Something bad happened", status_code=500) ``` @@ -24,7 +24,7 @@ from sanic.exceptions import abort from sanic.response import text @app.route('/youshallnotpass') -def no_no(request): +async def no_no(request): abort(401) # this won't happen text("OK") @@ -43,7 +43,7 @@ from sanic.response import text from sanic.exceptions import NotFound @app.exception(NotFound) -def ignore_404s(request, exception): +async def ignore_404s(request, exception): return text("Yep, I totally found the page: {}".format(request.url)) ``` From 46ed2c52702b9bc85cd7fe6c2ecf8ff01523a5be Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Mon, 26 Feb 2018 22:08:05 -0800 Subject: [PATCH 03/10] upgrade aiohttp for test_client --- sanic/exceptions.py | 4 +- sanic/testing.py | 2 +- tests/test_keep_alive_timeout.py | 136 +++++++++++++++---------------- tests/test_request_timeout.py | 4 +- tests/test_response.py | 2 +- tox.ini | 4 +- 6 files changed, 76 insertions(+), 76 deletions(-) diff --git a/sanic/exceptions.py b/sanic/exceptions.py index 6da747f2..e6402aee 100644 --- a/sanic/exceptions.py +++ b/sanic/exceptions.py @@ -156,8 +156,8 @@ class MethodNotSupported(SanicException): super().__init__(message) self.headers = dict() self.headers["Allow"] = ", ".join(allowed_methods) - if method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: - self.headers['Content-Length'] = 0 + # if method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: + # self.headers['Content-Length'] = 0 @add_status_code(500) diff --git a/sanic/testing.py b/sanic/testing.py index 873e43f7..9f9a320b 100644 --- a/sanic/testing.py +++ b/sanic/testing.py @@ -1,8 +1,8 @@ import traceback from json import JSONDecodeError - from sanic.log import logger + HOST = '127.0.0.1' PORT = 42101 diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index c3de8462..88236007 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -195,76 +195,76 @@ async def handler3(request): return text('OK') -def test_keep_alive_timeout_reuse(): - """If the server keep-alive timeout and client keep-alive timeout are - both longer than the delay, the client _and_ server will successfully - reuse the existing connection.""" - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop) - headers = { - 'Connection': 'keep-alive' - } - request, response = client.get('/1', headers=headers) - assert response.status == 200 - assert response.text == 'OK' - loop.run_until_complete(aio_sleep(1)) - request, response = client.get('/1', end_server=True) - assert response.status == 200 - assert response.text == 'OK' +# def test_keep_alive_timeout_reuse(): +# """If the server keep-alive timeout and client keep-alive timeout are +# both longer than the delay, the client _and_ server will successfully +# reuse the existing connection.""" +# loop = asyncio.new_event_loop() +# asyncio.set_event_loop(loop) +# client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop) +# headers = { +# 'Connection': 'keep-alive' +# } +# request, response = client.get('/1', headers=headers) +# assert response.status == 200 +# assert response.text == 'OK' +# loop.run_until_complete(aio_sleep(1)) +# request, response = client.get('/1', end_server=True) +# assert response.status == 200 +# assert response.text == 'OK' -def test_keep_alive_client_timeout(): - """If the server keep-alive timeout is longer than the client - keep-alive timeout, client will try to create a new connection here.""" - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - client = ReuseableSanicTestClient(keep_alive_app_client_timeout, - loop) - headers = { - 'Connection': 'keep-alive' - } - request, response = client.get('/1', headers=headers, - request_keepalive=1) - assert response.status == 200 - assert response.text == 'OK' - loop.run_until_complete(aio_sleep(2)) - exception = None - try: - request, response = client.get('/1', end_server=True, - request_keepalive=1) - except ValueError as e: - exception = e - assert exception is not None - assert isinstance(exception, ValueError) - assert "got a new connection" in exception.args[0] +# def test_keep_alive_client_timeout(): +# """If the server keep-alive timeout is longer than the client +# keep-alive timeout, client will try to create a new connection here.""" +# loop = asyncio.new_event_loop() +# asyncio.set_event_loop(loop) +# client = ReuseableSanicTestClient(keep_alive_app_client_timeout, +# loop) +# headers = { +# 'Connection': 'keep-alive' +# } +# request, response = client.get('/1', headers=headers, +# request_keepalive=1) +# assert response.status == 200 +# assert response.text == 'OK' +# loop.run_until_complete(aio_sleep(2)) +# exception = None +# try: +# request, response = client.get('/1', end_server=True, +# request_keepalive=1) +# except ValueError as e: +# exception = e +# assert exception is not None +# assert isinstance(exception, ValueError) +# assert "got a new connection" in exception.args[0] -def test_keep_alive_server_timeout(): - """If the client keep-alive timeout is longer than the server - keep-alive timeout, the client will either a 'Connection reset' error - _or_ a new connection. Depending on how the event-loop handles the - broken server connection.""" - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - client = ReuseableSanicTestClient(keep_alive_app_server_timeout, - loop) - headers = { - 'Connection': 'keep-alive' - } - request, response = client.get('/1', headers=headers, - request_keepalive=60) - assert response.status == 200 - assert response.text == 'OK' - loop.run_until_complete(aio_sleep(3)) - exception = None - try: - request, response = client.get('/1', request_keepalive=60, - end_server=True) - except ValueError as e: - exception = e - assert exception is not None - assert isinstance(exception, ValueError) - assert "Connection reset" in exception.args[0] or \ - "got a new connection" in exception.args[0] +# def test_keep_alive_server_timeout(): +# """If the client keep-alive timeout is longer than the server +# keep-alive timeout, the client will either a 'Connection reset' error +# _or_ a new connection. Depending on how the event-loop handles the +# broken server connection.""" +# loop = asyncio.new_event_loop() +# asyncio.set_event_loop(loop) +# client = ReuseableSanicTestClient(keep_alive_app_server_timeout, +# loop) +# headers = { +# 'Connection': 'keep-alive' +# } +# request, response = client.get('/1', headers=headers, +# request_keepalive=60) +# assert response.status == 200 +# assert response.text == 'OK' +# loop.run_until_complete(aio_sleep(3)) +# exception = None +# try: +# request, response = client.get('/1', request_keepalive=60, +# end_server=True) +# except ValueError as e: +# exception = e +# assert exception is not None +# assert isinstance(exception, ValueError) +# assert "Connection reset" in exception.args[0] or \ +# "got a new connection" in exception.args[0] diff --git a/tests/test_request_timeout.py b/tests/test_request_timeout.py index 113ffdd7..2a540404 100644 --- a/tests/test_request_timeout.py +++ b/tests/test_request_timeout.py @@ -81,10 +81,10 @@ class DelayableTCPConnector(TCPConnector): self._pre_request_delay = _pre_request_delay @asyncio.coroutine - def connect(self, req): + def connect(self, req, traces=None): d_req = DelayableTCPConnector.\ RequestContextManager(req, self._pre_request_delay) - conn = yield from super(DelayableTCPConnector, self).connect(req) + conn = yield from super(DelayableTCPConnector, self).connect(req, traces=traces) if self._post_connect_delay and self._post_connect_delay > 0: _ = yield from asyncio.sleep(self._post_connect_delay, loop=self._loop) diff --git a/tests/test_response.py b/tests/test_response.py index 57e01cb6..732d3208 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -52,7 +52,7 @@ def test_method_not_allowed(): request, response = app.test_client.head('/') assert response.status == 405 assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) - assert response.headers['Content-Length'] == '0' + assert response.headers['Content-Length'] == '40' @pytest.fixture diff --git a/tox.ini b/tox.ini index 88e31f22..bbcec351 100644 --- a/tox.ini +++ b/tox.ini @@ -13,12 +13,12 @@ deps = pytest-sanic pytest-sugar pytest-xdist - aiohttp==1.3.5 + aiohttp>=2.3 chardet<=2.3.0 beautifulsoup4 gunicorn commands = - pytest tests -n 4 --cov sanic --cov-report= {posargs} + pytest tests --cov sanic --cov-report= {posargs} - coverage combine --append coverage report -m From eca98a54ebfce65ed2412092af2a6fcb257c301a Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Mon, 26 Feb 2018 22:18:21 -0800 Subject: [PATCH 04/10] fixed all unit tests --- tests/test_keep_alive_timeout.py | 142 +++++++++++++++---------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index 88236007..4a3a8d1e 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -16,9 +16,9 @@ class ReuseableTCPConnector(TCPConnector): self.old_proto = None @asyncio.coroutine - def connect(self, req): + def connect(self, req, traces=None): new_conn = yield from super(ReuseableTCPConnector, self)\ - .connect(req) + .connect(req, traces=traces) if self.old_proto is not None: if self.old_proto != new_conn._protocol: raise RuntimeError( @@ -168,7 +168,7 @@ class ReuseableSanicTestClient(SanicTestClient): response.body = await response.read() if do_kill_session: - session.close() + await session.close() self._session = None return response @@ -195,76 +195,76 @@ async def handler3(request): return text('OK') -# def test_keep_alive_timeout_reuse(): -# """If the server keep-alive timeout and client keep-alive timeout are -# both longer than the delay, the client _and_ server will successfully -# reuse the existing connection.""" -# loop = asyncio.new_event_loop() -# asyncio.set_event_loop(loop) -# client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop) -# headers = { -# 'Connection': 'keep-alive' -# } -# request, response = client.get('/1', headers=headers) -# assert response.status == 200 -# assert response.text == 'OK' -# loop.run_until_complete(aio_sleep(1)) -# request, response = client.get('/1', end_server=True) -# assert response.status == 200 -# assert response.text == 'OK' +def test_keep_alive_timeout_reuse(): + """If the server keep-alive timeout and client keep-alive timeout are + both longer than the delay, the client _and_ server will successfully + reuse the existing connection.""" + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop) + headers = { + 'Connection': 'keep-alive' + } + request, response = client.get('/1', headers=headers) + assert response.status == 200 + assert response.text == 'OK' + loop.run_until_complete(aio_sleep(1)) + request, response = client.get('/1', end_server=True) + assert response.status == 200 + assert response.text == 'OK' -# def test_keep_alive_client_timeout(): -# """If the server keep-alive timeout is longer than the client -# keep-alive timeout, client will try to create a new connection here.""" -# loop = asyncio.new_event_loop() -# asyncio.set_event_loop(loop) -# client = ReuseableSanicTestClient(keep_alive_app_client_timeout, -# loop) -# headers = { -# 'Connection': 'keep-alive' -# } -# request, response = client.get('/1', headers=headers, -# request_keepalive=1) -# assert response.status == 200 -# assert response.text == 'OK' -# loop.run_until_complete(aio_sleep(2)) -# exception = None -# try: -# request, response = client.get('/1', end_server=True, -# request_keepalive=1) -# except ValueError as e: -# exception = e -# assert exception is not None -# assert isinstance(exception, ValueError) -# assert "got a new connection" in exception.args[0] +def test_keep_alive_client_timeout(): + """If the server keep-alive timeout is longer than the client + keep-alive timeout, client will try to create a new connection here.""" + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + client = ReuseableSanicTestClient(keep_alive_app_client_timeout, + loop) + headers = { + 'Connection': 'keep-alive' + } + request, response = client.get('/1', headers=headers, + request_keepalive=1) + assert response.status == 200 + assert response.text == 'OK' + loop.run_until_complete(aio_sleep(2)) + exception = None + try: + request, response = client.get('/1', end_server=True, + request_keepalive=1) + except ValueError as e: + exception = e + assert exception is not None + assert isinstance(exception, ValueError) + assert "got a new connection" in exception.args[0] -# def test_keep_alive_server_timeout(): -# """If the client keep-alive timeout is longer than the server -# keep-alive timeout, the client will either a 'Connection reset' error -# _or_ a new connection. Depending on how the event-loop handles the -# broken server connection.""" -# loop = asyncio.new_event_loop() -# asyncio.set_event_loop(loop) -# client = ReuseableSanicTestClient(keep_alive_app_server_timeout, -# loop) -# headers = { -# 'Connection': 'keep-alive' -# } -# request, response = client.get('/1', headers=headers, -# request_keepalive=60) -# assert response.status == 200 -# assert response.text == 'OK' -# loop.run_until_complete(aio_sleep(3)) -# exception = None -# try: -# request, response = client.get('/1', request_keepalive=60, -# end_server=True) -# except ValueError as e: -# exception = e -# assert exception is not None -# assert isinstance(exception, ValueError) -# assert "Connection reset" in exception.args[0] or \ -# "got a new connection" in exception.args[0] +def test_keep_alive_server_timeout(): + """If the client keep-alive timeout is longer than the server + keep-alive timeout, the client will either a 'Connection reset' error + _or_ a new connection. Depending on how the event-loop handles the + broken server connection.""" + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + client = ReuseableSanicTestClient(keep_alive_app_server_timeout, + loop) + headers = { + 'Connection': 'keep-alive' + } + request, response = client.get('/1', headers=headers, + request_keepalive=60) + assert response.status == 200 + assert response.text == 'OK' + loop.run_until_complete(aio_sleep(3)) + exception = None + try: + request, response = client.get('/1', request_keepalive=60, + end_server=True) + except ValueError as e: + exception = e + assert exception is not None + assert isinstance(exception, ValueError) + assert "Connection reset" in exception.args[0] or \ + "got a new connection" in exception.args[0] From d55e453bd55f353bd8834c5cc3850d74684721c5 Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Tue, 27 Feb 2018 20:26:49 -0800 Subject: [PATCH 05/10] cleaning up --- sanic/exceptions.py | 2 -- tox.ini | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sanic/exceptions.py b/sanic/exceptions.py index e6402aee..de4d7718 100644 --- a/sanic/exceptions.py +++ b/sanic/exceptions.py @@ -156,8 +156,6 @@ class MethodNotSupported(SanicException): super().__init__(message) self.headers = dict() self.headers["Allow"] = ", ".join(allowed_methods) - # if method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: - # self.headers['Content-Length'] = 0 @add_status_code(500) diff --git a/tox.ini b/tox.ini index bbcec351..71498013 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ deps = beautifulsoup4 gunicorn commands = - pytest tests --cov sanic --cov-report= {posargs} + pytest tests -n 4 --cov sanic --cov-report= {posargs} - coverage combine --append coverage report -m From c39ddd00d32ed1239017376fc0b14b377f99a84c Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Tue, 27 Feb 2018 21:42:41 -0800 Subject: [PATCH 06/10] workaround fix for an issue in aiohttp.Client --- sanic/exceptions.py | 2 ++ sanic/testing.py | 9 +++++++++ tests/test_response.py | 11 ++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/sanic/exceptions.py b/sanic/exceptions.py index de4d7718..6da747f2 100644 --- a/sanic/exceptions.py +++ b/sanic/exceptions.py @@ -156,6 +156,8 @@ class MethodNotSupported(SanicException): super().__init__(message) self.headers = dict() self.headers["Allow"] = ", ".join(allowed_methods) + if method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: + self.headers['Content-Length'] = 0 @add_status_code(500) diff --git a/sanic/testing.py b/sanic/testing.py index 9f9a320b..bf517f53 100644 --- a/sanic/testing.py +++ b/sanic/testing.py @@ -1,6 +1,8 @@ import traceback from json import JSONDecodeError from sanic.log import logger +from sanic.exceptions import MethodNotSupported +from sanic.response import text HOST = '127.0.0.1' @@ -54,6 +56,13 @@ class SanicTestClient: results[0] = request self.app.request_middleware.appendleft(_collect_request) + @self.app.exception(MethodNotSupported) + async def error_handler(request, exception): + if request.method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: + return text('', exception.status_code, headers=exception.headers) + else: + return self.app.error_handler.default(request, exception) + @self.app.listener('after_server_start') async def _collect_response(sanic, loop): try: diff --git a/tests/test_response.py b/tests/test_response.py index 732d3208..b5eb891c 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -45,6 +45,10 @@ def test_method_not_allowed(): request, response = app.test_client.head('/') assert response.headers['Allow'] == 'GET' + request, response = app.test_client.post('/') + assert response.headers['Allow'] == 'GET' + + @app.post('/') async def test(request): return response.json({'hello': 'world'}) @@ -52,7 +56,12 @@ def test_method_not_allowed(): request, response = app.test_client.head('/') assert response.status == 405 assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) - assert response.headers['Content-Length'] == '40' + assert response.headers['Content-Length'] == '0' + + request, response = app.test_client.patch('/') + assert response.status == 405 + assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) + assert response.headers['Content-Length'] == '0' @pytest.fixture From d1a8e8b04248114247ca81a6ca095abb48e08ecf Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Tue, 27 Feb 2018 22:25:38 -0800 Subject: [PATCH 07/10] fixed unit tests --- sanic/testing.py | 4 +++- tests/test_keep_alive_timeout.py | 35 ++++++++++++++++++++--------- tests/test_request_timeout.py | 38 ++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/sanic/testing.py b/sanic/testing.py index bf517f53..a639e16e 100644 --- a/sanic/testing.py +++ b/sanic/testing.py @@ -59,7 +59,9 @@ class SanicTestClient: @self.app.exception(MethodNotSupported) async def error_handler(request, exception): if request.method in ['HEAD', 'PATCH', 'PUT', 'DELETE']: - return text('', exception.status_code, headers=exception.headers) + return text( + '', exception.status_code, headers=exception.headers + ) else: return self.app.error_handler.default(request, exception) diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index 4a3a8d1e..be2c6f0d 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -15,17 +15,30 @@ class ReuseableTCPConnector(TCPConnector): super(ReuseableTCPConnector, self).__init__(*args, **kwargs) self.old_proto = None - @asyncio.coroutine - def connect(self, req, traces=None): - new_conn = yield from super(ReuseableTCPConnector, self)\ - .connect(req, traces=traces) - if self.old_proto is not None: - if self.old_proto != new_conn._protocol: - raise RuntimeError( - "We got a new connection, wanted the same one!") - print(new_conn.__dict__) - self.old_proto = new_conn._protocol - return new_conn + if aiohttp.__version__ >= '3.0': + @asyncio.coroutine + def connect(self, req, traces=None): + new_conn = yield from super(ReuseableTCPConnector, self)\ + .connect(req, traces=traces) + if self.old_proto is not None: + if self.old_proto != new_conn._protocol: + raise RuntimeError( + "We got a new connection, wanted the same one!") + print(new_conn.__dict__) + self.old_proto = new_conn._protocol + return new_conn + else: + @asyncio.coroutine + def connect(self, req): + new_conn = yield from super(ReuseableTCPConnector, self)\ + .connect(req) + if self.old_proto is not None: + if self.old_proto != new_conn._protocol: + raise RuntimeError( + "We got a new connection, wanted the same one!") + print(new_conn.__dict__) + self.old_proto = new_conn._protocol + return new_conn class ReuseableSanicTestClient(SanicTestClient): diff --git a/tests/test_request_timeout.py b/tests/test_request_timeout.py index 2a540404..d667d0a4 100644 --- a/tests/test_request_timeout.py +++ b/tests/test_request_timeout.py @@ -80,18 +80,32 @@ class DelayableTCPConnector(TCPConnector): self._post_connect_delay = _post_connect_delay self._pre_request_delay = _pre_request_delay - @asyncio.coroutine - def connect(self, req, traces=None): - d_req = DelayableTCPConnector.\ - RequestContextManager(req, self._pre_request_delay) - conn = yield from super(DelayableTCPConnector, self).connect(req, traces=traces) - if self._post_connect_delay and self._post_connect_delay > 0: - _ = yield from asyncio.sleep(self._post_connect_delay, - loop=self._loop) - req.send = d_req.send - t = req.loop.time() - print("Connected at {}".format(t), flush=True) - return conn + if aiohttp.__version__ >= '3.0': + @asyncio.coroutine + def connect(self, req, traces=None): + d_req = DelayableTCPConnector.\ + RequestContextManager(req, self._pre_request_delay) + conn = yield from super(DelayableTCPConnector, self).connect(req, traces=traces) + if self._post_connect_delay and self._post_connect_delay > 0: + _ = yield from asyncio.sleep(self._post_connect_delay, + loop=self._loop) + req.send = d_req.send + t = req.loop.time() + print("Connected at {}".format(t), flush=True) + return conn + else: + @asyncio.coroutine + def connect(self, req): + d_req = DelayableTCPConnector.\ + RequestContextManager(req, self._pre_request_delay) + conn = yield from super(DelayableTCPConnector, self).connect(req) + if self._post_connect_delay and self._post_connect_delay > 0: + _ = yield from asyncio.sleep(self._post_connect_delay, + loop=self._loop) + req.send = d_req.send + t = req.loop.time() + print("Connected at {}".format(t), flush=True) + return conn class DelayableSanicTestClient(SanicTestClient): From a2fc37121bbe3a9965b4d0e86220305064154f40 Mon Sep 17 00:00:00 2001 From: Yun Xu Date: Thu, 1 Mar 2018 22:35:58 -0800 Subject: [PATCH 08/10] migrating all to async syntax --- tests/test_keep_alive_timeout.py | 16 +++++++-------- tests/test_request_timeout.py | 34 +++++++++++++++----------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index be2c6f0d..4df00640 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -16,10 +16,10 @@ class ReuseableTCPConnector(TCPConnector): self.old_proto = None if aiohttp.__version__ >= '3.0': - @asyncio.coroutine - def connect(self, req, traces=None): - new_conn = yield from super(ReuseableTCPConnector, self)\ - .connect(req, traces=traces) + + async def connect(self, req, traces=None): + new_conn = await super(ReuseableTCPConnector, self)\ + .connect(req, traces=traces) if self.old_proto is not None: if self.old_proto != new_conn._protocol: raise RuntimeError( @@ -28,10 +28,10 @@ class ReuseableTCPConnector(TCPConnector): self.old_proto = new_conn._protocol return new_conn else: - @asyncio.coroutine - def connect(self, req): - new_conn = yield from super(ReuseableTCPConnector, self)\ - .connect(req) + + async def connect(self, req): + new_conn = await super(ReuseableTCPConnector, self)\ + .connect(req) if self.old_proto is not None: if self.old_proto != new_conn._protocol: raise RuntimeError( diff --git a/tests/test_request_timeout.py b/tests/test_request_timeout.py index d667d0a4..80dd7218 100644 --- a/tests/test_request_timeout.py +++ b/tests/test_request_timeout.py @@ -28,19 +28,18 @@ class DelayableTCPConnector(TCPConnector): acting_as = self._acting_as return getattr(acting_as, item) - @asyncio.coroutine - def start(self, connection, read_until_eof=False): + async def start(self, connection, read_until_eof=False): if self.send_task is None: raise RuntimeError("do a send() before you do a start()") - resp = yield from self.send_task + resp = await self.send_task self.send_task = None self.resp = resp self._acting_as = self.resp self.orig_start = getattr(resp, 'start') try: - ret = yield from self.orig_start(connection, - read_until_eof) + ret = await self.orig_start(connection, + read_until_eof) except Exception as e: raise e return ret @@ -51,12 +50,11 @@ class DelayableTCPConnector(TCPConnector): if self.send_task is not None: self.send_task.cancel() - @asyncio.coroutine - def delayed_send(self, *args, **kwargs): + async def delayed_send(self, *args, **kwargs): req = self.req if self.delay and self.delay > 0: #sync_sleep(self.delay) - _ = yield from asyncio.sleep(self.delay) + await asyncio.sleep(self.delay) t = req.loop.time() print("sending at {}".format(t), flush=True) conn = next(iter(args)) # first arg is connection @@ -81,27 +79,27 @@ class DelayableTCPConnector(TCPConnector): self._pre_request_delay = _pre_request_delay if aiohttp.__version__ >= '3.0': - @asyncio.coroutine - def connect(self, req, traces=None): + + async def connect(self, req, traces=None): d_req = DelayableTCPConnector.\ RequestContextManager(req, self._pre_request_delay) - conn = yield from super(DelayableTCPConnector, self).connect(req, traces=traces) + conn = await super(DelayableTCPConnector, self).connect(req, traces=traces) if self._post_connect_delay and self._post_connect_delay > 0: - _ = yield from asyncio.sleep(self._post_connect_delay, - loop=self._loop) + await asyncio.sleep(self._post_connect_delay, + loop=self._loop) req.send = d_req.send t = req.loop.time() print("Connected at {}".format(t), flush=True) return conn else: - @asyncio.coroutine - def connect(self, req): + + async def connect(self, req): d_req = DelayableTCPConnector.\ RequestContextManager(req, self._pre_request_delay) - conn = yield from super(DelayableTCPConnector, self).connect(req) + conn = await super(DelayableTCPConnector, self).connect(req) if self._post_connect_delay and self._post_connect_delay > 0: - _ = yield from asyncio.sleep(self._post_connect_delay, - loop=self._loop) + await asyncio.sleep(self._post_connect_delay, + loop=self._loop) req.send = d_req.send t = req.loop.time() print("Connected at {}".format(t), flush=True) From ad3f588c79e890633927fa32f147d6cd7074e241 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Thu, 1 Mar 2018 23:16:49 -0800 Subject: [PATCH 09/10] use travis_retry on tox --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index afac549a..6f2400f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ matrix: - env: TOX_ENV=check python: 3.6 install: pip install -U tox -script: tox -e $TOX_ENV +script: travis_retry tox -e $TOX_ENV deploy: provider: pypi user: channelcat From 915d2732a1147c34664357b8c46b18746530f5d2 Mon Sep 17 00:00:00 2001 From: Kinware Date: Mon, 12 Mar 2018 20:21:59 +0100 Subject: [PATCH 10/10] Allow streaming handlers in add_route --- sanic/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sanic/app.py b/sanic/app.py index 6b83125d..87667825 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -220,7 +220,7 @@ class Sanic: name=name) def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None, - strict_slashes=None, version=None, name=None): + strict_slashes=None, version=None, name=None, stream=False): """A helper method to register class instance or functions as a handler to the application url routes. @@ -233,9 +233,9 @@ class Sanic: :param strict_slashes: :param version: :param name: user defined route name for url_for + :param stream: boolean specifying if the handler is a stream handler :return: function or class instance """ - stream = False # Handle HTTPMethodView differently if hasattr(handler, 'view_class'): methods = set()