feat: Python client implementation for River protocol v2.0#356
Draft
feat: Python client implementation for River protocol v2.0#356
Conversation
Clean room implementation of a River client in Python, referencing the TypeScript client and PROTOCOL.md. Includes full support for all four procedure types (rpc, stream, upload, subscription), transparent reconnection, heartbeat echo, and seq/ack bookkeeping. 40 tests passing against the TypeScript server.
Replace the hand-written .mjs test server with the original .ts source. conftest.py now runs esbuild at test session start to bundle test_server.ts -> test_server.mjs (gitignored build artifact). This avoids tsx/ts-node runtime module resolution issues with the river repo's bundler-style tsconfig while keeping the test server as authored TypeScript.
…se, and Readable edge cases - Add CancellationService to test server with blocking + immediate handlers - Add client-initiated cancellation tests for all 4 proc types (rpc, stream, upload, subscription) - Add idempotent close tests (stream, subscription) and cancellation-after-transport-close - Add eagerly-connect E2E test - Add 17 new Readable unit tests: locking semantics, eager iteration, collect-waits-for-close, break variants - Fix Readable to enforce locking (TypeError on double __aiter__/collect) - Replace async generator iterator with _ReadableIterator class for synchronous break cleanup - Fix break() to be no-op when stream already closed with empty queue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Add a Python client for the River protocol, enabling Python applications to call River services. This is a clean room implementation referencing the TypeScript client and
PROTOCOL.md.Slack thread: https://slack.com/archives/C0AEE1W8E3X/p1771490314906739
What changed
Added
python-client/directory containing a complete River v2.0 client implementation in Python.Core modules:
river/types.py— Transport message types, control flags, handshake payloads, result types, ID generationriver/codec.py— NaiveJsonCodec (JSON) and BinaryCodec (msgpack) with CodecMessageAdapterriver/streams.py— Async Readable/Writable stream abstractions for procedure I/Oriver/session.py— Session state machine with seq/ack bookkeeping, send buffer, heartbeat echo, grace periodsriver/transport.py— WebSocketClientTransport with connection lifecycle, handshake, reconnection with exponential backoff, message dispatchriver/client.py— RiverClient providingrpc(),stream(),upload(),subscribe()APIsriver/codegen/— Code generator producing typed Python client stubs from River service schemasProtocol features implemented:
Test suite (168 tests, all passing):
test_e2e.py(57 tests) — E2E tests against a TS server: rpc, stream, upload, subscription, fallible procedures, concurrent operations, disconnect handling, cancellation, idempotent close, transparent reconnect, Readable/Writable edge cases, codec unit tests, listener cleanuptest_equivalence.py(64 tests) — Cross-codec parametrized equivalence tests running every scenario against both NaiveJsonCodec and BinaryCodec (each codec paired with a matching TS server instance). Covers RPC, stream, upload, subscription, cancellation, disconnect, and orderingtest_handshake.py(4 tests) — Custom handshake metadata: valid token, invalid token (protocolError event), missing metadata, metadata resent across reconnect. Uses a dedicated TS server with handshake validationtest_session.py(6 tests) — Deterministic session lifecycle with short timeouts: heartbeat miss → NO_CONNECTION, active RPCs keep connection alive, grace period expiry destroys session, reconnect within grace preserves session, retry budget backoff increases, budget restores after successtest_codegen.py(23 tests) — Schema conversion, generated imports, typed client live testsTest infrastructure:
test_server.ts— 6 services (test, ordering, fallible, subscribable, uploadable, cancellation) withRIVER_CODECenv var for binary codec supporttest_server_handshake.ts— Server with{token: string}handshake validationextract_test_schema.ts— Schema extraction for codegen testsconftest.py— Session-scoped server fixtures (JSON, binary, handshake),codec_and_urlparametrized fixture, esbuild bundlingRunning the tests:
Versioning