refactor: narrow the exception handlers this branch introduced

Codacy flagged the new code; it passes on other recent PRs, so the finding is
mine. Four of the five broad `except Exception` clauses I added were catching
far more than they needed to, which is the same shape as several bugs this
branch fixes -- hello-world's TypeError sat invisible for exactly this reason.

  freezer() / move_to() / tick()   -> (AttributeError, TypeError, ValueError)
  cache_manager.delete()           -> (OSError, AttributeError, KeyError)

The fifth stays broad and now says why: it wraps a call into a plugin's own
display(), which can raise anything, and the first frame has already rendered --
so a failure there must not turn a good result into an error.

Verified against a checkout of main: f1-scoreboard 48 PASS / 0 FAIL with 0 empty
warnings, on-air keeps its 8 true positives, clock-simple 8 PASS. geochron shows
7 golden drifts both before and after this branch, so it is not from these
changes -- its committed goldens predate #521's 1-bit text rendering.
This commit is contained in:
ChuckBuilds
2026-09-05 17:25:24 -04:00
parent 31bd0cb40a
commit d9f6b110bc
2 changed files with 15 additions and 5 deletions
+3 -1
View File
@@ -1333,7 +1333,9 @@ class DisplayController:
# against double-processing if this delete fails. # against double-processing if this delete fails.
try: try:
self.cache_manager.delete('display_on_demand_request') self.cache_manager.delete('display_on_demand_request')
except Exception as err: # pragma: no cover - best-effort cleanup except (OSError, AttributeError, KeyError) as err:
# Best-effort: processed_id still guards against reprocessing if the
# mailbox cannot be cleared.
logger.debug("Could not clear the on-demand request mailbox: %s", err) logger.debug("Could not clear the on-demand request mailbox: %s", err)
if action == 'start': if action == 'start':
+12 -4
View File
@@ -222,7 +222,9 @@ def _settle_empty_frame(inst, mode, dm, result, freezer) -> None:
if freezer is not None: if freezer is not None:
try: try:
resume_at = freezer() resume_at = freezer()
except Exception: # noqa: BLE001 - only to restore, never load-bearing except (AttributeError, TypeError, ValueError):
# Not a freezegun factory, or a version whose factory is not
# callable. Only used to restore the clock, never load-bearing.
resume_at = None resume_at = None
try: try:
_settle_loop(inst, mode, dm, result, freezer) _settle_loop(inst, mode, dm, result, freezer)
@@ -230,7 +232,7 @@ def _settle_empty_frame(inst, mode, dm, result, freezer) -> None:
if resume_at is not None: if resume_at is not None:
try: try:
freezer.move_to(resume_at) freezer.move_to(resume_at)
except Exception: # noqa: BLE001 except (AttributeError, TypeError, ValueError):
pass pass
@@ -242,7 +244,9 @@ def _settle_loop(inst, mode, dm, result, freezer) -> None:
# number only since 1.x, and a stale pin would raise here. # number only since 1.x, and a stale pin would raise here.
try: try:
tick(timedelta(seconds=EMPTY_RECHECK_STEP)) tick(timedelta(seconds=EMPTY_RECHECK_STEP))
except Exception: # noqa: BLE001 - pacing is best-effort except (AttributeError, TypeError, ValueError):
# Pacing is best-effort; a freezegun that will not take a
# timedelta just means this probe runs without advancing time.
pass pass
else: else:
# No frozen clock, so the real one has to do the advancing. Without # No frozen clock, so the real one has to do the advancing. Without
@@ -252,7 +256,11 @@ def _settle_loop(inst, mode, dm, result, freezer) -> None:
time.sleep(EMPTY_RECHECK_STEP) time.sleep(EMPTY_RECHECK_STEP)
try: try:
result.display_returned = _render_mode_again(inst, mode) result.display_returned = _render_mode_again(inst, mode)
except Exception: # noqa: BLE001 - the first frame already succeeded except Exception: # noqa: BLE001
# Deliberately broad: this calls a plugin's display(), which can
# raise anything. The first frame already rendered, so whatever
# happens on a re-draw must not turn a good result into an error --
# keep the frame we have and stop probing.
return return
image = dm.get_image() image = dm.get_image()
if _has_content(image): if _has_content(image):