From 275def6429276e30084bcb742bdf1e4c1a63ee93 Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 13:44:26 +0200 Subject: [PATCH 1/7] initial api documentation --- docs/api.rst | 30 +++++++++++------------------- docs/conf.py | 9 +++++---- docs/extensions.rst | 9 +++++++++ docs/guide.rst | 8 ++++++++ docs/index.rst | 14 +++++++++----- docs/quickstart.rst | 7 +++++-- rsocket/request_handler.py | 26 +++++++++++++++++++++----- rsocket/rsocket_base.py | 20 ++++++++++++++++++++ rsocket/rsocket_server.py | 4 +++- 9 files changed, 91 insertions(+), 36 deletions(-) create mode 100644 docs/extensions.rst create mode 100644 docs/guide.rst diff --git a/docs/api.rst b/docs/api.rst index 94308d4b..a9106423 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,30 +1,22 @@ -API Reference -------------- +Core API Reference +================== -Core ----- +Server +------ .. automodule:: rsocket.rsocket_server :members: + :inherited-members: + +Client +------ .. automodule:: rsocket.rsocket_client :members: + :inherited-members: -Helpers --------- - -Requester -~~~~~~~~~ - -Responder -~~~~~~~~~ - -Clients +Handler ------- -.. automodule:: rsocket.awaitable.awaitable_rsocket +.. automodule:: rsocket.request_handler :members: - -.. automodule:: rsocket.rx_support.rx_rsocket - :members: - diff --git a/docs/conf.py b/docs/conf.py index 95d64f77..a4227f59 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,12 +18,12 @@ # -- Project information ----------------------------------------------------- import os -project = 'rsocket' -copyright = '2022, gabis@precog.co' -author = 'gabis@precog.co' +project = 'RSocket python' +copyright = '2021, jellofishi@pm.me' +author = 'jellofishi@pm.me' # The full version, including alpha/beta/rc tags -release = '0.3' +release = '0.4.6' # -- General configuration --------------------------------------------------- @@ -72,3 +72,4 @@ autosummary_generate = True autodoc_typehints = 'description' autodoc_typehints_format = 'short' +autodoc_member_order = 'bysource' diff --git a/docs/extensions.rst b/docs/extensions.rst new file mode 100644 index 00000000..177a4259 --- /dev/null +++ b/docs/extensions.rst @@ -0,0 +1,9 @@ +Extensions +========== + +.. autosummary:: + :toctree: generated + +.. automodule:: rsocket + :noindex: + diff --git a/docs/guide.rst b/docs/guide.rst new file mode 100644 index 00000000..c85a6df7 --- /dev/null +++ b/docs/guide.rst @@ -0,0 +1,8 @@ +Guide +===== + +.. autosummary:: + :toctree: generated + +A detailed getting started guide is available at https://rsocket.io/guides/rsocket-py/tutorial/ + diff --git a/docs/index.rst b/docs/index.rst index 93b19c6b..0afbbd28 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,4 @@ -.. Dataclasses Serialization documentation master file, created by -sphinx-quickstart on Fri Apr 22 11:01:09 2022. -You can adapt this file completely to your liking, but it should at least -contain the root `toctree` directive. +.. rsocket documentation master file RSocket ======= @@ -10,14 +7,21 @@ RSocket :maxdepth: 1 quickstart + guide api + extensions changelog .. autosummary:: :toctree: generated -RSocket ... +The python rsocket package implements the 1.0 version of the RSocket protocol (excluding "resume" functionality) +and is designed for use in python >= 3.8 using asyncio. + +.. note:: + The python package API is not stable. There may be changes until version 1.0.0. + Indices and tables ================== diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 70d6749b..35f5fd5b 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -1,5 +1,8 @@ Quick start =========== -.. automodule:: rsocket.rsocket - :members: +.. autosummary:: + :toctree: generated + + +A quick getting started guide is available at https://rsocket.io/guides/rsocket-py/simple \ No newline at end of file diff --git a/rsocket/request_handler.py b/rsocket/request_handler.py index 087205d0..9eb93580 100644 --- a/rsocket/request_handler.py +++ b/rsocket/request_handler.py @@ -1,4 +1,3 @@ -import asyncio from abc import ABCMeta, abstractmethod from datetime import timedelta from typing import Tuple, Optional @@ -13,6 +12,9 @@ class RequestHandler(metaclass=ABCMeta): + """ + An interface which defines handler for all rsocket interactions, and some other events (e.g. on_setup). + """ @abstractmethod async def on_setup(self, @@ -39,12 +41,16 @@ async def request_fire_and_forget(self, payload: Payload): ... @abstractmethod - async def request_response(self, payload: Payload) -> asyncio.Future: - ... + async def request_response(self, payload: Payload) -> Awaitable[Payload]: + """ + Handle request-response interaction + """ @abstractmethod async def request_stream(self, payload: Payload) -> Publisher: - ... + """ + Handle request-stream interaction + """ @abstractmethod async def on_error(self, error_code: ErrorCode, payload: Payload): @@ -72,6 +78,13 @@ def _parse_composite_metadata(self, metadata: bytes) -> CompositeMetadata: class BaseRequestHandler(RequestHandler): + """ + Default implementation of :class:`RequestHandler ` to simplify + implementing handlers. + + For each request handler, the implementation will raise a RuntimeError. For :meth:`request_fire_and_forget` and + :meth:`on_metadata_push` the request will be ignored. + """ async def on_setup(self, data_encoding: bytes, @@ -80,10 +93,13 @@ async def on_setup(self, """Nothing to do on setup by default""" async def request_channel(self, payload: Payload) -> Tuple[Optional[Publisher], Optional[Subscriber]]: + """ + Raise RuntimeError by default if not implemented. + """ raise RuntimeError('Not implemented') async def request_fire_and_forget(self, payload: Payload): - """The requester isn't listening for errors. Nothing to do.""" + """Ignored by default""" async def on_metadata_push(self, payload: Payload): """Nothing by default""" diff --git a/rsocket/rsocket_base.py b/rsocket/rsocket_base.py index 02109ab5..ab797bf7 100644 --- a/rsocket/rsocket_base.py +++ b/rsocket/rsocket_base.py @@ -481,6 +481,10 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): await self.close() def request_response(self, payload: Payload) -> Awaitable[Payload]: + """ + Initiate a request-response interaction. + """ + logger().debug('%s: request-response: %s', self._log_identifier(), payload) requester = RequestResponseRequester(self, payload) @@ -488,6 +492,10 @@ def request_response(self, payload: Payload) -> Awaitable[Payload]: return requester.run() def fire_and_forget(self, payload: Payload) -> Awaitable[None]: + """ + Initiate a fire-and-forget interaction. + """ + logger().debug('%s: fire-and-forget: %s', self._log_identifier(), payload) stream_id = self._allocate_stream() @@ -497,6 +505,10 @@ def fire_and_forget(self, payload: Payload) -> Awaitable[None]: return frame.sent_future def request_stream(self, payload: Payload) -> Union[BackpressureApi, Publisher]: + """ + Initiate a request-stream interaction. + """ + logger().debug('%s: request-stream: %s', self._log_identifier(), payload) requester = RequestStreamRequester(self, payload) @@ -507,12 +519,20 @@ def request_channel( payload: Payload, publisher: Optional[Publisher] = None, sending_done: Optional[asyncio.Event] = None) -> Union[BackpressureApi, Publisher]: + """ + Initiate a request-channel interaction. + """ + logger().debug('%s: request-channel: %s', self._log_identifier(), payload) requester = RequestChannelRequester(self, payload, publisher, sending_done) return self.register_new_stream(requester) def metadata_push(self, metadata: bytes) -> Awaitable[None]: + """ + Initiate a metadata-push interaction. + """ + logger().debug('%s: metadata-push: %s', self._log_identifier(), metadata) frame = to_metadata_push_frame(metadata) diff --git a/rsocket/rsocket_server.py b/rsocket/rsocket_server.py index 7274c5d4..601ce6bd 100644 --- a/rsocket/rsocket_server.py +++ b/rsocket/rsocket_server.py @@ -12,7 +12,9 @@ class RSocketServer(RSocketBase): - + """ + Server side instance of an rsocket connection. + """ def __init__(self, transport: Transport, handler_factory: Callable[[], RequestHandler] = BaseRequestHandler, From 37fd4262f4c5a26263d4476223e6ca69baea1e76 Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 13:53:26 +0200 Subject: [PATCH 2/7] documentation build fix --- docs/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a4227f59..7c5a2d59 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,9 +10,10 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys + +sys.path.insert(0, os.path.abspath('..')) # -- Project information ----------------------------------------------------- From a52ea5f6a5d7b3b2ff4d621d00da8045d43521f1 Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 13:58:52 +0200 Subject: [PATCH 3/7] documentation build fix --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 7c5a2d59..96bff2ec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -74,3 +74,4 @@ autodoc_typehints = 'description' autodoc_typehints_format = 'short' autodoc_member_order = 'bysource' +master_doc = 'index' From 7ed1ff7ff77ef9bbf0aa77d5d48d1bf11e3fc403 Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 14:23:06 +0200 Subject: [PATCH 4/7] documentation update --- docs/conf.py | 1 + docs/extensions.rst | 37 ++++++++++++++++++++++--- rsocket/transports/aiohttp_websocket.py | 7 +++++ rsocket/transports/aioquic_transport.py | 4 +++ rsocket/transports/http3_transport.py | 10 +++++-- rsocket/transports/quart_websocket.py | 7 +++++ rsocket/transports/tcp.py | 7 +++++ 7 files changed, 66 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 96bff2ec..352b65ca 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,6 +71,7 @@ autodoc_default_flags = ['members'] autosummary_generate = True +autodoc_inherit_docstrings = False autodoc_typehints = 'description' autodoc_typehints_format = 'short' autodoc_member_order = 'bysource' diff --git a/docs/extensions.rst b/docs/extensions.rst index 177a4259..60f04232 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -1,9 +1,38 @@ Extensions ========== -.. autosummary:: - :toctree: generated +Transports +---------- -.. automodule:: rsocket - :noindex: +TCP +~~~ +.. automodule:: rsocket.transports.tcp + :members: + +Websocket +~~~~~~~~~ + +aiohttp ++++++++ + +.. automodule:: rsocket.transports.aiohttp_websocket + :members: + +quart ++++++ + +.. automodule:: rsocket.transports.quart_websocket + :members: + +quic +~~~~ + +.. automodule:: rsocket.transports.aioquic_transport + :members: + +http3 +~~~~~ + +.. automodule:: rsocket.transports.http3_transport + :members: \ No newline at end of file diff --git a/rsocket/transports/aiohttp_websocket.py b/rsocket/transports/aiohttp_websocket.py index a7692d17..b8d934ce 100644 --- a/rsocket/transports/aiohttp_websocket.py +++ b/rsocket/transports/aiohttp_websocket.py @@ -18,6 +18,10 @@ async def websocket_client(url: Optional[str] = None, websocket: Optional[ClientWebSocketResponse] = None, **kwargs) -> RSocketClient: + """ + Helper method to instantiate an RSocket client using a websocket url over aiohttp client. + """ + async with RSocketClient(single_transport_provider(TransportAioHttpClient(url, websocket)), **kwargs) as client: yield client @@ -40,6 +44,9 @@ async def websocket_handler(request): class TransportAioHttpClient(AbstractMessagingTransport): + """ + RSocket transport over client side aiohttp websocket. + """ def __init__(self, url: Optional[str] = None, websocket: Optional[ClientWebSocketResponse] = None): super().__init__() diff --git a/rsocket/transports/aioquic_transport.py b/rsocket/transports/aioquic_transport.py index 3e35ce25..29ef559f 100644 --- a/rsocket/transports/aioquic_transport.py +++ b/rsocket/transports/aioquic_transport.py @@ -80,6 +80,10 @@ def quic_event_received(self, event: QuicEvent) -> None: class RSocketQuicTransport(AbstractMessagingTransport): + """ + RSocket transport over server/client side quic connection. + """ + def __init__(self, quic_protocol: RSocketQuicProtocol): super().__init__() self._quic_protocol = quic_protocol diff --git a/rsocket/transports/http3_transport.py b/rsocket/transports/http3_transport.py index 38521fdc..3cca09d3 100644 --- a/rsocket/transports/http3_transport.py +++ b/rsocket/transports/http3_transport.py @@ -43,9 +43,6 @@ async def receive_bytes(self) -> bytes: return await self.queue.get() async def close(self, code: int = 1000, reason: str = "") -> None: - """ - Perform the closing handshake. - """ data = self.websocket.send( wsproto.events.CloseConnection(code=code, reason=reason) ) @@ -80,6 +77,10 @@ def __init__(self, url: str) -> None: class RSocketHttp3ClientProtocol(QuicConnectionProtocol): + """ + RSocket transport over client side http3 connection. + """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pushes: Dict[int, Deque[H3Event]] = {} @@ -149,6 +150,9 @@ def quic_event_received(self, event: QuicEvent) -> None: class Http3TransportWebsocket(AbstractMessagingTransport): + """ + RSocket transport over server side http3 connection. + """ def __init__(self, websocket: Union[WebSocket, ClientWebSocket]): super().__init__() diff --git a/rsocket/transports/quart_websocket.py b/rsocket/transports/quart_websocket.py index c087321f..5189cae5 100644 --- a/rsocket/transports/quart_websocket.py +++ b/rsocket/transports/quart_websocket.py @@ -10,6 +10,10 @@ async def websocket_handler(on_server_create=None, **kwargs): + """ + Helper method to instantiate an RSocket server using a quart websocket connection. + """ + transport = TransportQuartWebsocket() server = RSocketServer(transport, **kwargs) @@ -20,6 +24,9 @@ async def websocket_handler(on_server_create=None, **kwargs): class TransportQuartWebsocket(AbstractMessagingTransport): + """ + RSocket transport over server side quart websocket. + """ async def handle_incoming_ws_messages(self): try: diff --git a/rsocket/transports/tcp.py b/rsocket/transports/tcp.py index 7d25ba3d..c9c0dccc 100644 --- a/rsocket/transports/tcp.py +++ b/rsocket/transports/tcp.py @@ -6,6 +6,13 @@ class TransportTCP(Transport): + """ + RSocket transport over asyncio TCP connection. + + :param reader: asyncio connection reader stream + :param writer: asyncio connection writer stream + """ + def __init__(self, reader: StreamReader, writer: StreamWriter): super().__init__() self._writer = writer From 93b769d21097a9f44c6ac14dd428b58f128291e6 Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 14:27:48 +0200 Subject: [PATCH 5/7] documentation update --- docs/api.rst | 22 ++++++++++++++++++++++ reactivestreams/publisher.py | 4 ++++ reactivestreams/subscriber.py | 4 ++++ reactivestreams/subscription.py | 4 ++++ rsocket/rsocket_client.py | 3 +++ rsocket/rsocket_server.py | 1 + 6 files changed, 38 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index a9106423..84614fa4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -20,3 +20,25 @@ Handler .. automodule:: rsocket.request_handler :members: + + +Interfaces +---------- + +Publisher +~~~~~~~~~ + +.. automodule:: reactivestreams.publisher + :members: + +Subscriber +~~~~~~~~~~ + +.. automodule:: reactivestreams.subscriber + :members: + +Subscription +~~~~~~~~~~~~ + +.. automodule:: reactivestreams.subscription + :members: \ No newline at end of file diff --git a/reactivestreams/publisher.py b/reactivestreams/publisher.py index 6e278746..b4418597 100644 --- a/reactivestreams/publisher.py +++ b/reactivestreams/publisher.py @@ -4,6 +4,10 @@ class Publisher(metaclass=abc.ABCMeta): + """ + Handles event for subscription to a subscriber + """ + @abc.abstractmethod def subscribe(self, subscriber: Subscriber): ... diff --git a/reactivestreams/subscriber.py b/reactivestreams/subscriber.py index 62fd64f9..da06b6dc 100644 --- a/reactivestreams/subscriber.py +++ b/reactivestreams/subscriber.py @@ -5,6 +5,10 @@ class Subscriber(metaclass=ABCMeta): + """ + Handles stream events. + """ + @abstractmethod def on_subscribe(self, subscription: Subscription): ... diff --git a/reactivestreams/subscription.py b/reactivestreams/subscription.py index e0f07f13..301d23a3 100644 --- a/reactivestreams/subscription.py +++ b/reactivestreams/subscription.py @@ -2,6 +2,10 @@ class Subscription(metaclass=ABCMeta): + """ + Backpressure stream control. + """ + @abstractmethod def request(self, n: int): ... diff --git a/rsocket/rsocket_client.py b/rsocket/rsocket_client.py index 6b3cb859..d6daca51 100644 --- a/rsocket/rsocket_client.py +++ b/rsocket/rsocket_client.py @@ -18,6 +18,9 @@ class RSocketClient(RSocketBase): + """ + Server side instance of an rsocket connection. + """ def __init__(self, transport_provider: AsyncGenerator[Transport, Any], diff --git a/rsocket/rsocket_server.py b/rsocket/rsocket_server.py index 601ce6bd..a99d5a6b 100644 --- a/rsocket/rsocket_server.py +++ b/rsocket/rsocket_server.py @@ -15,6 +15,7 @@ class RSocketServer(RSocketBase): """ Server side instance of an rsocket connection. """ + def __init__(self, transport: Transport, handler_factory: Callable[[], RequestHandler] = BaseRequestHandler, From fd49f88f37f4d89cdfa049e5bc30c879d591f84d Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 14:32:32 +0200 Subject: [PATCH 6/7] documentation update --- docs/extensions.rst | 15 +++++++++++++++ rsocket/routing/request_router.py | 7 +++++++ rsocket/routing/routing_request_handler.py | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/docs/extensions.rst b/docs/extensions.rst index 60f04232..db79e231 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -35,4 +35,19 @@ http3 ~~~~~ .. automodule:: rsocket.transports.http3_transport + :members: + +Routing +------- + +RequestRouter +~~~~~~~~~~~~~ + +.. automodule:: rsocket.routing.request_router + :members: + +RoutingRequestHandler +~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: rsocket.routing.routing_request_handler :members: \ No newline at end of file diff --git a/rsocket/routing/request_router.py b/rsocket/routing/request_router.py index cfcfabd6..8f33b997 100644 --- a/rsocket/routing/request_router.py +++ b/rsocket/routing/request_router.py @@ -22,6 +22,13 @@ def decorator(function: decorated_method): class RequestRouter: + """ + Used to define routes for RSocket endpoints. + + Pass this to :class:`RoutingRequestHandler ` + to instantiate a handler using these routes. + """ + __slots__ = ( '_channel_routes', '_stream_routes', diff --git a/rsocket/routing/routing_request_handler.py b/rsocket/routing/routing_request_handler.py index 603e9d6f..310b1ba6 100644 --- a/rsocket/routing/routing_request_handler.py +++ b/rsocket/routing/routing_request_handler.py @@ -20,6 +20,11 @@ class RoutingRequestHandler(BaseRequestHandler): + """ + Handler implementation which uses a :class:`RequestRouter ` + to handle requests based on route information provided in the payload metadata. + """ + __slots__ = ( 'router', 'data_encoding', From 283cdfc6f5a1291336efa005c4a05ded7983fb8f Mon Sep 17 00:00:00 2001 From: jell-o-fishi Date: Sat, 10 Dec 2022 14:59:49 +0200 Subject: [PATCH 7/7] documentation update changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8530a67d..79cae0ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog v0.4.6 ====== - fire_and_forget now only removes the stream id when the future denoting the frame was sent, is done +- API documentation auto generated at rsocket.readthedocs.io v0.4.5 ======