Skip to content

Commit

Permalink
[FIX] Async servers are now typed not to return (with typing.NoReturn)
Browse files Browse the repository at this point in the history
  • Loading branch information
francis-clairicia committed Sep 26, 2023
1 parent d2cfe64 commit 2a550ad
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/easynetwork/api_async/server/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
]

from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING, Any, Protocol, Self
from typing import TYPE_CHECKING, Any, NoReturn, Protocol, Self

if TYPE_CHECKING:
from types import TracebackType
Expand Down Expand Up @@ -74,7 +74,7 @@ def is_serving(self) -> bool:
raise NotImplementedError

@abstractmethod
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = ...) -> None:
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = ...) -> NoReturn:
"""
Starts the server's main loop.
Expand Down
12 changes: 7 additions & 5 deletions src/easynetwork/api_async/server/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import weakref
from collections import deque
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Coroutine, Iterator, Mapping, Sequence
from typing import TYPE_CHECKING, Any, Generic, final
from typing import TYPE_CHECKING, Any, Generic, NoReturn, final

from ..._typevars import _RequestT, _ResponseT
from ...exceptions import ClientClosedError, ServerAlreadyRunning, ServerClosedError
Expand Down Expand Up @@ -210,8 +210,8 @@ def _value_or_default(value: float | None, default: float) -> float:
self.__is_shutdown.set()
self.__shutdown_asked: bool = False
self.__max_recv_size: int = max_recv_size
self.__listener_tasks: deque[Task[None]] = deque()
self.__mainloop_task: Task[None] | None = None
self.__listener_tasks: deque[Task[NoReturn]] = deque() # type: ignore[assignment]
self.__mainloop_task: Task[NoReturn] | None = None
self.__logger: logging.Logger = logger or logging.getLogger(__name__)
self.__client_connection_log_level: int
if log_client_connection:
Expand Down Expand Up @@ -280,7 +280,7 @@ async def shutdown(self) -> None:

shutdown.__doc__ = AbstractAsyncNetworkServer.shutdown.__doc__

async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> None:
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> NoReturn:
async with _contextlib.AsyncExitStack() as server_exit_stack:
# Wake up server
if not self.__is_shutdown.is_set():
Expand Down Expand Up @@ -346,9 +346,11 @@ async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) ->
finally:
self.__mainloop_task = None

raise AssertionError("sleep_forever() does not return")

serve_forever.__doc__ = AbstractAsyncNetworkServer.serve_forever.__doc__

async def __listener_accept(self, listener: AsyncListenerSocketAdapter, task_group: TaskGroup) -> None:
async def __listener_accept(self, listener: AsyncListenerSocketAdapter, task_group: TaskGroup) -> NoReturn:
backend = self.__backend
client_task = self.__client_coroutine
async with listener:
Expand Down
10 changes: 6 additions & 4 deletions src/easynetwork/api_async/server/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import weakref
from collections import Counter, deque
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Coroutine, Iterator, Mapping
from typing import TYPE_CHECKING, Any, Generic, TypeVar, final
from typing import TYPE_CHECKING, Any, Generic, NoReturn, TypeVar, final
from weakref import WeakValueDictionary

from ..._typevars import _RequestT, _ResponseT
Expand Down Expand Up @@ -131,7 +131,7 @@ def __init__(
self.__is_shutdown.set()
self.__shutdown_asked: bool = False
self.__sendto_lock: ILock = backend.create_lock()
self.__mainloop_task: Task[None] | None = None
self.__mainloop_task: Task[NoReturn] | None = None
self.__logger: logging.Logger = logger or logging.getLogger(__name__)
self.__client_manager: _ClientAPIManager[_ResponseT] = _ClientAPIManager(
self.__backend,
Expand Down Expand Up @@ -184,7 +184,7 @@ async def shutdown(self) -> None:

shutdown.__doc__ = AbstractAsyncNetworkServer.shutdown.__doc__

async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> None:
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> NoReturn:
async with _contextlib.AsyncExitStack() as server_exit_stack:
# Wake up server
if not self.__is_shutdown.is_set():
Expand Down Expand Up @@ -245,13 +245,15 @@ async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) ->
finally:
self.__mainloop_task = None

raise AssertionError("received_datagrams() does not return")

serve_forever.__doc__ = AbstractAsyncNetworkServer.serve_forever.__doc__

async def __receive_datagrams(
self,
socket: AsyncDatagramSocketAdapter,
task_group: TaskGroup,
) -> None:
) -> NoReturn:
backend = self.__backend
socket_family: int = socket.socket().family
datagram_received_task_method = self.__datagram_received_coroutine
Expand Down
10 changes: 6 additions & 4 deletions src/easynetwork/api_sync/server/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import threading as _threading
import time
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, NoReturn

from ...api_async.backend.abc import ThreadsPortal
from ...api_async.server.abc import SupportsEventSet
Expand Down Expand Up @@ -84,8 +84,10 @@ def shutdown(self, timeout: float | None = None) -> None:
portal.run_coroutine(self.__server.shutdown)
else:
_start = time.perf_counter()
portal.run_coroutine(self.__do_shutdown_with_timeout, timeout)
timeout -= time.perf_counter() - _start
try:
portal.run_coroutine(self.__do_shutdown_with_timeout, timeout)
finally:
timeout -= time.perf_counter() - _start
self.__is_shutdown.wait(timeout)

shutdown.__doc__ = AbstractNetworkServer.shutdown.__doc__
Expand Down Expand Up @@ -139,7 +141,7 @@ def acquire_bootstrap_lock() -> None:
server_exit_stack.callback(reset_threads_portal)
server_exit_stack.callback(acquire_bootstrap_lock)

async def serve_forever() -> None:
async def serve_forever() -> NoReturn:
async with backend.create_threads_portal() as self.__threads_portal:
# Initialization finished; release the locks
locks_stack.close()
Expand Down
2 changes: 1 addition & 1 deletion src/easynetwork/api_sync/server/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def start(self) -> None:

def run(self) -> None:
try:
return self.__server.serve_forever(is_up_event=self.__is_up_event)
self.__server.serve_forever(is_up_event=self.__is_up_event)
finally:
self.__is_up_event.set()

Expand Down

0 comments on commit 2a550ad

Please sign in to comment.