web-dev-qa-db-ja.com

クラスにasyncio WebSocketを実装するにはどうすればよいですか?

asyncioおよびwebsocketsを介して、以下に示す形式でWebSocketに接続したいと思います。どうすればこれを達成できますか?

from websockets import connect


class EchoWebsocket:

    def __init__(self):
        self.websocket = self._connect()

    def _connect(self):
        return connect("wss://echo.websocket.org")

    def send(self, message):
        self.websocket.send(message)

    def receive(self):
        return self.websocket.recv()

echo = EchoWebsocket()
echo.send("Hello!")
print(echo.receive())  # "Hello!"
20
2Cubed

非同期プログラムの書き方は?

  1. asyncで非同期関数を定義する必要があります
  2. awaitで非同期関数を呼び出す必要があります
  3. 非同期プログラムを開始するには イベントループ が必要です

その他はすべて、通常のPythonプログラムと同じです。

import asyncio
from websockets import connect


class EchoWebsocket:
    async def __aenter__(self):
        self._conn = connect("wss://echo.websocket.org")
        self.websocket = await self._conn.__aenter__()        
        return self

    async def __aexit__(self, *args, **kwargs):
        await self._conn.__aexit__(*args, **kwargs)

    async def send(self, message):
        await self.websocket.send(message)

    async def receive(self):
        return await self.websocket.recv()


async def main():
    async with EchoWebsocket() as echo:
        await echo.send("Hello!")
        print(await echo.receive())  # "Hello!"


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

出力:

Hello!

ご覧のとおり、コードは作成したものとほとんど同じです。

唯一の違いは、websockets.connectは非同期コンテキストマネージャとして設計されていることです(__aenter____aexit__を使用します)。接続を解放する必要があり、クラスの初期化中に非同期操作を行うのにも役立ちます(__init__の非同期バージョンがないため)。

同じ方法でクラスを編成することをお勧めします。しかし、なんらかの理由でコンテキストマネージャーを使用したくない場合は、新しい__await__メソッドを使用して非同期の初期化を行い、その他の非同期関数を使用して接続を解放できます。

import sys
import asyncio
from websockets import connect


class EchoWebsocket:
    def __await__(self):
        # see: http://stackoverflow.com/a/33420721/1113207
        return self._async_init().__await__()

    async def _async_init(self):
        self._conn = connect("wss://echo.websocket.org")
        self.websocket = await self._conn.__aenter__()
        return self

    async def close(self):
        await self._conn.__aexit__(*sys.exc_info())

    async def send(self, message):
        await self.websocket.send(message)

    async def receive(self):
        return await self.websocket.recv()


async def main():
    echo = await EchoWebsocket()
    try:
        await echo.send("Hello!")
        print(await echo.receive())  # "Hello!"
    finally:
        await echo.close()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

websocketsの使用例の多くは docs にあります。

24