Skip to content

refactor(macos): extract the fail-soft degrade-and-return pattern in presentation.py - #373

Merged
dhruvbatra merged 1 commit into
mainfrom
claude/cool-brahmagupta-cqkcox
Sep 4, 2026
Merged

refactor(macos): extract the fail-soft degrade-and-return pattern in presentation.py#373
dhruvbatra merged 1 commit into
mainfrom
claude/cool-brahmagupta-cqkcox

Conversation

@dhruvbatra

@dhruvbatra dhruvbatra commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What

MacOSPresentationController.show_thumbnail, .before_capture, .after_capture, and .encode_observation each wrapped their entire body in an identical shape:

try:
    ...
    return <success value>
except Exception as error:  # noqa: BLE001 - presentation is fail-soft
    await self._degrade(reason, error)
    return <default value>

This pulls that shape into a single _fail_soft(reason, default) decorator (module-level, functools.wraps-preserving) so each of the four methods keeps only its distinct body plus its own reason/default. Net: four duplicated try/except blocks become one shared helper, applied via decorator.

Why this is safe / no public behavior change

  • Purely an internal implementation detail of yutori/navigator/macos/presentation.py — no function signatures, return types, or observable behavior change. functools.wraps keeps __name__/__doc__/introspection intact.
  • The decorator wraps the entire method body, including the existing early-return guard clauses — since those don't raise, they execute identically to before (no try was ever wrapped around them either way).
  • self._degrade is still resolved dynamically via self at call time, so existing tests that monkeypatch controller._degrade on the instance continue to work unchanged.
  • present and _present_status were deliberately left alone — they interleave multiple branches and must re-raise asyncio.CancelledError before degrading, so they don't fit this single-exit shape and keep their own inline try/except.
  • I did not touch the two methods that have that different CancelledError-first shape (present, _present_status), and did not touch any other file.

What I verified

  • ruff check . and ruff format --check pass on the changed file (repo-wide ruff format --check reports 13 pre-existing unrelated files with drift, none of which is this one — confirmed via git diff that only presentation.py changed).
  • pytest tests/test_navigator_macos_presentation.py tests/test_navigator_macos_computer.py -v — 109/109 pass, including the existing fail-soft/degrade coverage (test_capture_ids_are_monotonic_and_a_stale_transition_degrades, test_status_mode_thumbnail_rejection_degrades_fail_soft, etc.) unmodified.
  • Full suite: pytest -m "not slow" — 862 passed, 9 skipped (pre-existing), 1 deselected (slow marker).

🤖 Generated with Claude Code

https://claude.ai/code/session_01F9iAjBxPM94GjhCENMjXP8


Generated by Claude Code


Note

Low Risk
Internal refactor in one file with no signature or observable behavior change; existing presentation fail-soft tests cover the same paths.

Overview
Introduces a module-level _fail_soft(reason, default) async decorator that centralizes the shared “catch any exception → await self._degrade(reason, error) → return default” pattern used by macOS presentation operations.

show_thumbnail, before_capture, after_capture, and encode_observation now use the decorator instead of four copy-pasted try/except blocks; each method keeps its guard clauses and success-path logic unchanged. present and _present_status are intentionally unchanged because they must re-raise asyncio.CancelledError before degrading.

Reviewed by Cursor Bugbot for commit 924b13e. Bugbot is set up for automated code reviews on this repo. Configure here.

…presentation.py

show_thumbnail, before_capture, after_capture, and encode_observation each
wrapped their body in an identical try/except Exception: await
self._degrade(reason, error); return default. Pull that shape into a shared
_fail_soft(reason, default) decorator so the four methods keep only their
distinct bodies and reason/default values.

No public behavior change: MacOSPresentationController's signatures and
return values are unchanged, and the existing presentation/computer test
suites (109 tests) plus the full non-slow suite (862 tests) pass unmodified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9iAjBxPM94GjhCENMjXP8
@dhruvbatra
dhruvbatra merged commit 14d9117 into main Sep 4, 2026
16 checks passed
@dhruvbatra
dhruvbatra deleted the claude/cool-brahmagupta-cqkcox branch September 4, 2026 05:02
dhruvbatra added a commit that referenced this pull request Sep 4, 2026
…_present_status (#378)

present and _present_status each wrapped their body in the identical
except asyncio.CancelledError: raise / except Exception as error: await
self._degrade(f"presentation_failed:{type(error).__name__}", error) shape.
Extract a shared _fail_soft_cancellable decorator (the cancellation-aware
counterpart to the existing _fail_soft decorator from #373) so both methods
keep only their distinct dispatch bodies.

No behavior change: the try/except previously wrapped the code below the
early-return guards in present, and those guards cannot raise, so moving
them inside the decorated body is equivalent. Full suite (914 passed / 3
skipped / 1 deselected) and ruff check/format pass unmodified.


Claude-Session: https://claude.ai/code/session_01NoGweAuMDdcoLXYHWgfY3j

Co-authored-by: Claude <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.

2 participants