feat(python): async send_op + AsyncDaemonConnection (partial #65)#84
feat(python): async send_op + AsyncDaemonConnection (partial #65)#84
Conversation
Extends the AsyncDaemon scaffold (PR #78) with the method set that #65 explicitly targets: "Daemon/DaemonConnection: send_op and any other HTTP call". Added: - `send_op_async`: tokio/reqwest counterpart to `send_op` that yields on I/O instead of blocking a runtime thread. - `AsyncDaemon.ensure_running` / `AsyncDaemon.stop`: native async lifecycle calls so callers no longer need `_run_in_thread` for daemon bring-up/shutdown. - `AsyncDaemonConnection`: new class mirroring `DaemonConnection` with async `build`, `deploy`, `monitor`, and their `_result` counterparts. Shares `parse_outcome` / `outcome_to_pydict` with the sync class so the structured-result schema stays identical. - `connect_daemon_async` factory. Re-exports: `AsyncDaemon`, `AsyncDaemonConnection`, `connect_daemon_async` from `fbuild`; `AsyncSerialMonitor` from `fbuild.api`. Still remaining for #65: `AsyncSerialMonitor.read_lines` / `write` / `write_json_rpc`. These need the shared WebSocket state refactored to be `Send + Sync` across `.await` points (noted in the scaffold comment) and are a follow-up PR. Tests: 3 new Rust tests spin a minimal in-process HTTP mock and exercise `send_op_async` against success, failure, and connection- error paths. Failure outcomes must still carry `message` / `exit_code` / `stderr` so async callers retain the structured-result parity with the sync API. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 57 minutes and 43 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
#65) Completes the remaining async surface from #65: - __aenter__ / __aexit__ — async context-manager that opens the /ws/serial-monitor session, sends the attach handshake, stores the split sink/source halves, and cleans up on exit. - read_lines(timeout_secs) — async batch read, honors auto_reconnect (Preempted → continue; Reconnected → continue). - write(data) — async write that waits for daemon write_ack. - write_json_rpc(request, timeout_secs) — async JSON-RPC send + poll for REMOTE: response, preserving the PR #57 full-timeout guarantee by keeping polling across empty batches. Send+Sync refactor: - Split WebSocketStream via .split() into WsSink + WsSource. - Each half stored in its own Arc<tokio::sync::Mutex<Option<_>>> so concurrent read/write futures do not serialize each other and each future owns its own Arc clone. Sync SerialMonitor is untouched — additive only. Deferred follow-up: unit tests for the new async methods. The existing 11 fbuild-python tests still pass; new tests would need a more elaborate WebSocket mock than the HTTP mock used for send_op_async (PR #84). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#65) (#107) Completes the remaining async surface from #65: - __aenter__ / __aexit__ — async context-manager that opens the /ws/serial-monitor session, sends the attach handshake, stores the split sink/source halves, and cleans up on exit. - read_lines(timeout_secs) — async batch read, honors auto_reconnect (Preempted → continue; Reconnected → continue). - write(data) — async write that waits for daemon write_ack. - write_json_rpc(request, timeout_secs) — async JSON-RPC send + poll for REMOTE: response, preserving the PR #57 full-timeout guarantee by keeping polling across empty batches. Send+Sync refactor: - Split WebSocketStream via .split() into WsSink + WsSource. - Each half stored in its own Arc<tokio::sync::Mutex<Option<_>>> so concurrent read/write futures do not serialize each other and each future owns its own Arc clone. Sync SerialMonitor is untouched — additive only. Deferred follow-up: unit tests for the new async methods. The existing 11 fbuild-python tests still pass; new tests would need a more elaborate WebSocket mock than the HTTP mock used for send_op_async (PR #84). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Extends the
AsyncDaemonscaffold (PR #78) with the method set that issue #65 explicitly targets: "Daemon/DaemonConnection:send_opand any other HTTP call".Async methods added
send_op_async-- tokio/reqwest counterpart to the blockingsend_ophelper (used by everyDaemonConnectionHTTP call).AsyncDaemon.ensure_running()-- async daemon bring-up; health poll usestokio::time::sleepso the event loop isn't pinned.AsyncDaemon.stop()-- async counterpart toDaemon.stop.AsyncDaemonConnection-- new class with:build,deploy,monitor-- async bool-returning wrappers.build_result,deploy_result,monitor_result-- async structured-dict wrappers (same keys as the sync*_resultmethods:success,message,exit_code,stdout,stderr).__aenter__/__aexit__so callers canasync with AsyncDaemonConnection(...) as conn:.connect_daemon_async()factory, mirroringconnect_daemon.AsyncDaemon,AsyncDaemonConnection,connect_daemon_asyncfromfbuild;AsyncSerialMonitorfromfbuild.api.The sync surface is untouched -- this is purely additive per the issue's Non-goals.
Still remaining for #65
AsyncSerialMonitor.read_lines/write/write_json_rpc. These need the persistent WebSocket state onAsyncSerialMonitorrefactored to beSend + Syncacross.awaitpoints (flagged in the scaffold comment added by PR feat(python): AsyncSerialMonitor scaffold via pyo3-async-runtimes (partial #65) #73). Follow-up PR.Test plan
uv run cargo test -p fbuild-python-- 11 tests pass (8 existing + 3 new).uv run cargo clippy -p fbuild-python --all-targets -- -D warnings-- clean.uv run cargo check --workspace --all-targets-- clean.tokio::net::TcpListener, no extra dev deps) and exercisesend_op_asyncagainst three paths:send_op_async_parses_success_responsesend_op_async_parses_failure_response(guardsexit_code/stderrparity with the sync API)send_op_async_returns_failure_outcome_on_connection_error(guards against panics when the daemon is down)The shared
parse_outcome/outcome_to_pydicthelpers are now exercised from both the sync and async paths, so the structured-result schema stays identical for callers migrating to async.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com