From c29b630f22884704676edf9bb223b78413a7447f Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 7 Nov 2017 20:38:08 +0300 Subject: [PATCH] Await in websockets (#2475) * Work on * Fix tests * Update docs --- CHANGES/2475.removal | 2 + aiohttp/client_ws.py | 24 ++++---- aiohttp/http_writer.py | 15 ++--- aiohttp/payload_streamer.py | 4 +- aiohttp/web_ws.py | 26 ++++----- docs/client_reference.rst | 30 +++++++++- docs/web_reference.rst | 81 +++++++++++++++----------- tests/test_client_request.py | 5 +- tests/test_client_ws.py | 11 ++-- tests/test_client_ws_functional.py | 14 ++--- tests/test_http_writer.py | 5 +- tests/test_test_utils.py | 4 +- tests/test_web_websocket.py | 40 ++++++------- tests/test_web_websocket_functional.py | 46 +++------------ 14 files changed, 163 insertions(+), 144 deletions(-) create mode 100644 CHANGES/2475.removal diff --git a/CHANGES/2475.removal b/CHANGES/2475.removal new file mode 100644 index 00000000000..cb0d29212e6 --- /dev/null +++ b/CHANGES/2475.removal @@ -0,0 +1,2 @@ +`send_str()`, `send_bytes()`, `send_json()`, `ping()` and `pong()` are +genuine async functions now. diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index b7a449dfa60..6f4c0d9c42e 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -61,7 +61,7 @@ def _reset_heartbeat(self): def _send_heartbeat(self): if self._heartbeat is not None and not self._closed: - self.ping() + self._writer.ping() if self._pong_response_cb is not None: self._pong_response_cb.cancel() @@ -106,25 +106,25 @@ def get_extra_info(self, name, default=None): def exception(self): return self._exception - def ping(self, message='b'): - self._writer.ping(message) + async def ping(self, message='b'): + await self._writer.ping(message) - def pong(self, message='b'): - self._writer.pong(message) + async def pong(self, message='b'): + await self._writer.pong(message) - def send_str(self, data): + async def send_str(self, data): if not isinstance(data, str): raise TypeError('data argument must be str (%r)' % type(data)) - return self._writer.send(data, binary=False) + await self._writer.send(data, binary=False) - def send_bytes(self, data): + async def send_bytes(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError('data argument must be byte-ish (%r)' % type(data)) - return self._writer.send(data, binary=True) + await self._writer.send(data, binary=True) - def send_json(self, data, *, dumps=json.dumps): - return self.send_str(dumps(data)) + async def send_json(self, data, *, dumps=json.dumps): + await self.send_str(dumps(data)) async def close(self, *, code=1000, message=b''): # we need to break `receive()` cycle first, @@ -223,7 +223,7 @@ async def receive(self, timeout=None): elif msg.type == WSMsgType.CLOSING: self._closing = True elif msg.type == WSMsgType.PING and self._autoping: - self.pong(msg.data) + await self.pong(msg.data) continue elif msg.type == WSMsgType.PONG and self._autoping: continue diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 9905eae32a5..c9b0d06f650 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -1,6 +1,5 @@ """Http related parsers and protocol.""" -import asyncio import collections import socket import zlib @@ -112,17 +111,16 @@ def set_tcp_cork(self, value): except OSError: pass - @asyncio.coroutine - def drain(self): + async def drain(self): """Flush the write buffer. The intended use is to write w.write(data) - yield from w.drain() + await w.drain() """ if self._protocol.transport is not None: - yield from self._protocol._drain_helper() + await self._protocol._drain_helper() class PayloadWriter(AbstractPayloadWriter): @@ -281,17 +279,16 @@ async def write_eof(self, chunk=b''): self._transport = None self._stream.release() - @asyncio.coroutine - def drain(self, last=False): + async def drain(self, last=False): if self._transport is not None: if self._buffer: self._transport.write(b''.join(self._buffer)) if not last: self._buffer.clear() - yield from self._stream.drain() + await self._stream.drain() else: # wait for transport if self._drain_waiter is None: self._drain_waiter = self.loop.create_future() - yield from self._drain_waiter + await self._drain_waiter diff --git a/aiohttp/payload_streamer.py b/aiohttp/payload_streamer.py index 936e1a98538..3b45888aeaa 100644 --- a/aiohttp/payload_streamer.py +++ b/aiohttp/payload_streamer.py @@ -3,11 +3,11 @@ As a simple case, you can upload data from file:: @aiohttp.streamer - def file_sender(writer, file_name=None): + async def file_sender(writer, file_name=None): with open(file_name, 'rb') as f: chunk = f.read(2**16) while chunk: - yield from writer.write(chunk) + await writer.write(chunk) chunk = f.read(2**16) diff --git a/aiohttp/web_ws.py b/aiohttp/web_ws.py index d3f92fc8a88..25a36017bb1 100644 --- a/aiohttp/web_ws.py +++ b/aiohttp/web_ws.py @@ -76,7 +76,7 @@ def _reset_heartbeat(self): def _send_heartbeat(self): if self._heartbeat is not None and not self._closed: - self.ping() + self._writer.ping() if self._pong_response_cb is not None: self._pong_response_cb.cancel() @@ -166,34 +166,34 @@ def compress(self): def exception(self): return self._exception - def ping(self, message='b'): + async def ping(self, message='b'): if self._writer is None: raise RuntimeError('Call .prepare() first') - self._writer.ping(message) + await self._writer.ping(message) - def pong(self, message='b'): + async def pong(self, message='b'): # unsolicited pong if self._writer is None: raise RuntimeError('Call .prepare() first') - self._writer.pong(message) + await self._writer.pong(message) - def send_str(self, data): + async def send_str(self, data): if self._writer is None: raise RuntimeError('Call .prepare() first') if not isinstance(data, str): raise TypeError('data argument must be str (%r)' % type(data)) - return self._writer.send(data, binary=False) + await self._writer.send(data, binary=False) - def send_bytes(self, data): + async def send_bytes(self, data): if self._writer is None: raise RuntimeError('Call .prepare() first') if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError('data argument must be byte-ish (%r)' % type(data)) - return self._writer.send(data, binary=True) + await self._writer.send(data, binary=True) - def send_json(self, data, *, dumps=json.dumps): - return self.send_str(dumps(data)) + async def send_json(self, data, *, dumps=json.dumps): + await self.send_str(dumps(data)) async def write_eof(self): if self._eof_sent: @@ -303,7 +303,7 @@ async def receive(self, timeout=None): elif msg.type == WSMsgType.CLOSING: self._closing = True elif msg.type == WSMsgType.PING and self._autoping: - self.pong(msg.data) + await self.pong(msg.data) continue elif msg.type == WSMsgType.PONG and self._autoping: continue @@ -330,7 +330,7 @@ async def receive_json(self, *, loads=json.loads, timeout=None): data = await self.receive_str(timeout=timeout) return loads(data) - def write(self, data): + async def write(self, data): raise RuntimeError("Cannot call .write() for websocket") def __aiter__(self): diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 151fa8db51b..b719e77de52 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1256,7 +1256,7 @@ manually. Returns exception if any occurs or returns None. - .. method:: ping(message=b'') + .. comethod:: ping(message=b'') Send :const:`~aiohttp.WSMsgType.PING` to peer. @@ -1264,6 +1264,22 @@ manually. :class:`str` (converted to *UTF-8* encoded bytes) or :class:`bytes`. + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: pong(message=b'') + + Send :const:`~aiohttp.WSMsgType.PONG` to peer. + + :param message: optional payload of *pong* message, + :class:`str` (converted to *UTF-8* encoded bytes) + or :class:`bytes`. + + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + .. comethod:: send_str(data) Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message. @@ -1272,6 +1288,10 @@ manually. :raise TypeError: if data is not :class:`str` + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + .. comethod:: send_bytes(data) Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message. @@ -1281,6 +1301,10 @@ manually. :raise TypeError: if data is not :class:`bytes`, :class:`bytearray` or :class:`memoryview`. + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + .. comethod:: send_json(data, *, dumps=json.dumps) Send *data* to peer as JSON string. @@ -1298,6 +1322,10 @@ manually. :raise TypeError: if value returned by ``dumps(data)`` is not :class:`str` + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + .. comethod:: close(*, code=1000, message=b'') A :ref:`coroutine` that initiates closing handshake by sending diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 36dd03e9355..ad57ea4558d 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -885,11 +885,10 @@ WebSocketResponse communicate with websocket client by :meth:`send_str`, :meth:`receive` and others. - .. versionadded:: 1.3.0 - To enable back-pressure from slow websocket clients treat methods - `ping()`, `pong()`, `send_str()`, `send_bytes()`, `send_json()` as - coroutines. By default write buffer size is set to 64k. + :meth:`ping()`, :meth:`pong()`, :meth:`send_str()`, + :meth:`send_bytes()`, :meth:`send_json()` as coroutines. By + default write buffer size is set to 64k. :param bool autoping: Automatically send :const:`~aiohttp.WSMsgType.PONG` on @@ -902,8 +901,6 @@ WebSocketResponse requests, you need to do this explicitly using :meth:`ping` method. - .. versionadded:: 1.3.0 - :param float heartbeat: Send `ping` message every `heartbeat` seconds and wait `pong` response, close connection if `pong` response is not @@ -916,16 +913,14 @@ WebSocketResponse :param float compress: Enable per-message deflate extension support. False for disabled, default value is True. - .. versionadded:: 0.19 - - The class supports ``async for`` statement for iterating over - incoming messages:: + The class supports ``async for`` statement for iterating over + incoming messages:: - ws = web.WebSocketResponse() - await ws.prepare(request) + ws = web.WebSocketResponse() + await ws.prepare(request) - async for msg in ws: - print(msg.data) + async for msg in ws: + print(msg.data) .. coroutinemethod:: prepare(request) @@ -938,8 +933,6 @@ WebSocketResponse :raises HTTPException: if websocket handshake has failed. - .. versionadded:: 0.18 - .. method:: can_prepare(request) Performs checks for *request* data to figure out if websocket @@ -985,7 +978,7 @@ WebSocketResponse Returns last occurred exception or None. - .. method:: ping(message=b'') + .. comethod:: ping(message=b'') Send :const:`~aiohttp.WSMsgType.PING` to peer. @@ -995,7 +988,11 @@ WebSocketResponse :raise RuntimeError: if connections is not started or closing. - .. method:: pong(message=b'') + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: pong(message=b'') Send *unsolicited* :const:`~aiohttp.WSMsgType.PONG` to peer. @@ -1005,7 +1002,11 @@ WebSocketResponse :raise RuntimeError: if connections is not started or closing. - .. coroutinemethod:: send_str(data) + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: send_str(data) Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message. @@ -1015,7 +1016,11 @@ WebSocketResponse :raise TypeError: if data is not :class:`str` - .. coroutinemethod:: send_bytes(data) + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: send_bytes(data) Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message. @@ -1026,7 +1031,11 @@ WebSocketResponse :raise TypeError: if data is not :class:`bytes`, :class:`bytearray` or :class:`memoryview`. - .. coroutinemethod:: send_json(data, *, dumps=json.dumps) + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: send_json(data, *, dumps=json.dumps) Send *data* to peer as JSON string. @@ -1042,7 +1051,11 @@ WebSocketResponse :raise TypeError: if value returned by ``dumps`` param is not :class:`str` - .. coroutinemethod:: close(*, code=1000, message=b'') + .. versionchanged:: 3.0 + + The method is converted into :term:`coroutine` + + .. comethod:: close(*, code=1000, message=b'') A :ref:`coroutine` that initiates closing handshake by sending :const:`~aiohttp.WSMsgType.CLOSE` message. @@ -1057,7 +1070,7 @@ WebSocketResponse :raise RuntimeError: if connection is not started - .. coroutinemethod:: receive(timeout=None) + .. comethod:: receive(timeout=None) A :ref:`coroutine` that waits upcoming *data* message from peer and returns it. @@ -1075,13 +1088,14 @@ WebSocketResponse Can only be called by the request handling task. :param timeout: timeout for `receive` operation. - timeout value overrides response`s receive_timeout attribute. + + timeout value overrides response`s receive_timeout attribute. :return: :class:`~aiohttp.WSMessage` :raise RuntimeError: if connection is not started - .. coroutinemethod:: receive_str(*, timeout=None) + .. comethod:: receive_str(*, timeout=None) A :ref:`coroutine` that calls :meth:`receive` but also asserts the message type is :const:`~aiohttp.WSMsgType.TEXT`. @@ -1091,13 +1105,14 @@ WebSocketResponse Can only be called by the request handling task. :param timeout: timeout for `receive` operation. - timeout value overrides response`s receive_timeout attribute. + + timeout value overrides response`s receive_timeout attribute. :return str: peer's message content. :raise TypeError: if message is :const:`~aiohttp.WSMsgType.BINARY`. - .. coroutinemethod:: receive_bytes(*, timeout=None) + .. comethod:: receive_bytes(*, timeout=None) A :ref:`coroutine` that calls :meth:`receive` but also asserts the message type is @@ -1108,13 +1123,14 @@ WebSocketResponse Can only be called by the request handling task. :param timeout: timeout for `receive` operation. - timeout value overrides response`s receive_timeout attribute. + + timeout value overrides response`s receive_timeout attribute. :return bytes: peer's message content. :raise TypeError: if message is :const:`~aiohttp.WSMsgType.TEXT`. - .. coroutinemethod:: receive_json(*, loads=json.loads, timeout=None) + .. comethod:: receive_json(*, loads=json.loads, timeout=None) A :ref:`coroutine` that calls :meth:`receive_str` and loads the JSON string to a Python dict. @@ -1128,16 +1144,15 @@ WebSocketResponse with parsed JSON (:func:`json.loads` by default). - :param timeout: timeout for `receive` operation. - timeout value overrides response`s receive_timeout attribute. + :param timeout: timeout for `receive` operation. + + timeout value overrides response`s receive_timeout attribute. :return dict: loaded JSON content :raise TypeError: if message is :const:`~aiohttp.WSMsgType.BINARY`. :raise ValueError: if message is not valid JSON. - .. versionadded:: 0.22 - .. seealso:: :ref:`WebSockets handling` diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 0574d9badda..3b13084fb08 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -59,7 +59,7 @@ def conn(stream): @pytest.fixture -def stream(buf, transport): +def stream(buf, transport, loop): stream = mock.Mock() stream.transport = transport @@ -67,7 +67,8 @@ def acquire(writer): writer.set_transport(transport) stream.acquire.side_effect = acquire - stream.drain.return_value = () + stream.drain.return_value = loop.create_future() + stream.drain.return_value.set_result(None) return stream diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py index d5b0d6c9c13..bc11529b9e4 100644 --- a/tests/test_client_ws.py +++ b/tests/test_client_ws.py @@ -353,7 +353,7 @@ async def test_send_data_after_close(ws_key, key_data, loop, mocker): (resp.send_str, ('s',)), (resp.send_bytes, (b'b',)), (resp.send_json, ({},))): - meth(*args) + await meth(*args) assert ws_logger.warning.called ws_logger.warning.reset_mock() @@ -377,9 +377,12 @@ async def test_send_data_type_errors(ws_key, key_data, loop): resp = await aiohttp.ClientSession(loop=loop).ws_connect( 'http://test.org') - pytest.raises(TypeError, resp.send_str, b's') - pytest.raises(TypeError, resp.send_bytes, 'b') - pytest.raises(TypeError, resp.send_json, set()) + with pytest.raises(TypeError): + await resp.send_str(b's') + with pytest.raises(TypeError): + await resp.send_bytes('b') + with pytest.raises(TypeError): + await resp.send_json(set()) async def test_reader_read_exception(ws_key, key_data, loop): diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py index a05b940e75b..ca395b9efe7 100644 --- a/tests/test_client_ws_functional.py +++ b/tests/test_client_ws_functional.py @@ -127,7 +127,7 @@ async def handler(request): client = await test_client(app) resp = await client.ws_connect('/') payload = {'request': 'test'} - resp.send_json(payload) + await resp.send_json(payload) data = await resp.receive_json() assert data['response'] == payload['request'] @@ -143,7 +143,7 @@ async def handler(request): await ws.prepare(request) msg = await ws.receive_bytes() - ws.ping() + await ws.ping() await ws.send_bytes(msg+b'/answer') try: await ws.close() @@ -156,7 +156,7 @@ async def handler(request): client = await test_client(app) resp = await client.ws_connect('/') - resp.ping() + await resp.ping() await resp.send_bytes(b'ask') msg = await resp.receive() @@ -179,7 +179,7 @@ async def handler(request): await ws.prepare(request) msg = await ws.receive_bytes() - ws.ping() + await ws.ping() await ws.send_bytes(msg+b'/answer') try: await ws.close() @@ -192,7 +192,7 @@ async def handler(request): client = await test_client(app) resp = await client.ws_connect('/', autoping=False) - resp.ping() + await resp.ping() await resp.send_bytes(b'ask') msg = await resp.receive() @@ -200,7 +200,7 @@ async def handler(request): msg = await resp.receive() assert msg.type == aiohttp.WSMsgType.PING - resp.pong() + await resp.pong() msg = await resp.receive() assert msg.data == b'ask/answer' @@ -770,7 +770,7 @@ async def handler(request): async for msg in resp: messages.append(msg) if b'started' == msg.data: - resp.send_bytes(b'ask') + await resp.send_bytes(b'ask') await resp.close() assert 1 == len(messages) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 61778290379..ac5280fc391 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -25,14 +25,15 @@ def write(chunk): @pytest.fixture -def stream(transport): +def stream(transport, loop): stream = mock.Mock(transport=transport) def acquire(writer): writer.set_transport(transport) stream.acquire = acquire - stream.drain.return_value = () + stream.drain.return_value = loop.create_future() + stream.drain.return_value.set_result(None) return stream diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index cda9b571386..0fd67900f98 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -180,11 +180,11 @@ async def test_get_route(): async def test_client_websocket(loop, test_client): resp = await test_client.ws_connect("/websocket") - resp.send_str("foo") + await resp.send_str("foo") msg = await resp.receive() assert msg.type == aiohttp.WSMsgType.TEXT assert "foo" in msg.data - resp.send_str("close") + await resp.send_str("close") msg = await resp.receive() assert msg.type == aiohttp.WSMsgType.CLOSE diff --git a/tests/test_web_websocket.py b/tests/test_web_websocket.py index 47c072a58a3..2875102ec3b 100644 --- a/tests/test_web_websocket.py +++ b/tests/test_web_websocket.py @@ -59,34 +59,34 @@ def maker(method, path, headers=None, protocols=False): return maker -def test_nonstarted_ping(): +async def test_nonstarted_ping(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.ping() + await ws.ping() -def test_nonstarted_pong(): +async def test_nonstarted_pong(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.pong() + await ws.pong() -def test_nonstarted_send_str(): +async def test_nonstarted_send_str(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.send_str('string') + await ws.send_str('string') -def test_nonstarted_send_bytes(): +async def test_nonstarted_send_bytes(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.send_bytes(b'bytes') + await ws.send_bytes(b'bytes') -def test_nonstarted_send_json(): +async def test_nonstarted_send_json(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.send_json({'type': 'json'}) + await ws.send_json({'type': 'json'}) async def test_nonstarted_close(): @@ -146,7 +146,7 @@ async def test_send_str_nonstring(make_request): ws = WebSocketResponse() await ws.prepare(req) with pytest.raises(TypeError): - ws.send_str(b'bytes') + await ws.send_str(b'bytes') async def test_send_bytes_nonbytes(make_request): @@ -154,7 +154,7 @@ async def test_send_bytes_nonbytes(make_request): ws = WebSocketResponse() await ws.prepare(req) with pytest.raises(TypeError): - ws.send_bytes('string') + await ws.send_bytes('string') async def test_send_json_nonjson(make_request): @@ -162,13 +162,13 @@ async def test_send_json_nonjson(make_request): ws = WebSocketResponse() await ws.prepare(req) with pytest.raises(TypeError): - ws.send_json(set()) + await ws.send_json(set()) -def test_write_non_prepared(): +async def test_write_non_prepared(): ws = WebSocketResponse() with pytest.raises(RuntimeError): - ws.write(b'data') + await ws.write(b'data') def test_websocket_ready(): @@ -248,7 +248,7 @@ async def test_send_str_closed(make_request, mocker): await ws.close() mocker.spy(ws_logger, 'warning') - ws.send_str('string') + await ws.send_str('string') assert ws_logger.warning.called @@ -260,7 +260,7 @@ async def test_send_bytes_closed(make_request, mocker): await ws.close() mocker.spy(ws_logger, 'warning') - ws.send_bytes(b'bytes') + await ws.send_bytes(b'bytes') assert ws_logger.warning.called @@ -272,7 +272,7 @@ async def test_send_json_closed(make_request, mocker): await ws.close() mocker.spy(ws_logger, 'warning') - ws.send_json({'type': 'json'}) + await ws.send_json({'type': 'json'}) assert ws_logger.warning.called @@ -284,7 +284,7 @@ async def test_ping_closed(make_request, mocker): await ws.close() mocker.spy(ws_logger, 'warning') - ws.ping() + await ws.ping() assert ws_logger.warning.called @@ -296,7 +296,7 @@ async def test_pong_closed(make_request, mocker): await ws.close() mocker.spy(ws_logger, 'warning') - ws.pong() + await ws.pong() assert ws_logger.warning.called diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index d605cfdcd26..74b3a1753cc 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -115,34 +115,6 @@ async def handler(request): assert data['test'] == expected_value -async def test_websocket_send_drain(loop, test_client): - - async def handler(request): - ws = web.WebSocketResponse() - await ws.prepare(request) - - ws._writer._limit = 1 - - data = await ws.receive_json() - drain = ws.send_json(data) - assert drain - - await drain - await ws.close() - return ws - - app = web.Application() - app.router.add_route('GET', '/', handler) - client = await test_client(app) - - ws = await client.ws_connect('/') - expected_value = 'value' - await ws.send_json({'test': expected_value}) - - data = await ws.receive_json() - assert data['test'] == expected_value - - async def test_websocket_receive_json(loop, test_client): async def handler(request): @@ -386,7 +358,7 @@ async def handler(request): client = await test_client(app) ws = await client.ws_connect('/', autoclose=False, autoping=False) - ws.ping() + await ws.ping() await ws.send_str('ask') msg = await ws.receive() @@ -403,7 +375,7 @@ async def handler(request): ws = web.WebSocketResponse() await ws.prepare(request) - ws.ping('data') + await ws.ping('data') await ws.receive() closed.set_result(None) return ws @@ -417,7 +389,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == WSMsgType.PING assert msg.data == b'data' - ws.pong() + await ws.pong() await ws.close() await closed @@ -440,11 +412,11 @@ async def handler(request): ws = await client.ws_connect('/', autoping=False) - ws.ping('data') + await ws.ping('data') msg = await ws.receive() assert msg.type == WSMsgType.PONG assert msg.data == b'data' - ws.pong() + await ws.pong() await ws.close() @@ -458,7 +430,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == WSMsgType.PING - ws.pong('data') + await ws.pong('data') msg = await ws.receive() assert msg.type == WSMsgType.CLOSE @@ -473,7 +445,7 @@ async def handler(request): ws = await client.ws_connect('/', autoping=False) - ws.ping('data') + await ws.ping('data') msg = await ws.receive() assert msg.type == WSMsgType.PONG assert msg.data == b'data' @@ -613,7 +585,7 @@ async def handler(request): await ws.send_str('text') await ws.send_bytes(b'bytes') - ws.ping() + await ws.ping() await ws.close() await closed @@ -743,7 +715,7 @@ async def handler(request): items = ['q1', 'q2', 'q3'] for item in items: - resp.send_str(item) + await resp.send_str(item) msg = await resp.receive() assert msg.type == aiohttp.WSMsgType.TEXT assert item + '/answer' == msg.data