From 46ab26fa1581afcaa79a652a327ff42add623494 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sun, 8 Nov 2026 20:55:09 +0100 Subject: [PATCH] =?UTF-8?q?pylint=20--fail-under=3D10=20everywhere,=20and?= =?UTF-8?q?=20clear=20what=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Todo: fda71ed1 --- leetcode_coach/.pre-commit-config.yaml | 6 ++++-- leetcode_coach/leetcode_coach/relay/phone.py | 22 +++++++++++++------- leetcode_coach/leetcode_coach/shape.py | 22 ++++++++++++++------ leetcode_coach/requirements.txt | 4 ++-- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/leetcode_coach/.pre-commit-config.yaml b/leetcode_coach/.pre-commit-config.yaml index 190bab2..0d45cce 100644 --- a/leetcode_coach/.pre-commit-config.yaml +++ b/leetcode_coach/.pre-commit-config.yaml @@ -112,8 +112,10 @@ repos: - id: pylint args: - --rcfile=leetcode_coach/pyproject.toml - - --fail-under=8.0 - - --jobs=4 + - --fail-under=10 + # --jobs=1: pylint's parallel mode pickles its state with dill, + # which crashes on Python 3.15 (no code.co_lnotab). + - --jobs=1 - leetcode_coach/leetcode_coach # Whole package, never the staged subset: a score over an arbitrary # handful of files is an arbitrary number. Kept in step with diff --git a/leetcode_coach/leetcode_coach/relay/phone.py b/leetcode_coach/leetcode_coach/relay/phone.py index 11869a0..ef7ae88 100644 --- a/leetcode_coach/leetcode_coach/relay/phone.py +++ b/leetcode_coach/leetcode_coach/relay/phone.py @@ -15,6 +15,7 @@ write is logged and retried in order; the relay never exits over it. from __future__ import annotations +import dataclasses import functools import json import logging @@ -54,6 +55,14 @@ def _pairs(event: sse.Event) -> list[tuple[list[str], object]]: return [(base, event.data)] +@dataclasses.dataclass +class _ChatMirror: + """Which session's chat RTDB holds, and the line keys already sent.""" + + sid: str | None = None + sent: set[str] = dataclasses.field(default_factory=set) + + class PhoneLink: """Mirrors the relay into RTDB and feeds RTDB requests to the lane.""" @@ -78,15 +87,14 @@ class PhoneLink: self._rescan_every = rescan_s self.outbox = Outbox() self._mirror_lock = threading.Lock() - self._chat_sid: str | None = None - self._sent: set[str] = set() + self._chat = _ChatMirror() def attach(self) -> None: """Listen to the lane and the backend; then mirror everything once.""" self.lane.on_result.append(self.on_result) self.lane.on_busy.append(lambda _busy: self.beat()) self.coach.listeners.append(self.on_state) - self._chat_sid = None + self._chat.sid = None self.on_state(self.coach.state(), self.coach.session) def beat(self) -> None: @@ -111,18 +119,18 @@ class PhoneLink: text = json.dumps(state, ensure_ascii=False) with self._mirror_lock: sid = None if session is None else session.sid - if sid != self._chat_sid: + if sid != self._chat.sid: self.outbox.put( "chat reset", functools.partial(self.rtdb.delete, "chat") ) - self._chat_sid, self._sent = sid, set() + self._chat = _ChatMirror(sid) lines = {} if session is None else dict(session.chat) new: dict[str, object] = { - k: v for k, v in lines.items() if k not in self._sent + k: v for k, v in lines.items() if k not in self._chat.sent } if new: self.outbox.put("chat", functools.partial(self.rtdb.patch, "chat", new)) - self._sent |= set(new) + self._chat.sent |= set(new) put = functools.partial(self.rtdb.put, "state", text) self.outbox.put("state", put, latest="state") self.beat() diff --git a/leetcode_coach/leetcode_coach/shape.py b/leetcode_coach/leetcode_coach/shape.py index d1595e3..92334f3 100644 --- a/leetcode_coach/leetcode_coach/shape.py +++ b/leetcode_coach/leetcode_coach/shape.py @@ -8,10 +8,18 @@ mechanically (no model, nothing to fail open), and the verdict rides the same from __future__ import annotations import re -from typing import Final +from typing import Final, NamedTuple from leetcode_coach import req_check + +class Facet(NamedTuple): + """One part of an output shape: how to spot it, and what to ask for.""" + + pattern: re.Pattern[str] + hint: str + + VIA: Final = "shape" SHAPE_RE: Final = re.compile(r"^\s*shape\s*:", re.IGNORECASE) SQL_LANGS: Final = frozenset( @@ -20,15 +28,15 @@ SQL_LANGS: Final = frozenset( LANGUAGES_RE: Final = re.compile(r"^Languages available:(.*)$", re.MULTILINE) TAGS_RE: Final = re.compile(r"^Tags:(.*)$", re.MULTILINE) FACETS: Final = { - "columns": ( + "columns": Facet( re.compile(r"\b(?:columns?|fields?|headers?|named)\b", re.IGNORECASE), "which columns come back, named how", ), - "rows": ( + "rows": Facet( re.compile(r"\b(?:rows?|records?|per)\b", re.IGNORECASE), "what one row stands for (one row per ...)", ), - "grouping": ( + "grouping": Facet( re.compile( r"\b(?:group\w*|aggregat\w*|distinct|unique|per|sum|count|ungrouped)\b", re.IGNORECASE, @@ -49,7 +57,9 @@ def is_sql(statement: str) -> bool: def missing_facets(text: str) -> tuple[str, ...]: - return tuple(name for name, (found, _) in FACETS.items() if not found.search(text)) + return tuple( + name for name, facet in FACETS.items() if not facet.pattern.search(text) + ) def check(text: str) -> req_check.Check: @@ -58,4 +68,4 @@ def check(text: str) -> req_check.Check: def hints(missing: tuple[str, ...] | list[str]) -> list[str]: - return [f"{name}: {FACETS[name][1]}" for name in missing if name in FACETS] + return [f"{name}: {FACETS[name].hint}" for name in missing if name in FACETS] diff --git a/leetcode_coach/requirements.txt b/leetcode_coach/requirements.txt index fbbe0d5..e7e0802 100644 --- a/leetcode_coach/requirements.txt +++ b/leetcode_coach/requirements.txt @@ -5,7 +5,7 @@ # none of them at import time. bandit[toml]==1.9.4 codespell==2.4.3 -coverage==7.17.0 +coverage==7.17.2 crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync genanki==0.13.1 mypy==2.4.1 @@ -16,5 +16,5 @@ pytest-cov==7.1.0 pytest-randomly==5.0.0 pytest-xdist==3.8.0 requests==2.34.2 -ruff==0.17.3 +ruff==0.17.4 types-requests==2.33.0.20261030