Enabled all Ruff lint rules and resolved findings with justified inline suppressions.
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 5s
CI / Tests (Python 3.12) (push) Successful in 14s
CI / Tests (Python 3.13) (push) Successful in 14s
CI / Tests (Python 3.14) (push) Successful in 11s
CI / Type Checking (push) Successful in 10s
CI / Spelling (push) Successful in 5s

This commit is contained in:
2026-04-13 15:31:06 -04:00
parent b68c717845
commit 0ff3c7a6b4
44 changed files with 452 additions and 430 deletions
+8 -7
View File
@@ -36,6 +36,7 @@ from .types import (
MAX_QUESTION_LENGTH,
MIN_DURATION,
MIN_OPTIONS,
REMINDER_THRESHOLD,
RESULT_EXPIRY,
STREAM_GRACE_PERIOD,
ActivePoll,
@@ -256,10 +257,10 @@ class PollManager:
# so clearing the creation reference has no effect on it.
self.cancel_creation_token()
# Start auto-end timer task with 60-second reminder.
# Start auto-end timer task with reminder before expiry.
async def _poll_timer() -> None:
if duration > 60:
await asyncio.sleep(duration - 60)
if duration > REMINDER_THRESHOLD:
await asyncio.sleep(duration - REMINDER_THRESHOLD)
if poll.allow_multiple:
instructions = "Use <strong>!vote</strong> to get a voting link."
else:
@@ -269,13 +270,13 @@ class PollManager:
escaped_question = escape(poll.question)
reminder = (
f"<strong>60 seconds remaining:</strong>"
f"<strong>{REMINDER_THRESHOLD} seconds remaining:</strong>"
f" {escaped_question}<br>{instructions}"
)
await self._ctx.owncast_client.send_system_message(
reminder, unsanitized=True
)
await asyncio.sleep(60)
await asyncio.sleep(REMINDER_THRESHOLD)
else:
await asyncio.sleep(duration)
await self.end()
@@ -283,7 +284,7 @@ class PollManager:
poll.timer_task = asyncio.create_task(_poll_timer())
# Announce in chat.
if duration >= 60:
if duration >= REMINDER_THRESHOLD:
minutes = duration // 60
time_str = f"{minutes} minute{'s' if minutes != 1 else ''}"
else:
@@ -692,5 +693,5 @@ def get_manager(ctx: ModuleContext) -> PollManager:
"""
manager = ctx.state.get("manager")
if not isinstance(manager, PollManager):
raise RuntimeError("PollManager is not initialized.")
raise RuntimeError("PollManager is not initialized.") # noqa: TRY004 # state error, not a type error
return manager
+4 -4
View File
@@ -339,7 +339,7 @@ async def events_stream(ctx: RouteContext) -> web.StreamResponse | web.Response:
remaining = poll.time_remaining
payload = sse_payload(SSEEvent.KEEPALIVE, {"time_remaining": remaining})
await response.write(payload)
except Exception:
except Exception: # noqa: BLE001 # SSE disconnect; no single exception covers all transport failures
ctx.logger.debug("SSE keepalive failed for %s...", token[:8])
break
finally:
@@ -439,7 +439,7 @@ async def results_page(ctx: RouteContext) -> web.Response:
@on_route("/static/polls.css", methods=["GET"])
async def static_polls_css(ctx: RouteContext) -> web.StreamResponse:
async def static_polls_css(ctx: RouteContext) -> web.StreamResponse: # noqa: ARG001 # required by route handler signature
"""Serve the polls stylesheet.
:param ctx: The route context.
@@ -452,7 +452,7 @@ async def static_polls_css(ctx: RouteContext) -> web.StreamResponse:
@on_route("/static/polls.js", methods=["GET"])
async def static_polls_js(ctx: RouteContext) -> web.StreamResponse:
async def static_polls_js(ctx: RouteContext) -> web.StreamResponse: # noqa: ARG001 # required by route handler signature
"""Serve the polls JavaScript file.
:param ctx: The route context.
@@ -465,7 +465,7 @@ async def static_polls_js(ctx: RouteContext) -> web.StreamResponse:
@on_route("/static/create.js", methods=["GET"])
async def static_create_js(ctx: RouteContext) -> web.StreamResponse:
async def static_create_js(ctx: RouteContext) -> web.StreamResponse: # noqa: ARG001 # required by route handler signature
"""Serve the create form JavaScript file.
:param ctx: The route context.
+1
View File
@@ -38,6 +38,7 @@ MAX_OPTIONS = 10
DEFAULT_DURATION = 240
MIN_DURATION = 30
MAX_DURATION = 600
REMINDER_THRESHOLD = 60
CREATION_TOKEN_TIMEOUT = 15 * 60
STREAM_GRACE_PERIOD = 5 * 60
RESULT_EXPIRY = 3600