Skip to content

feat(python): AsyncDaemon with async status() (partial #65)#78

Merged
zackees merged 1 commit intomainfrom
feat/issue-65-async-daemon
Apr 17, 2026
Merged

feat(python): AsyncDaemon with async status() (partial #65)#78
zackees merged 1 commit intomainfrom
feat/issue-65-async-daemon

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented Apr 17, 2026

Summary

Next incremental step on #65 (native async Python API). Adds `AsyncDaemon` as a counterpart to the sync `Daemon` class, mirroring the `AsyncSerialMonitor` pattern from PR #73.

Changes

  • `AsyncDaemon` is purely additive — the sync `Daemon` class keeps all its methods and behavior.
  • Exposes `async status()` as the first method. Like `reset_device` on `AsyncSerialMonitor`, `status` is stateless HTTP so it ports without needing the cross-await state refactor.

Follow-up (tracked in #65)

  • `async ensure_running()`, `async stop()` are straightforward next steps (same HTTP-only pattern).
  • The WebSocket-backed methods (`AsyncSerialMonitor.read_lines`, `write`, `write_json_rpc`, `aenter`/`aexit`) still need the shared WebSocket state refactored to be `Send + Sync` across `await` points before they can be exposed.

Test plan

  • `cargo check -p fbuild-python` clean
  • `cargo clippy --workspace --all-targets -- -D warnings` clean
  • `cargo fmt --all -- --check` clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added async daemon status checking with HTTP connectivity and automatic error handling for connection failures and timeouts.

Next incremental step on #65 (native async API). Adds AsyncDaemon as a
counterpart to the sync Daemon class, mirroring the AsyncSerialMonitor
pattern from PR #73.

- AsyncDaemon is purely additive — the sync Daemon class keeps all
  its methods and behavior.
- Exposes async status() as the first method. Like reset_device on
  AsyncSerialMonitor, status is stateless HTTP so it ports without
  needing the cross-await state refactor.

Future work on #65: async ensure_running(), async stop(), and then the
WebSocket-backed AsyncSerialMonitor methods once the shared state is
Send + Sync across await points.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a10399e-fe81-4d60-916c-fbbed9088f4d

📥 Commits

Reviewing files that changed from the base of the PR and between 4f153d0 and 8dbdd03.

📒 Files selected for processing (1)
  • crates/fbuild-python/src/lib.rs

📝 Walkthrough

Walkthrough

A new Python-exposed Rust class AsyncDaemon was introduced with a static async method status that performs HTTP GET requests to a daemon info endpoint with a 10-second timeout, converting HTTP and JSON errors into Python exceptions.

Changes

Cohort / File(s) Summary
Async Daemon Status API
crates/fbuild-python/src/lib.rs
Added AsyncDaemon PyO3 class with status() static async method; performs HTTP GET to daemon API with 10s timeout; handles connection/send errors as PyConnectionError, response read/JSON parsing errors as PyRuntimeError; registered in module initializer.

Sequence Diagram(s)

sequenceDiagram
    participant Python as Python Client
    participant Async as AsyncDaemon
    participant Tokio as Tokio Runtime
    participant HTTP as HTTP Client
    participant Daemon as Daemon API
    
    Python->>Async: status(py)
    activate Async
    Async->>Tokio: spawn async task
    activate Tokio
    Tokio->>HTTP: GET /api/daemon/info
    activate HTTP
    HTTP->>Daemon: send request
    activate Daemon
    Daemon-->>HTTP: response
    deactivate Daemon
    HTTP-->>Tokio: response body
    deactivate HTTP
    Tokio->>Tokio: parse JSON
    Tokio-->>Async: future result
    deactivate Tokio
    Async->>Async: convert to Python
    Async-->>Python: PyResult<Bound<PyAny>>
    deactivate Async
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 Async daemons hopping fast,
HTTP requests unsurpassed,
Timeouts ticking, responses flow,
Python and Rust together glow!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(python): AsyncDaemon with async status() (partial #65)' clearly and concisely summarizes the main change: adding a new AsyncDaemon class with an async status() method. It accurately reflects the primary additions shown in the changeset and follows conventional commit formatting.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-65-async-daemon

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zackees zackees merged commit f2d1590 into main Apr 17, 2026
76 of 79 checks passed
zackees added a commit that referenced this pull request Apr 18, 2026
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>
zackees added a commit that referenced this pull request Apr 18, 2026
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>
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.

1 participant