Skip to content

Commit

Permalink
[DOCS] Internal: Simplified doc inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
francis-clairicia committed Nov 5, 2023
1 parent 72ea1e6 commit ac35820
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 50 deletions.
3 changes: 1 addition & 2 deletions src/easynetwork/api_async/client/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,10 @@ def get_remote_address(self) -> SocketAddress:
address_family = endpoint.extra(INETSocketAttribute.family)
return new_socket_address(remote_address, address_family)

@_utils.inherit_doc(AbstractAsyncNetworkClient)
def get_backend(self) -> AsyncBackend:
return self.__backend

get_backend.__doc__ = AbstractAsyncNetworkClient.get_backend.__doc__

async def __ensure_connected(self) -> AsyncStreamEndpoint[_SentPacketT, _ReceivedPacketT]:
if self.__endpoint is None:
endpoint_and_proxy = None
Expand Down
3 changes: 1 addition & 2 deletions src/easynetwork/api_async/client/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,10 @@ def get_remote_address(self) -> SocketAddress:
address_family = endpoint.extra(INETSocketAttribute.family)
return new_socket_address(remote_address, address_family)

@_utils.inherit_doc(AbstractAsyncNetworkClient)
def get_backend(self) -> AsyncBackend:
return self.__backend

get_backend.__doc__ = AbstractAsyncNetworkClient.get_backend.__doc__

async def __ensure_connected(self) -> AsyncDatagramEndpoint[_SentPacketT, _ReceivedPacketT]:
if self.__endpoint is None:
endpoint_and_proxy = None
Expand Down
18 changes: 6 additions & 12 deletions src/easynetwork/api_async/server/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,10 @@ def _value_or_default(value: float | None, default: float) -> float:
else:
self.__client_connection_log_level = logging.DEBUG

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def is_serving(self) -> bool:
return self.__servers is not None and all(not server.is_closing() for server in self.__servers)

is_serving.__doc__ = AbstractAsyncNetworkServer.is_serving.__doc__

def stop_listening(self) -> None:
"""
Schedules the shutdown of all listener sockets.
Expand All @@ -217,14 +216,13 @@ def stop_listening(self) -> None:
exit_stack.callback(listener_task.cancel)
del listener_task

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def server_close(self) -> None:
if self.__listeners_factory_scope is not None:
self.__listeners_factory_scope.cancel()
self.__listeners_factory = None
await self.__close_servers()

server_close.__doc__ = AbstractAsyncNetworkServer.server_close.__doc__

async def __close_servers(self) -> None:
async with contextlib.AsyncExitStack() as exit_stack:
servers, self.__servers = self.__servers, None
Expand All @@ -240,6 +238,7 @@ async def __close_servers(self) -> None:

await self.__backend.cancel_shielded_coro_yield()

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def shutdown(self) -> None:
if self.__mainloop_task is not None:
self.__mainloop_task.cancel()
Expand All @@ -252,8 +251,7 @@ async def shutdown(self) -> None:
finally:
self.__shutdown_asked = False

shutdown.__doc__ = AbstractAsyncNetworkServer.shutdown.__doc__

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> NoReturn:
async with contextlib.AsyncExitStack() as server_exit_stack:
# Wake up server
Expand Down Expand Up @@ -330,8 +328,6 @@ async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) ->

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

serve_forever.__doc__ = AbstractAsyncNetworkServer.serve_forever.__doc__

async def __serve(
self,
server: lowlevel_stream_server.AsyncStreamServer[_RequestT, _ResponseT],
Expand Down Expand Up @@ -449,6 +445,7 @@ def __suppress_and_log_remaining_exception(self, client_address: SocketAddress)
self.__logger.error("Exception occurred during processing of request from %s", client_address, exc_info=exc)
self.__logger.error("-" * 40)

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def get_addresses(self) -> Sequence[SocketAddress]:
if (servers := self.__servers) is None:
return ()
Expand All @@ -458,13 +455,10 @@ def get_addresses(self) -> Sequence[SocketAddress]:
if not server.is_closing()
)

get_addresses.__doc__ = AbstractAsyncNetworkServer.get_addresses.__doc__

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def get_backend(self) -> AsyncBackend:
return self.__backend

get_backend.__doc__ = AbstractAsyncNetworkServer.get_backend.__doc__

@property
def sockets(self) -> Sequence[SocketProxy]:
"""The listeners sockets. Read-only attribute."""
Expand Down
18 changes: 6 additions & 12 deletions src/easynetwork/api_async/server/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,17 @@ def __init__(
self.__clients_cache = defaultdict(weakref.WeakValueDictionary)
self.__send_locks_cache = defaultdict(weakref.WeakValueDictionary)

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def is_serving(self) -> bool:
return self.__servers is not None and all(not server.is_closing() for server in self.__servers)

is_serving.__doc__ = AbstractAsyncNetworkServer.is_serving.__doc__

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def server_close(self) -> None:
if self.__listeners_factory_scope is not None:
self.__listeners_factory_scope.cancel()
self.__listeners_factory = None
await self.__close_servers()

server_close.__doc__ = AbstractAsyncNetworkServer.server_close.__doc__

async def __close_servers(self) -> None:
async with contextlib.AsyncExitStack() as exit_stack:
servers, self.__servers = self.__servers, None
Expand All @@ -165,6 +163,7 @@ async def __close_servers(self) -> None:

await self.__backend.cancel_shielded_coro_yield()

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def shutdown(self) -> None:
if self.__mainloop_task is not None:
self.__mainloop_task.cancel()
Expand All @@ -177,8 +176,7 @@ async def shutdown(self) -> None:
finally:
self.__shutdown_asked = False

shutdown.__doc__ = AbstractAsyncNetworkServer.shutdown.__doc__

@_utils.inherit_doc(AbstractAsyncNetworkServer)
async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) -> NoReturn:
async with contextlib.AsyncExitStack() as server_exit_stack:
# Wake up server
Expand Down Expand Up @@ -256,8 +254,6 @@ async def serve_forever(self, *, is_up_event: SupportsEventSet | None = None) ->

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

serve_forever.__doc__ = AbstractAsyncNetworkServer.serve_forever.__doc__

async def __serve(
self,
server: lowlevel_datagram_server.AsyncDatagramServer[_RequestT, _ResponseT, tuple[Any, ...]],
Expand Down Expand Up @@ -323,6 +319,7 @@ def __suppress_and_log_remaining_exception(self, client_address: SocketAddress)
self.__logger.error("Exception occurred during processing of request from %s", client_address, exc_info=exc)
self.__logger.error("-" * 40)

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def get_addresses(self) -> Sequence[SocketAddress]:
if (servers := self.__servers) is None:
return ()
Expand All @@ -332,13 +329,10 @@ def get_addresses(self) -> Sequence[SocketAddress]:
if not server.is_closing()
)

get_addresses.__doc__ = AbstractAsyncNetworkServer.get_addresses.__doc__

@_utils.inherit_doc(AbstractAsyncNetworkServer)
def get_backend(self) -> AsyncBackend:
return self.__backend

get_backend.__doc__ = AbstractAsyncNetworkServer.get_backend.__doc__

@property
def sockets(self) -> Sequence[SocketProxy]:
"""The listeners sockets. Read-only attribute."""
Expand Down
20 changes: 5 additions & 15 deletions src/easynetwork/api_sync/server/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from ...api_async.server.abc import SupportsEventSet
from ...exceptions import ServerAlreadyRunning, ServerClosedError
from ...lowlevel import _utils
from ...lowlevel._lock import ForkSafeLock
from ...lowlevel.api_async.backend.abc import ThreadsPortal
from ...lowlevel.socket import SocketAddress
Expand Down Expand Up @@ -56,14 +57,14 @@ def __init__(self, server: AbstractAsyncNetworkServer) -> None:
self.__close_lock = ForkSafeLock()
self.__bootstrap_lock = ForkSafeLock()

@_utils.inherit_doc(AbstractNetworkServer)
def is_serving(self) -> bool:
if (portal := self._portal) is not None:
with contextlib.suppress(RuntimeError):
return portal.run_sync(self.__server.is_serving)
return False

is_serving.__doc__ = AbstractNetworkServer.is_serving.__doc__

@_utils.inherit_doc(AbstractNetworkServer)
def server_close(self) -> None:
with self.__close_lock.get(), contextlib.ExitStack() as stack, contextlib.suppress(RuntimeError):
if (portal := self._portal) is not None:
Expand All @@ -75,8 +76,7 @@ def server_close(self) -> None:
backend = self.__server.get_backend()
backend.bootstrap(self.__server.server_close)

server_close.__doc__ = AbstractNetworkServer.server_close.__doc__

@_utils.inherit_doc(AbstractNetworkServer)
def shutdown(self, timeout: float | None = None) -> None:
if (portal := self._portal) is not None:
with contextlib.suppress(RuntimeError, concurrent.futures.CancelledError):
Expand All @@ -91,8 +91,6 @@ def shutdown(self, timeout: float | None = None) -> None:
timeout -= time.perf_counter() - _start
self.__is_shutdown.wait(timeout)

shutdown.__doc__ = AbstractNetworkServer.shutdown.__doc__

async def __do_shutdown_with_timeout(self, timeout_delay: float) -> None:
backend = self.__server.get_backend()
with backend.move_on_after(timeout_delay):
Expand Down Expand Up @@ -151,21 +149,13 @@ async def serve_forever() -> NoReturn:

backend.bootstrap(serve_forever, runner_options=runner_options)

@_utils.inherit_doc(AbstractNetworkServer)
def get_addresses(self) -> Sequence[SocketAddress]:
"""
Returns all interfaces to which the listeners are bound. Thread-safe.
Returns:
A sequence of network socket address.
If the server is not serving (:meth:`is_serving` returns :data:`False`), an empty sequence is returned.
"""
if (portal := self._portal) is not None:
with contextlib.suppress(RuntimeError):
return portal.run_sync(self.__server.get_addresses)
return ()

get_addresses.__doc__ = AbstractNetworkServer.get_addresses.__doc__

@property
def _server(self) -> AbstractAsyncNetworkServer:
return self.__server
Expand Down
5 changes: 3 additions & 2 deletions src/easynetwork/api_sync/server/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from ..._typevars import _RequestT, _ResponseT
from ...api_async.server.tcp import AsyncTCPNetworkServer
from ...lowlevel import _utils
from ...lowlevel.socket import SocketProxy
from . import _base

Expand Down Expand Up @@ -110,17 +111,17 @@ def stop_listening(self) -> None:
portal.run_sync(self._server.stop_listening)

@property
@_utils.inherit_doc(AsyncTCPNetworkServer)
def sockets(self) -> Sequence[SocketProxy]:
"""The listeners sockets. Read-only attribute."""
if (portal := self._portal) is not None:
with contextlib.suppress(RuntimeError):
sockets = portal.run_sync(lambda: self._server.sockets)
return tuple(SocketProxy(sock, runner=portal.run_sync) for sock in sockets)
return ()

@property
@_utils.inherit_doc(AsyncTCPNetworkServer)
def logger(self) -> logging.Logger:
"""The server's logger."""
return self._server.logger

if TYPE_CHECKING:
Expand Down
5 changes: 3 additions & 2 deletions src/easynetwork/api_sync/server/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from ..._typevars import _RequestT, _ResponseT
from ...api_async.server.udp import AsyncUDPNetworkServer
from ...lowlevel import _utils
from ...lowlevel.socket import SocketProxy
from . import _base

Expand Down Expand Up @@ -84,17 +85,17 @@ def __init__(
)

@property
@_utils.inherit_doc(AsyncUDPNetworkServer)
def sockets(self) -> Sequence[SocketProxy]:
"""The listeners sockets. Read-only attribute."""
if (portal := self._portal) is not None:
with contextlib.suppress(RuntimeError):
sockets = portal.run_sync(lambda: self._server.sockets)
return tuple(SocketProxy(sock, runner=portal.run_sync) for sock in sockets)
return ()

@property
@_utils.inherit_doc(AsyncUDPNetworkServer)
def logger(self) -> logging.Logger:
"""The server's logger."""
return self._server.logger

if TYPE_CHECKING:
Expand Down
6 changes: 3 additions & 3 deletions src/easynetwork/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from typing import Any, Generic, final

from ._typevars import _DTOPacketT, _PacketT, _ReceivedPacketT, _SentPacketT
from .lowlevel import _utils


class AbstractPacketConverterComposite(Generic[_SentPacketT, _ReceivedPacketT, _DTOPacketT], metaclass=ABCMeta):
Expand Down Expand Up @@ -122,12 +123,11 @@ class AbstractPacketConverter(AbstractPacketConverterComposite[_PacketT, _Packet
__slots__ = ()

@abstractmethod
@_utils.inherit_doc(AbstractPacketConverterComposite)
def create_from_dto_packet(self, packet: _DTOPacketT, /) -> _PacketT:
raise NotImplementedError

@abstractmethod
@_utils.inherit_doc(AbstractPacketConverterComposite)
def convert_to_dto_packet(self, obj: _PacketT, /) -> _DTOPacketT:
raise NotImplementedError

create_from_dto_packet.__doc__ = AbstractPacketConverterComposite.create_from_dto_packet.__doc__
convert_to_dto_packet.__doc__ = AbstractPacketConverterComposite.convert_to_dto_packet.__doc__
12 changes: 12 additions & 0 deletions src/easynetwork/lowlevel/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
_T_Arg = TypeVar("_T_Arg")

_ExcType = TypeVar("_ExcType", bound=BaseException)
_FuncType = TypeVar("_FuncType", bound=Callable[..., Any])


def replace_kwargs(kwargs: dict[str, Any], keys: dict[str, str]) -> None:
Expand All @@ -89,6 +90,17 @@ def decorator(func: Callable[Concatenate[_T_Arg, _P], _R], /) -> Callable[_P, _R
return decorator


def inherit_doc(base_cls: type[Any]) -> Callable[[_FuncType], _FuncType]:
assert isinstance(base_cls, type) # nosec assert_used

def decorator(dest_func: _FuncType) -> _FuncType:
ref_func: Any = getattr(base_cls, dest_func.__name__)
dest_func.__doc__ = ref_func.__doc__
return dest_func

return decorator


def error_from_errno(errno: int) -> OSError:
return OSError(errno, os.strerror(errno))

Expand Down
Loading

0 comments on commit ac35820

Please sign in to comment.