Skip to content

Support Logging, improve interface#3

Merged
mesudip merged 9 commits into
masterfrom
dev
Feb 15, 2026
Merged

Support Logging, improve interface#3
mesudip merged 9 commits into
masterfrom
dev

Conversation

@mesudip
Copy link
Copy Markdown
Owner

@mesudip mesudip commented Feb 14, 2026

No description provided.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @mesudip, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the cbor-rpc library by introducing a more powerful and flexible client-side RPC call management system. It allows developers to monitor the execution of remote calls with real-time logging and progress indicators, and provides the ability to cancel ongoing operations. These changes aim to make the RPC framework more interactive, observable, and resilient for complex asynchronous applications.

Highlights

  • Enhanced RPC Client Interface: Introduced RpcCallHandle to provide a more robust interface for managing individual RPC calls, enabling features like real-time log streaming, progress updates, and explicit call cancellation.
  • Improved Logging and Progress Reporting: Refactored the RpcLogger to integrate directly with EventPipe for asynchronous log transmission and added mechanisms for servers to emit progress updates during long-running RPC calls, which clients can subscribe to.
  • RPC Call Cancellation Support: Implemented a cancellation mechanism allowing clients to signal long-running server-side RPC methods to terminate gracefully, improving responsiveness and resource management.
  • Refined RPC Base Classes: Renamed RpcClient to RpcInitClient and introduced a new RpcClient (inheriting from RpcInitClient) to better delineate client roles. RpcServer was made generic to support various connection ID types.
  • Expanded Pipe and Transformer Exports: Added StdioPipe, SshPipe, SshServer, AsyncTransformer, TransformerPipe, and EventTransformerPipe to the main cbor_rpc package exports, making them more accessible.
  • Comprehensive Documentation Updates: Significantly expanded README.md and added AGENTS.md to provide detailed architectural overviews, usage examples for clients and servers, and explanations of pipes, transformers, and the new RPC features.
  • Consolidated and New Test Coverage: Removed redundant RPC test helpers and dedicated logging/stdio tests, integrating their functionality into a comprehensive test_rpc_system.py. New tests were added specifically for RpcCallHandle and RPC cancellation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • AGENTS.md
    • Added new documentation covering architecture, pipes, transformers, and RPC layer, including client/server examples and tips.
  • README.md
    • Expanded documentation with sections on RPC system capabilities, server/client creation, pipes, transformers, and high-level pipes.
  • cbor_rpc/init.py
    • Updated imports and __all__ to include new pipe types (AioPipe, StdioPipe, SshPipe, SshServer) and new transformer types (AsyncTransformer, EventTransformerPipe, TransformerPipe).
    • Removed RpcAuthorizedClient and added RpcInitClient and RpcClient to exports.
  • cbor_rpc/rpc/init.py
    • Updated imports to reflect RpcInitClient and RpcClient changes, removing RpcAuthorizedClient.
  • cbor_rpc/rpc/context.py
    • Added current_request_id ContextVar for tracking request context.
    • Modified RpcCallContext constructor to include id_, emit_progress callback, and a cancelled flag.
    • Added a progress method to RpcCallContext for emitting progress updates.
  • cbor_rpc/rpc/logging.py
    • Refactored RpcLogger to use an EventPipe for sending logs and support a min_level for filtering.
    • Implemented asynchronous log writing using asyncio.create_task.
  • cbor_rpc/rpc/rpc_base.py
    • Renamed RpcClient to RpcInitClient.
    • Introduced a new RpcClient class inheriting from RpcInitClient.
    • Made RpcServer generic with T_ConnId to support various connection ID types.
    • Removed RpcAuthorizedClient.
  • cbor_rpc/rpc/rpc_call.py
    • Added new file defining RpcCallHandle for managing RPC call lifecycle, including log/progress listeners and cancellation.
  • cbor_rpc/rpc/rpc_server.py
    • Updated RpcV1Server to be generic with str as the connection ID type.
    • Adjusted get_client method to return Optional[RpcClient].
  • cbor_rpc/rpc/rpc_v1.py
    • Major refactoring: RpcCore was merged into RpcV1.
    • Implemented RpcCallHandle for managing outgoing calls, including log/progress streaming and cancellation.
    • Updated message protocols to support logging, progress, and cancellation messages.
    • Integrated RpcCallContext and RpcLogger for handling incoming calls and their associated context.
    • Removed _handle_proto_2 and _handle_proto_3 as their logic is now integrated into the main on_data handler.
    • Modified make_rpc_v1 and read_only_client to align with the new RpcV1 structure and RpcCallContext.
  • cbor_rpc/stdio/init.py
    • Added new file to export StdioPipe.
  • cbor_rpc/transformer/init.py
    • Updated imports to include AsyncTransformer, TransformerPipe, and EventTransformerPipe from the base module.
  • cbor_rpc/transformer/base/init.py
    • Updated imports to include TransformerPipe and EventTransformerPipe.
  • examples/ssh_exec/README.md
    • Updated RpcClient reference to RpcInitClient in the documentation.
  • examples/ssh_exec/rpc_client.py
    • Updated RpcClient import and instantiation to RpcInitClient.
  • tests/conftest.py
    • Removed rpc_test_helpers import.
    • Updated get_stdio_server_script_path to directly return the script path string.
  • tests/docker/sshd-python/Dockerfile
    • Renamed rpc_server.py to unix_socket_rpc_server.py in Dockerfile copy commands.
  • tests/docker/sshd-python/performance_server.py
    • Added test_logging and cancel_me methods to handle_method_call for testing new logging, progress, and cancellation features.
  • tests/docker/sshd-python/rpc_server.py
    • Renamed to unix_socket_rpc_server.py.
  • tests/integration/rpc_test_helpers.py
    • Removed file as its functionality was integrated or replaced.
  • tests/integration/test_rpc_ssh_stdio.py
    • Removed file as its functionality was integrated or replaced.
  • tests/integration/test_rpc_stdio.py
    • Removed file as its functionality was integrated or replaced.
  • tests/integration/test_rpc_system.py
    • Refactored test setup to use asynccontextmanager for pipe creation, consolidating test fixtures.
    • Consolidated RPC client creation into a single fixture.
    • Added new tests for RPC logging, progress reporting, and cancellation.
    • Removed direct usage of run_rpc_all_methods.
  • tests/rpc/test_rpc_base.py
    • Updated DummyClient to inherit from RpcInitClient and use its abstract methods.
  • tests/rpc/test_rpc_call.py
    • Added new file with comprehensive tests for RpcCallHandle functionality, including logging, progress, cancellation, timeout, and result resolution.
  • tests/rpc/test_rpc_cancellation.py
    • Added new file with a dedicated test for RPC call cancellation.
  • tests/rpc/test_rpc_logging.py
    • Removed file as logging tests were integrated into other test suites.
  • tests/rpc/test_rpc_v1.py
    • Updated EventRpcHelper and read_only_client to align with the new RpcV1 class structure and method signatures.
    • Adjusted test assertions to match the updated RPC protocol and logging behavior.
  • tests/rpc/test_rpc_v1_extra.py
    • Updated CoreOnlyRpc to inherit from RpcV1 and adjusted its method signatures.
    • Modified test assertions to reflect changes in logging and protocol message formats.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Feb 14, 2026

