refactor: extract shared retain_background_task helper for fire-and-forget tasks - #303
Conversation
…orget tasks navigator_client.py's _schedule_close (#300) and mcp_server.py's close_runners_sync (#302) each independently fixed the identical unretained-fire-and-forget-task GC hazard by hand-rolling the same "add to a module-level set, discard via a done-callback" pattern (their own comments already cross-reference each other for the exact same hazard). overlay.py's _on_navigation guarded the same hazard with an instance-level set and an extra logging callback. Extracted retain_background_task(pending, task) into utils.py, alongside this repo's other small shared helpers (resolve_optional_method, elapsed_ms, now_ms). All three call sites now delegate to it instead of duplicating the add+discard logic; overlay.py still chains its own extra completion callback afterward. Pure extraction — same set object, same task, same discard-on-completion timing — so the existing regression tests asserting on `module._pending_close_tasks` pass unchanged. Added direct unit tests for the new helper. Verified: full suite 326 passed / 21 pre-existing environment-only Chromium-sandbox failures (unchanged baseline, was 324/21 before this PR's 2 new tests); ruff check/format --check clean on touched files. Co-authored-by: Claude <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds ChangesBackground task retention
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to This PR centralizes existing background-task retention logic without changing call-site behavior or APIs; no actionable merge-blocking risk remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
What
navigator_client.py's_schedule_close(#300) andmcp_server.py'sclose_runners_sync(#302) each independently fixed the identical unretained-fire-and-forget-task GC hazard, but each one hand-rolled the same "add task to a module-levelset, remove it via adone_callback" pattern — their own comments already cross-reference each other as guarding "the identical hazard."overlay.py's_on_navigationguards the same hazard a third time with an instance-level set plus an extra logging callback.This PR extracts
retain_background_task(pending, task)intoutils.py— home to this repo's other small shared helpers (resolve_optional_method,elapsed_ms,now_ms) — and rewires all three call sites onto it instead of duplicating the add+discard logic.overlay.pystill chains its own extra completion callback after the shared call.Why it's safe
pending.add(task); task.add_done_callback(pending.discard); return task— byte-identical to what each site did inline._pending_close_tasks/_navigation_tasksset object; only the add+discard mechanics moved, so existing regression tests that assert directly onmodule._pending_close_tasks(added by fix(navigator_client): hold a strong reference to scheduled close() background tasks #300/fix(mcp_server): hold a strong reference to close_runners_sync's background close task #302) pass unchanged.test_retain_background_task_holds_reference_until_completion,test_retain_background_task_returns_the_same_task).Verification
uv run pytest tests/→ 326 passed / 21 failed (the documented pre-existing environment-only Chromium-sandbox failures — sandbox has no Chromium binary; same 21 test names as agit stash-backed baseline run). 326 vs. the baseline's 324 is exactly this PR's 2 new tests.uv run ruff check src/ tests/→ clean.uv run ruff format --checkon all 5 touched files → all already formatted.tests/test_navigator_client.py,tests/test_mcp_server.py,tests/test_overlay.py,tests/test_utils.py→ 91 passed.Only open PR (#148) is unrelated pre-existing human feature work — untouched by this change.
Generated by Claude Code
Note
Low Risk
Behavior-preserving refactor with no API or shutdown-path logic changes beyond centralizing identical task-retention code.
Overview
Introduces
retain_background_taskinutils.pyto keep a strong reference on fire-and-forgetasyncio.Taskinstances until they finish, avoiding premature GC when only the event loop holds a weak reference.mcp_server.close_runners_sync,navigator_client._schedule_close, andoverlay._on_navigationnow call this helper instead of duplicatingpending.add(task)plustask.add_done_callback(pending.discard). Each site still uses its own pending set; overlay continues to attach its navigation-restore logging callback on the returned task. Inline comments were shortened to point at the helper docstring.Adds two unit tests in
tests/test_utils.pythat assert the task stays in the set until completion and that the same task object is returned.Reviewed by Cursor Bugbot for commit e3261d6. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
Reliability Improvements
Tests