chattolib-psxw--move-chattolib-transport-to-the-connectrpc-python.md 2.5 KB


chattolib-psxw

title: Move chattolib transport to the connectrpc Python package status: completed type: task priority: high created_at: 2026-07-09T10:03:55Z

updated_at: 2026-07-09T10:20:09Z

Replace hand-rolled Connect-JSON-over-httpx with the official connectrpc Python library (chatto-bot uses this too). Generate connect-python service stubs for every Chatto service from the vendored .proto files, rewrite ChattoClient so its methods delegate to those stubs, drop _transport.py, and keep the Pythonic top-level API (client.post_message(...)) intact.

Summary of Changes

Migrated the chattolib request/response transport from hand-rolled Connect-JSON-over-httpx to the official connectrpc Python package plus generated ConnectRPC service stubs (same transport chatto-bot uses).

  • Extended scripts/generate_pb.sh to fetch every Chatto proto (api/v1, admin/v1, auth/v1, discovery/v1, realtime/v1) and to invoke protoc-gen-connect-python alongside the standard --python_out. Now produces 39 *_pb2.py message modules and 25 *_connect.py service stub modules under src/chattolib/_pb/.
  • _transport.py rewritten: exposes build_service_clients(base_url) (returns a typed ServiceClients bundle with one client per Chatto service, all sharing the google.protobuf binary codec) plus translate_connect_error (ConnectError → ChattoAuthError/ChattoConnectError) and pb_to_dict (bridges protobuf responses to the dataclass .parse() methods).
  • ChattoClient fully rewritten: each method builds a protobuf request, calls the appropriate stub via a common _rpc helper that adds auth headers and translates errors, then normalises the response into the existing dataclass types. Added a public .services property so callers can reach the raw ConnectRPC clients directly.
  • Removed the client.call(service, method, request) string-based escape hatch (replaced by .services).
  • pyproject.toml: added connectrpc>=0.11 and protobuf>=5.28 as hard runtime deps; httpx stays for the /auth/login endpoint; websockets still under [realtime].
  • Tests rewritten to mock at the service-client method level via unittest.mock.AsyncMock — 48 tests, all passing, no HTTP mocking.
  • ruff + mypy --strict clean.

Verified live against Chatto HQ (0.4.2) with the RoboChatto account: login → me() → list_rooms() → get_motd() → list_roles() → has_notifications() all succeed end-to-end. Realtime WebSocket unchanged and still working.

CLAUDE.md architecture section refreshed to describe the new transport and _pb layout.