Codecov Report

❌ Patch coverage is 86.34686% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.69%. Comparing base (3e95b3c) to head (58b9a46).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
cbor_rpc/rpc/rpc_v1.py 85.81% 14 Missing and 6 partials ⚠️
cbor_rpc/rpc/rpc_call.py 85.50% 7 Missing and 3 partials ⚠️
cbor_rpc/rpc/logging.py 78.26% 4 Missing and 1 partial ⚠️
cbor_rpc/rpc/context.py 88.88% 1 Missing ⚠️
cbor_rpc/ssh/ssh_pipe.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #3      +/-   ##
==========================================
- Coverage   83.38%   82.69%   -0.69%     
==========================================
  Files          28       30       +2     
  Lines        1294     1387      +93     
  Branches      137      153      +16     
==========================================
+ Hits         1079     1147      +68     
- Misses        170      189      +19     
- Partials       45       51       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant improvements to the RPC interface, most notably adding support for call-specific logging, progress reporting, and cancellation via the new RpcCallHandle. The refactoring also enhances type safety by making RpcServer generic. The documentation has been updated to reflect these new capabilities. While the core changes are excellent, there are a few areas that need attention. The example for SSH RPC is now broken due to the API changes and needs to be updated. Additionally, there are some minor issues in the documentation and a few inconsistencies in test helper method signatures that should be addressed for clarity and correctness.

Comment thread examples/ssh_exec/rpc_client.py Outdated

# Create RPC Client using the SSH pipe
client = RpcClient(json_pipe)
client = RpcInitClient(json_pipe)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The change to RpcInitClient (which is now RpcV1) breaks this example. The new RpcV1 API does not have set_on_log, request, or close methods directly on the client instance. The example needs to be updated to use the new RpcCallHandle for making calls and handling logs, and pipe.terminate() for closing the connection. A working example is crucial for users to understand and adopt the new API.

