Add types
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Paul Brinkmeier 2023-01-20 04:27:47 +01:00
parent 9986fb5faa
commit 1c1ca14fe7
2 changed files with 19 additions and 9 deletions

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
from quart import Quart, Response, websocket from quart import Quart, Response, websocket
from typing import NoReturn
from glebby.connection_manager import ConnectionManager from glebby.connection_manager import ConnectionManager
from glebby.model import Model from glebby.model import Model
@ -20,5 +21,5 @@ async def root() -> Response:
# is set up in the connection manager. It will handle sending and receiving # is set up in the connection manager. It will handle sending and receiving
# and keeping the model up to date. # and keeping the model up to date.
@app.websocket('/glebby') @app.websocket('/glebby')
async def ws() -> None: async def ws() -> NoReturn:
await connection_manager.setup_connection() await connection_manager.setup_connection()

View File

@ -3,17 +3,17 @@ import itertools
from collections import OrderedDict from collections import OrderedDict
from quart import websocket from quart import websocket
from typing import AsyncGenerator, Iterator, Optional from typing import Any, AsyncGenerator, Awaitable, Callable, Iterator, NoReturn, Optional
# Represents a single websocket connection # Represents a single websocket connection
class Connection: class Connection:
outgoing_queue: asyncio.Queue[str] outgoing_queue: asyncio.Queue[str]
def __init__(self): def __init__(self) -> None:
self.outgoing_queue = asyncio.Queue() self.outgoing_queue = asyncio.Queue()
# Sending a message simply puts it in the outgoing queue... # Sending a message simply puts it in the outgoing queue...
async def send(self, data: str): async def send(self, data: str) -> None:
await self.outgoing_queue.put(data) await self.outgoing_queue.put(data)
# ... and this infinite loop takes care of actually sending the outgoing # ... and this infinite loop takes care of actually sending the outgoing
@ -28,7 +28,11 @@ class ConnectionManager:
id_generator: Iterator[int] id_generator: Iterator[int]
connections: OrderedDict[int, Connection] connections: OrderedDict[int, Connection]
def __init__(self): on_open: Optional[Callable[[int], Awaitable[Any]]]
on_message: Optional[Callable[[int, str], Awaitable[Any]]]
on_close: Optional[Callable[[int], Awaitable[Any]]]
def __init__(self) -> None:
self.id_generator = itertools.count(start=0, step=1) self.id_generator = itertools.count(start=0, step=1)
self.connections = OrderedDict() self.connections = OrderedDict()
@ -38,17 +42,19 @@ class ConnectionManager:
# Wires up asyncio tasks etc. such that on_open, on_message and on_close # Wires up asyncio tasks etc. such that on_open, on_message and on_close
# are called at the right time with the right arguments. # are called at the right time with the right arguments.
async def setup_connection(self): async def setup_connection(self) -> NoReturn:
connection_id = next(self.id_generator) connection_id = next(self.id_generator)
connection = Connection() connection = Connection()
self.connections[connection_id] = connection self.connections[connection_id] = connection
if self.on_open:
await self.on_open(connection_id) await self.on_open(connection_id)
try: try:
send_task = asyncio.create_task(connection._send_loop()) send_task = asyncio.create_task(connection._send_loop())
while True: while True:
data = await websocket.receive() data = await websocket.receive()
if self.on_message:
await self.on_message(connection_id, data) await self.on_message(connection_id, data)
except asyncio.CancelledError: except asyncio.CancelledError:
@ -56,8 +62,11 @@ class ConnectionManager:
send_task.cancel() send_task.cancel()
await send_task await send_task
if self.on_close:
await self.on_close(connection_id) await self.on_close(connection_id)
raise
# Interface for the model to send messages. The model shouldn't # Interface for the model to send messages. The model shouldn't
# use the Connection class at all. # use the Connection class at all.
async def send_to(self, connection_id: int, data: str) -> None: async def send_to(self, connection_id: int, data: str) -> None: