Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal: Simplified doc inheritance #150

Merged
merged 1 commit into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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