refactor(jobs): lift watch_cmd WS per-type decoding into a dispatch table (BE-2839)#505
Conversation
…able (BE-2839) Extract the 5-way if/elif on msg type in watch_cmd into module-level per-type handlers dispatched via _WATCH_HANDLERS over an explicit _WatchState. The connect/recv-loop/timeout-reconnect/cancel and terminal-detection state machine stays in watch_cmd unchanged; handlers share loop-local state (completed_nodes, outputs, end_reason, end_details) through _WatchState and signal loop termination via state.terminal. Behavior-preserving: adds focused unit tests for each handler and the dispatch table; existing jobs/watch tests pass unchanged.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
Found 3 finding(s).
| Severity | Count |
|---|---|
| 🟡 Medium | 1 |
| 🟢 Low | 2 |
Panel: 6/8 reviewers contributed findings.
Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)
Address review findings on the dispatch-table refactor: - Guard the handler lookup with an isinstance(str) check: the dispatch table introduced a TypeError when a server sends an unhashable JSON array/object as the message ``type`` (dict.get on an unhashable key). The old if/elif chain tolerated this; unknown types now fall through and are ignored as before. - Escape the server-controlled ``node`` id before printing it into the Rich markup string so it can't inject style/markup tags into pretty console output. The raw id is still carried on the event stream. Add tests for the escape behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ELI-5
comfy jobs watchlistens to a live WebSocket and, for each message, does oneof ~5 things depending on the message
type(executing / cached / progress /executed / error). That decision lived as one big nested
if/elifchain insidean already-long function. This PR moves only that per-message decoding into
small, individually-testable handler functions looked up from a dispatch table
(
_WATCH_HANDLERS), each operating on an explicit_WatchState. Nothing aboutwhen or how we connect, time out, reconnect, or decide the job finished
changed — that state machine stays exactly where it was.
What changed
_WatchState(a@dataclass) holding the per-watch context(
renderer,prompt_id,host,port) plus the mutable accumulators theloop and handlers share (
completed_nodes,outputs,end_reason,end_details) and aterminalflag handlers set to ask the recv loop tobreak.
(
_watch_executing,_watch_execution_cached,_watch_progress,_watch_executed,_watch_execution_error) and a_WATCH_HANDLERStype → handlertable.watch_cmdnow dispatches via the table and breaks whenstate.terminalisset. The WS connect,
while Truerecv loop, per-recv timeout / snapshotreconnect (
missing_deadline,saw_any_event), cancellation handling, andthe post-loop terminal-status/payload rendering are unchanged — they just
read/write through
state.Why it's safe (behavior-preserving)
executingwith a nullnodeandexecution_errorset the terminal reason and emit nothingbefore the loop breaks, identical to the original.
saw_any_eventis still set for any prompt-matched message (includingunrecognized types); unknown types are still silently skipped (dict lookup
miss ⇒ no handler).
state, and the post-loopfinal_status/payload logic reads the samevalues.
Tests
(
tests/comfy_cli/jobs/test_jobs.py) — the WS loop previously had no directunit coverage, so extracting the pure decoders let me lock their behavior.
2360 passed, 12 skipped.ruff check/ruff format --checkclean. No existing test modified.Scope / judgment calls
per-type decoding (as instructed) and left the connect/loop/terminal-detection
state machine in place.
_WatchStateintentionally carries the immutable render context alongside themutable accumulators so handler signatures stay a clean
(state, data); areviewer could prefer a separate context object — flagging as a judgment call.
it adds no capability-denying / dead-end path.