Comment thread AGENTS.md
- **Timeouts**: Default 30s. Set via `rpc.set_timeout(ms)`.
- **Context**: `RpcCallContext` in handlers provides metadata/logging.
- **Logging**: Controlled via `cbor_rpc.RpcLogger`.
m No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There seems to be a stray 'm' character at the end of the file. Please remove it.

Comment thread README.md Outdated
Comment on lines +125 to +126
2. **`pipeline("data", handler)`**: Used for serial processing. Handlers are awaited in order. If a pipeline handler throws an error, it stops the chain and emits an `"error"`.
1. **`on("data", handler)`**: Simple pub/sub. The handler is called whenever data arrives. If it's a coroutine, it's run in the background.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The numbering in this list is incorrect. It goes from 2 to 1. It should be sequential.

Suggested change
2. **`pipeline("data", handler)`**: Used for serial processing. Handlers are awaited in order. If a pipeline handler throws an error, it stops the chain and emits an `"error"`.
1. **`on("data", handler)`**: Simple pub/sub. The handler is called whenever data arrives. If it's a coroutine, it's run in the background.
1. **`pipeline("data", handler)`**: Used for serial processing. Handlers are awaited in order. If a pipeline handler throws an error, it stops the chain and emits an `"error"`.
2. **`on("data", handler)`**: Simple pub/sub. The handler is called whenever data arrives. If it's a coroutine, it's run in the background.

Comment thread cbor_rpc/rpc/rpc_call.py
Comment on lines +71 to +76
def on_progress(self, callback: Callable[[Any, Any], None]) -> "RpcCallHandle":
"""
Register a callback for progress updates.
Callback signature: (value: Any, metadata: Any)
"""
self._progress_listeners.append(callback)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with on_log, the on_progress method should also return self to allow for method chaining. This improves the usability of the RpcCallHandle API.

Suggested change
def on_progress(self, callback: Callable[[Any, Any], None]) -> "RpcCallHandle":
"""
Register a callback for progress updates.
Callback signature: (value: Any, metadata: Any)
"""
self._progress_listeners.append(callback)
def on_progress(self, callback: Callable[[Any, Any], None]) -> "RpcCallHandle":
"""
Register a callback for progress updates.
Callback signature: (value: Any, metadata: Any)
"""
self._progress_listeners.append(callback)
return self

Comment thread cbor_rpc/rpc/rpc_v1.py Outdated

self._resolve_result = resolve_result

async def on_data(data: List[Any]) -> None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The on_data method is quite long and handles all protocol messages in a single block. To improve readability and maintainability, consider refactoring it by breaking it down into smaller, more focused helper methods for each protocol or message type (e.g., _handle_rpc_call, _handle_stream_message, _handle_event), similar to the previous _handle_proto_* structure.

Comment thread cbor_rpc/rpc/rpc_base.py
Comment on lines +20 to 23
class RpcClient(RpcInitClient):
@abstractmethod
def get_id(self) -> str:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The renaming of RpcAuthorizedClient to RpcClient and RpcClient to RpcInitClient could be confusing. While the new structure is logical, it might be worth adding a note in the documentation or PR description about this change to avoid confusion for developers familiar with the previous API.

Comment on lines +115 to +127
@pytest.fixture(params=["stdio", "unix_local", "unix_ssh"])
async def rpc_client(request, get_stdio_server_script_path, ssh_server):
if request.param == "stdio":
async with _stdio_pipe_ctx(get_stdio_server_script_path) as pipe:
yield RpcV1.read_only_client(pipe)
elif request.param == "unix_local":
async with _unix_local_pipe_ctx() as pipe:
yield RpcV1.read_only_client(pipe)
elif request.param == "unix_ssh":
async with _unix_ssh_pipe_ctx(ssh_server) as pipe:
yield RpcV1.read_only_client(pipe)
else:
raise ValueError(f"Unknown param: {request.param}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a parametrized fixture to run tests against multiple transport layers (stdio, unix local, unix ssh) is an excellent way to ensure broad compatibility and reduce code duplication. This is a great improvement to the test suite's structure and effectiveness.

Comment thread tests/rpc/test_rpc_v1.py Outdated
Comment on lines 44 to 45
def handle_method_call(self, method: str, args: List[Any]) -> Any:
raise Exception("Event-only RPC")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The signature of handle_method_call in the EventRpcHelper class does not match the one in its superclass RpcV1. It's missing the context: RpcCallContext parameter. Please update the signature to maintain consistency with the base class.

Suggested change
def handle_method_call(self, method: str, args: List[Any]) -> Any:
raise Exception("Event-only RPC")
def handle_method_call(self, context: RpcCallContext, method: str, args: List[Any]) -> Any:
raise Exception("Event-only RPC")

@mesudip mesudip merged commit c368904 into master Feb 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants