Add idiomatic documentation to Go, Java, Python, and Rust clients

This commit is contained in:
Joseph Doherty
2026-04-30 12:04:46 -04:00
parent eed1e88a37
commit 8d3352f2c6
44 changed files with 1631 additions and 102 deletions
+3
View File
@@ -14,13 +14,16 @@ class ApiKey:
value: str
def __post_init__(self) -> None:
"""Validate that the API key value is non-empty."""
if not self.value:
raise ValueError("api_key must not be empty")
def __repr__(self) -> str:
"""Return a repr that redacts the secret value."""
return f"{type(self).__name__}({REDACTED!r})"
def bearer_value(self) -> str:
"""Return the value formatted as an HTTP `Bearer` token."""
return f"Bearer {self.value}"
+6
View File
@@ -25,6 +25,7 @@ class GatewayClient:
stub: Any,
channel: grpc.aio.Channel | None = None,
) -> None:
"""Initialize the client with resolved options and a gRPC stub."""
self.options = options
self.raw_stub = stub
self._channel = channel
@@ -63,9 +64,11 @@ class GatewayClient:
)
async def __aenter__(self) -> "GatewayClient":
"""Return self to support ``async with`` usage."""
return self
async def __aexit__(self, *_exc_info: object) -> None:
"""Close the client when leaving an ``async with`` block."""
await self.close()
async def close(self) -> None:
@@ -99,6 +102,7 @@ class GatewayClient:
return Session(client=self, session_id=reply.session_id, open_reply=reply)
async def open_session_raw(self, request: pb.OpenSessionRequest) -> pb.OpenSessionReply:
"""Send an `OpenSession` RPC and return the raw reply."""
reply = await self._unary("open session", self.raw_stub.OpenSession, request)
ensure_protocol_success("open session", reply.protocol_status, reply)
return reply
@@ -107,11 +111,13 @@ class GatewayClient:
self,
request: pb.CloseSessionRequest,
) -> pb.CloseSessionReply:
"""Send a `CloseSession` RPC and return the raw reply."""
reply = await self._unary("close session", self.raw_stub.CloseSession, request)
ensure_protocol_success("close session", reply.protocol_status, reply)
return reply
async def invoke_raw(self, request: pb.MxCommandRequest) -> pb.MxCommandReply:
"""Send an `Invoke` RPC and return the raw reply."""
reply = await self._unary("invoke", self.raw_stub.Invoke, request)
ensure_protocol_success("invoke", reply.protocol_status, reply)
return reply
+1
View File
@@ -19,6 +19,7 @@ class MxGatewayError(Exception):
protocol_status: pb.ProtocolStatus | None = None,
raw_reply: Any | None = None,
) -> None:
"""Initialize with a message and the optional raw protocol context."""
super().__init__(message)
self.protocol_status = protocol_status
self.raw_reply = raw_reply
+3
View File
@@ -34,6 +34,7 @@ class GalaxyRepositoryClient:
stub: Any,
channel: grpc.aio.Channel | None = None,
) -> None:
"""Initialize the client with resolved options and a gRPC stub."""
self.options = options
self.raw_stub = stub
self._channel = channel
@@ -72,9 +73,11 @@ class GalaxyRepositoryClient:
)
async def __aenter__(self) -> "GalaxyRepositoryClient":
"""Return self to support ``async with`` usage."""
return self
async def __aexit__(self, *_exc_info: object) -> None:
"""Close the client when leaving an ``async with`` block."""
await self.close()
async def close(self) -> None:
+2
View File
@@ -23,6 +23,7 @@ class ClientOptions:
stream_timeout: float | None = None
def __post_init__(self) -> None:
"""Validate options; raise `ValueError` for invalid combinations."""
if not self.endpoint:
raise ValueError("endpoint must not be empty")
@@ -34,6 +35,7 @@ class ClientOptions:
raise ValueError("stream_timeout must be greater than zero")
def __repr__(self) -> str:
"""Return a repr that redacts the API key value."""
api_key = REDACTED if self.api_key else None
return (
f"{type(self).__name__}(endpoint={self.endpoint!r}, "
+19
View File
@@ -21,15 +21,18 @@ class Session:
session_id: str,
open_reply: pb.OpenSessionReply | None = None,
) -> None:
"""Initialize a session bound to a client and gateway session id."""
self.client = client
self.session_id = session_id
self.open_reply = open_reply
self._closed = False
async def __aenter__(self) -> "Session":
"""Return self to support ``async with`` usage."""
return self
async def __aexit__(self, *_exc_info: object) -> None:
"""Close the session when leaving an ``async with`` block."""
await self.close()
async def close(self, *, client_correlation_id: str = "") -> pb.CloseSessionReply:
@@ -74,6 +77,7 @@ class Session:
)
async def register(self, client_name: str, *, correlation_id: str = "") -> int:
"""Invoke MXAccess `Register` and return the new `ServerHandle`."""
reply = await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_REGISTER,
@@ -84,6 +88,7 @@ class Session:
return reply.register.server_handle
async def unregister(self, server_handle: int, *, correlation_id: str = "") -> None:
"""Invoke MXAccess `Unregister` for a previously registered `ServerHandle`."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_UNREGISTER,
@@ -99,6 +104,7 @@ class Session:
*,
correlation_id: str = "",
) -> None:
"""Invoke MXAccess `RemoveItem` for the given `ItemHandle`."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_REMOVE_ITEM,
@@ -117,6 +123,7 @@ class Session:
*,
correlation_id: str = "",
) -> int:
"""Invoke MXAccess `AddItem` and return the new `ItemHandle`."""
reply = await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_ADD_ITEM,
@@ -137,6 +144,7 @@ class Session:
*,
correlation_id: str = "",
) -> int:
"""Invoke MXAccess `AddItem2` with item context and return the new `ItemHandle`."""
reply = await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_ADD_ITEM2,
@@ -157,6 +165,7 @@ class Session:
*,
correlation_id: str = "",
) -> None:
"""Invoke MXAccess `Advise` to subscribe an existing `ItemHandle` to events."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_ADVISE,
@@ -175,6 +184,7 @@ class Session:
*,
correlation_id: str = "",
) -> None:
"""Invoke MXAccess `UnAdvise` to stop event delivery for an `ItemHandle`."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_UN_ADVISE,
@@ -193,6 +203,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `AddItemBulk` and return one result per tag address."""
if tag_addresses is None:
raise TypeError("tag_addresses is required")
_ensure_bulk_size("tag_addresses", len(tag_addresses))
@@ -215,6 +226,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `AdviseItemBulk` and return one result per item handle."""
if item_handles is None:
raise TypeError("item_handles is required")
_ensure_bulk_size("item_handles", len(item_handles))
@@ -237,6 +249,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `RemoveItemBulk` and return one result per item handle."""
if item_handles is None:
raise TypeError("item_handles is required")
_ensure_bulk_size("item_handles", len(item_handles))
@@ -259,6 +272,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `UnAdviseItemBulk` and return one result per item handle."""
if item_handles is None:
raise TypeError("item_handles is required")
_ensure_bulk_size("item_handles", len(item_handles))
@@ -281,6 +295,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `SubscribeBulk` and return one result per tag address."""
if tag_addresses is None:
raise TypeError("tag_addresses is required")
_ensure_bulk_size("tag_addresses", len(tag_addresses))
@@ -303,6 +318,7 @@ class Session:
*,
correlation_id: str = "",
) -> list[pb.SubscribeResult]:
"""Invoke MXAccess `UnsubscribeBulk` and return one result per item handle."""
if item_handles is None:
raise TypeError("item_handles is required")
_ensure_bulk_size("item_handles", len(item_handles))
@@ -327,6 +343,7 @@ class Session:
user_id: int = 0,
correlation_id: str = "",
) -> None:
"""Invoke MXAccess `Write` for an `ItemHandle` with the converted value."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_WRITE,
@@ -350,6 +367,7 @@ class Session:
user_id: int = 0,
correlation_id: str = "",
) -> None:
"""Invoke MXAccess `Write2` with both a value and a client-supplied timestamp."""
await self.invoke(
pb.MxCommand(
kind=pb.MX_COMMAND_KIND_WRITE2,
@@ -369,6 +387,7 @@ class Session:
*,
after_worker_sequence: int = 0,
) -> AsyncIterator[pb.MxEvent]:
"""Return an async iterator of `MxEvent` messages for this session."""
return self.client.stream_events_raw(
pb.StreamEventsRequest(
session_id=self.session_id,
@@ -42,6 +42,7 @@ def version(output_json: bool) -> None:
def gateway_options(command: Callable[..., Any]) -> Callable[..., Any]:
"""Apply the shared gateway connection options to a Click command."""
command = click.option("--endpoint", default="localhost:5000", show_default=True)(command)
command = click.option("--api-key", default=None, help="Gateway API key.")(command)
command = click.option(