ruff gains RUF100, PLW1510 and flake8-bugbear, every hit fixed rather than ignored. B008 stays on with FastAPI's Depends/Query declared immutable. basedpyright moves to standard mode, pytest to --strict-markers, and a hand run of pytest now requires the database unless SIFTLODE_TEST_REQUIRE_DB=0. PLW1510 found a real bug: the three ffprobe probes in plex/stream.py never looked at the exit status, so a probe that failed was read as the file's truth (an HDR source sent down the HW-safe path, a multi-audio file collapsed to one track, a subtitle offset taken from garbage) and the warning each except branch promises never fired. They now run with check=True.
57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
"""Lightweight, decoupled progress reporting for long-running scheduler jobs.
|
|
|
|
A job's work function calls `report(current, total, phase)`; the scheduler binds a sink (via
|
|
`bind()`) that forwards it into the in-memory activity the admin dashboard polls. A contextvar
|
|
holds the sink, so the sync code stays unaware of the scheduler and concurrent jobs (each in
|
|
its own thread/context) never cross wires. When no sink is bound (e.g. a manual sync endpoint),
|
|
`report` is a cheap no-op.
|
|
"""
|
|
|
|
import contextvars
|
|
from typing import Callable, Literal, Optional
|
|
|
|
Sink = Callable[[int, Optional[int], Optional[str]], None]
|
|
|
|
_sink: contextvars.ContextVar[Optional[Sink]] = contextvars.ContextVar(
|
|
"progress_sink", default=None
|
|
)
|
|
|
|
|
|
def report(current: int, total: int | None = None, phase: str | None = None) -> None:
|
|
"""Report job progress to the bound sink, if any. `total=None` means indeterminate."""
|
|
sink = _sink.get()
|
|
if sink is not None:
|
|
sink(current, total, phase)
|
|
|
|
|
|
class JobFailed(RuntimeError):
|
|
"""A job's own failure, said so the reader's language can name it (E31): the client reads
|
|
`scheduler.failure.<reason>` and interpolates `params` (keep them to values the reader may see —
|
|
numbers, ids, short codes). The message is the English line the log and `last_error` keep. An
|
|
upstream's own error (a timeout, a refused connection) is not one of these: it stays as raised,
|
|
being diagnostic."""
|
|
|
|
def __init__(self, reason: str, message: str, **params: object):
|
|
super().__init__(message)
|
|
self.reason = reason
|
|
self.params = params
|
|
|
|
|
|
class bind:
|
|
"""Context manager that binds a progress sink for the duration of a job run."""
|
|
|
|
def __init__(self, sink: Sink):
|
|
self._sink = sink
|
|
self._token = None
|
|
|
|
def __enter__(self) -> "bind":
|
|
self._token = _sink.set(self._sink)
|
|
return self
|
|
|
|
# Literal[False], not bool: this never swallows the job's exception, and saying so is what lets
|
|
# a type checker know a name assigned inside `with bind(...)` is bound after it.
|
|
def __exit__(self, *exc) -> Literal[False]:
|
|
if self._token is not None:
|
|
_sink.reset(self._token)
|
|
return False
|