pylint --fail-under=10 everywhere, and clear what…
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
leetcode-coach pre-commit / pre-commit (push) Waiting to run
leetcode-coach tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run

Todo: fda71ed1
This commit is contained in:
Krzysztof Rudnicki 2026-11-08 20:55:09 +01:00
parent f71b0532fd
commit 46ab26fa15
4 changed files with 37 additions and 17 deletions

View File

@ -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

View File

@ -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()

View File

@ -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]

View File

@ -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