krz/nba-scores
A CLI/TUI app for NBA scores.
clone: git clone https://gitbay.org/krz/nba-scores.git
953307b17732f8988ae099932eb2d7f4ec70404e
verified · cmc
author: Christian Cleberg <hello@cleberg.net> · 2026-08-22T23:52:06Z
.github/workflows/pylint.yml | 5 +++++ nba/box_score.py | 2 +- nba/bracket.py | 13 ++++++++----- nba/cli.py | 1 + nba/fetch_data.py | 1 + nba/leaders.py | 2 +- nba/playoff.py | 2 +- nba/tui/app.py | 8 +++++--- pyproject.toml | 20 ++++++++++++++++++++ 9 files changed, 43 insertions(+), 11 deletions(-) @@ -19,8 +19,13 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt + # Pinned. Unpinned, this installs whatever ruff shipped this week, and a + # release that adds a rule turns the build red with no change to the code — + # which is what happened between May and August 2026. - name: Install Ruff uses: astral-sh/ruff-action@v3.2.2 + with: + version: "0.16.4" - name: Ruff Actions run: | ruff check --fix @@ -4,8 +4,8 @@ Fetches and formats live box scores for individual games. import json -from tabulate import tabulate from nba_api.live.nba.endpoints.boxscore import BoxScore +from tabulate import tabulate BOLD = "\033[1m" END = "\033[0m" @@ -2,10 +2,11 @@ Fetches and formats the NBA playoff bracket. """ +from __future__ import annotations + import json import re from collections import defaultdict -from typing import Optional from nba_api.stats.endpoints.commonplayoffseries import CommonPlayoffSeries from nba_api.stats.endpoints.leaguegamelog import LeagueGameLog @@ -59,8 +60,10 @@ def get_bracket_table(data: dict) -> str: return "\n".join( [ _center(f"{BOLD}NBA Playoff Bracket{END}", 74), - f"{GREEN}* Advanced{END} {RED}x Eliminated{END} " - f"{CYAN}> Series lead{END} {YELLOW}! Can clinch next win{END}", + ( + f"{GREEN}* Advanced{END} {RED}x Eliminated{END} " + f"{CYAN}> Series lead{END} {YELLOW}! Can clinch next win{END}" + ), "", _render_conference("Western", summaries), "", @@ -205,11 +208,11 @@ def _conference_round(summaries: dict, conference: str, round_number: int) -> li ) -def _slot(items: list, idx: int) -> Optional[dict]: +def _slot(items: list, idx: int) -> dict | None: return items[idx] if idx < len(items) else None -def _series_box(summary: Optional[dict], width: int) -> str: +def _series_box(summary: dict | None, width: int) -> str: if summary is None: return _blank(width, "TBD") @@ -5,6 +5,7 @@ It imports the required modules and sets up a parser with basic options for demo """ import argparse + from nba import fetch_data, scores, standings @@ -3,6 +3,7 @@ Fetches data for use in other modules. """ import json + from nba_api.live.nba.endpoints import scoreboard from nba_api.stats.endpoints import leaguestandings @@ -4,8 +4,8 @@ Fetches and formats NBA statistical leaders. import json -from tabulate import tabulate from nba_api.stats.endpoints.leagueleaders import LeagueLeaders +from tabulate import tabulate BOLD = "\033[1m" END = "\033[0m" @@ -4,8 +4,8 @@ Fetches and formats the NBA playoff picture. import json -from tabulate import tabulate from nba_api.stats.endpoints.playoffpicture import PlayoffPicture +from tabulate import tabulate BOLD = "\033[1m" END = "\033[0m" @@ -6,17 +6,19 @@ from __future__ import annotations import asyncio from pathlib import Path +from typing import ClassVar from rich.text import Text from textual.app import App, ComposeResult +from textual.binding import BindingType from textual.containers import Horizontal from textual.widgets import Footer, Header, Static, TabbedContent, TabPane -from nba import fetch_data +from nba import box_score as box_score_mod from nba import bracket as bracket_mod +from nba import fetch_data from nba import leaders as leaders_mod from nba import playoff as playoff_mod -from nba import box_score as box_score_mod from nba.scores import get_scoreboard_table from nba.standings import get_east_standings_table, get_west_standings_table from nba.tui.widgets import CountdownBar, ScoresWidget @@ -27,7 +29,7 @@ class NBAApp(App): CSS_PATH = Path(__file__).parent / "styles.tcss" - BINDINGS = [ + BINDINGS: ClassVar[list[BindingType]] = [ ("q", "quit", "Quit"), ("s", "show_scores", "Scores"), ("t", "show_standings", "Standings"), @@ -39,3 +39,23 @@ include = ["nba*"] [tool.setuptools.package-data] "nba.tui" = ["*.tcss"] + +[tool.ruff.lint] +# ruff's defaults widen with each release, and the workflow installs whatever is +# newest — which is how this repo went from green in May to sixteen findings in +# August without a line of its own code changing. The rules below are the two +# that fight the code rather than improve it; everything else ruff flags is +# treated as worth fixing. +ignore = [ + # Blind `except Exception`. Deliberate here: a duration parser that falls back + # to the raw string, and TUI handlers that render "Error loading …" instead of + # taking the whole app down. Narrowing them would make the app crash on the + # cases they exist to absorb. + "BLE001", +] + +[tool.ruff.lint.per-file-ignores] +# Textual's compose() nests `with` blocks because the nesting *is* the widget +# tree. Collapsing them into a single `with A, B:` would flatten a hierarchy +# that is meant to be read as one. +"nba/tui/app.py" = ["SIM117"]