screen-locker/screen_locker/_degraded_sources.py
2026-08-27 21:32:39 +02:00

51 lines
1.6 KiB
Python

"""Which workout backends could not be read on this run, and why.
Split out of :mod:`screen_locker._sync_client` to keep every file under the
250-line cap. Re-exported from there, so existing importers and their patch
targets are unchanged.
"""
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class DegradedSource:
"""A workout backend that could not be read on this run.
``reason`` is the backend's own error text, kept verbatim so the status
view and the lock decision can quote something actionable rather than a
generic "sync problem".
"""
name: str
reason: str
# Process-scoped rather than persisted: it describes THIS run's ability to
# read, and a stale marker from an earlier run would be worse than none. The
# lock chain and the pull both happen in one process, so this survives exactly
# as long as it is true.
_degraded: list[DegradedSource] = []
def degraded_sources() -> list[DegradedSource]:
"""Return the backends that failed to answer during this run."""
return list(_degraded)
def clear_degraded_sources() -> None:
"""Forget recorded failures (called at the start of a fresh check)."""
_degraded.clear()
def _record_degraded(name: str, reason: str) -> None:
"""Note that *name* could not be read, so callers can stop guessing.
Logging alone was not enough: on 2026-08-24 the warning was emitted, the
Firebase read was skipped, and the lock decision still reported
"0 workouts this week" as though the source had answered "none".
"""
_degraded.append(DegradedSource(name, reason))