From 0999581fa0713b132b7635bc35a8316e04c7087e Mon Sep 17 00:00:00 2001 From: shaurya2k06 Date: Mon, 27 Jul 2026 16:25:30 +0530 Subject: [PATCH] fix(tools/security-tracker-stats-dashboard): align Python floor to 3.11 Match requires-python, ruff, and mypy to the repo-wide 3.11 floor so type-checking cannot green-light constructs unsupported by the declared runtime. Apply the py311 ruff UP017/B905 fixes that the raised target-version now surfaces. Also re-lock root uv.lock mypy requires-dev specifiers to >=2.3.0 so prek stops rewriting the lock on every README-touching commit (same correction as apache/magpie#965). Generated-by: Cursor Grok 4.5 Signed-off-by: shaurya2k06 --- .../README.md | 2 +- .../pyproject.toml | 6 ++-- .../render.py | 8 +++--- .../security_tracker_stats_dashboard/core.py | 6 ++-- .../tests/test_core.py | 28 +++++++++---------- .../security-tracker-stats-dashboard/uv.lock | 5 ++-- uv.lock | 16 +++++------ 7 files changed, 35 insertions(+), 36 deletions(-) diff --git a/tools/security-tracker-stats-dashboard/README.md b/tools/security-tracker-stats-dashboard/README.md index ddd0f4985..a5732e391 100644 --- a/tools/security-tracker-stats-dashboard/README.md +++ b/tools/security-tracker-stats-dashboard/README.md @@ -224,7 +224,7 @@ remaining charts still render. ## Prerequisites -- **Runtime:** Python 3.9+ (`python3`). Run each stage as `python3 fetch_events.py`, etc. +- **Runtime:** Python 3.11+ (`python3`). Run each stage as `python3 fetch_events.py`, etc. Optional: `pyyaml` — when missing, `render.py` uses a bundled minimal YAML subset parser sufficient for `default-config.yaml` and typical overlays; set `TRACKER_STATS_PY=uv-yaml` to pin clean PyYAML invocations via `uv run --with pyyaml`. diff --git a/tools/security-tracker-stats-dashboard/pyproject.toml b/tools/security-tracker-stats-dashboard/pyproject.toml index 55a47405f..8acecdaca 100644 --- a/tools/security-tracker-stats-dashboard/pyproject.toml +++ b/tools/security-tracker-stats-dashboard/pyproject.toml @@ -24,7 +24,7 @@ name = "security-tracker-stats-dashboard" version = "0.1.0" description = "Rendering helpers for the security-tracker statistics dashboard." readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.11" license = { text = "Apache-2.0" } dependencies = [] @@ -33,7 +33,7 @@ packages = ["src/security_tracker_stats_dashboard"] [tool.ruff] line-length = 110 -target-version = "py39" +target-version = "py311" src = ["src", "tests"] [tool.ruff.lint] @@ -41,7 +41,7 @@ select = ["E", "W", "F", "I", "B", "UP", "SIM", "C4", "RUF"] ignore = ["E501"] [tool.mypy] -python_version = "3.10" +python_version = "3.11" files = ["src", "tests"] check_untyped_defs = true no_implicit_optional = true diff --git a/tools/security-tracker-stats-dashboard/render.py b/tools/security-tracker-stats-dashboard/render.py index e3961053a..b3921379d 100644 --- a/tools/security-tracker-stats-dashboard/render.py +++ b/tools/security-tracker-stats-dashboard/render.py @@ -195,7 +195,7 @@ def _issue_label_names(issue): with open(prs_path) as f: prs_cache = json.load(f) -NOW = dt.datetime.now(dt.timezone.utc) +NOW = dt.datetime.now(dt.UTC) if UPSTREAM_REPO: # Match the original literal in the body-parse regex so an upstream @@ -308,7 +308,7 @@ def _issue_label_names(issue): # No / malformed date line — skip this entry defensively. continue try: - d = dt.datetime(int(dm.group(1)), int(dm.group(2)), int(dm.group(3)), tzinfo=dt.timezone.utc) + d = dt.datetime(int(dm.group(1)), int(dm.group(2)), int(dm.group(3)), tzinfo=dt.UTC) except ValueError: continue rejections_dated.append(d) @@ -501,7 +501,7 @@ def classify_per_config(labels, is_open, ts, n): be = bucket_end(*b) ts = NOW if be > NOW else be cum_rejected.append(rejections_backfill_total + sum(1 for rd in rejections_dated if rd <= ts)) -cum_reported = [o + r for o, r in zip(cum_opened, cum_rejected)] +cum_reported = [o + r for o, r in zip(cum_opened, cum_rejected, strict=True)] # --- Opened-in-bucket vs untriaged-at-bucket-end ------------------ @@ -521,7 +521,7 @@ def classify_per_config(labels, is_open, ts, n): # Per-bucket reported = trackers opened in the bucket + reports rejected in # the same bucket (rejected_series). Equals opened_in_b when the rejections # stat is disabled, so the trace is only drawn when the stat is active. -reported_in_b = [o + r for o, r in zip(opened_in_b, rejected_series)] +reported_in_b = [o + r for o, r in zip(opened_in_b, rejected_series, strict=True)] # --- triage / response --------------------------------------------- diff --git a/tools/security-tracker-stats-dashboard/src/security_tracker_stats_dashboard/core.py b/tools/security-tracker-stats-dashboard/src/security_tracker_stats_dashboard/core.py index 21656c44d..09de924bc 100644 --- a/tools/security-tracker-stats-dashboard/src/security_tracker_stats_dashboard/core.py +++ b/tools/security-tracker-stats-dashboard/src/security_tracker_stats_dashboard/core.py @@ -71,14 +71,14 @@ def quarter_label(y: int, q: int) -> str: def month_end(y: int, m: int) -> dt.datetime: """Return the last instant of the given calendar month (UTC).""" last_day = calendar.monthrange(y, m)[1] - return dt.datetime(y, m, last_day, 23, 59, 59, tzinfo=dt.timezone.utc) + return dt.datetime(y, m, last_day, 23, 59, 59, tzinfo=dt.UTC) def quarter_end(y: int, q: int) -> dt.datetime: """Return the last instant of the given calendar quarter (UTC).""" last_month = q * 3 last_day = calendar.monthrange(y, last_month)[1] - return dt.datetime(y, last_month, last_day, 23, 59, 59, tzinfo=dt.timezone.utc) + return dt.datetime(y, last_month, last_day, 23, 59, 59, tzinfo=dt.UTC) def iter_months(y0: int, m0: int, y1: int, m1: int) -> Iterator[tuple[int, int]]: @@ -119,7 +119,7 @@ def week_label(y: int, w: int) -> str: def week_end(y: int, w: int) -> dt.datetime: # ISO weeks run Monday (day 1) .. Sunday (day 7); end at Sunday 23:59:59. sunday = dt.date.fromisocalendar(y, w, 7) - return dt.datetime(sunday.year, sunday.month, sunday.day, 23, 59, 59, tzinfo=dt.timezone.utc) + return dt.datetime(sunday.year, sunday.month, sunday.day, 23, 59, 59, tzinfo=dt.UTC) def iter_weeks(y0: int, w0: int, y1: int, w1: int) -> Iterator[tuple[int, int]]: diff --git a/tools/security-tracker-stats-dashboard/tests/test_core.py b/tools/security-tracker-stats-dashboard/tests/test_core.py index 5374ace59..faf9c2253 100644 --- a/tools/security-tracker-stats-dashboard/tests/test_core.py +++ b/tools/security-tracker-stats-dashboard/tests/test_core.py @@ -58,11 +58,11 @@ def test_empty_returns_none(self): def test_z_suffix(self): result = parse_dt("2024-03-15T10:30:00Z") - assert result == dt.datetime(2024, 3, 15, 10, 30, 0, tzinfo=dt.timezone.utc) + assert result == dt.datetime(2024, 3, 15, 10, 30, 0, tzinfo=dt.UTC) def test_plus_offset(self): result = parse_dt("2024-03-15T10:30:00+00:00") - assert result == dt.datetime(2024, 3, 15, 10, 30, 0, tzinfo=dt.timezone.utc) + assert result == dt.datetime(2024, 3, 15, 10, 30, 0, tzinfo=dt.UTC) def test_aware(self): result = parse_dt("2025-01-01T00:00:00Z") @@ -77,41 +77,41 @@ def test_aware(self): class TestMonthOf: def test_basic(self): - d = dt.datetime(2024, 6, 15, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 6, 15, tzinfo=dt.UTC) assert month_of(d) == (2024, 6) def test_january(self): - d = dt.datetime(2025, 1, 1, tzinfo=dt.timezone.utc) + d = dt.datetime(2025, 1, 1, tzinfo=dt.UTC) assert month_of(d) == (2025, 1) def test_december(self): - d = dt.datetime(2024, 12, 31, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 12, 31, tzinfo=dt.UTC) assert month_of(d) == (2024, 12) class TestQuarterOf: def test_q1(self): - d = dt.datetime(2024, 2, 15, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 2, 15, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 1) def test_q2(self): - d = dt.datetime(2024, 4, 1, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 4, 1, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 2) def test_q3(self): - d = dt.datetime(2024, 9, 30, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 9, 30, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 3) def test_q4(self): - d = dt.datetime(2024, 12, 1, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 12, 1, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 4) def test_boundary_month_3_is_q1(self): - d = dt.datetime(2024, 3, 31, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 3, 31, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 1) def test_boundary_month_4_is_q2(self): - d = dt.datetime(2024, 4, 1, tzinfo=dt.timezone.utc) + d = dt.datetime(2024, 4, 1, tzinfo=dt.UTC) assert quarter_of(d) == (2024, 2) @@ -140,7 +140,7 @@ def test_january(self): assert result.day == 31 assert result.hour == 23 assert result.minute == 59 - assert result.tzinfo == dt.timezone.utc + assert result.tzinfo == dt.UTC def test_february_leap(self): result = month_end(2024, 2) @@ -177,7 +177,7 @@ def test_q4(self): assert result.day == 31 def test_aware(self): - assert quarter_end(2024, 1).tzinfo == dt.timezone.utc + assert quarter_end(2024, 1).tzinfo == dt.UTC # --------------------------------------------------------------------------- @@ -254,7 +254,7 @@ def test_sunday_end_of_week(self): assert (result.hour, result.minute, result.second) == (23, 59, 59) def test_is_utc(self): - assert week_end(2025, 52).tzinfo == dt.timezone.utc + assert week_end(2025, 52).tzinfo == dt.UTC def test_week_52(self): # ISO 2025-W52 ends Sun 2025-12-28. diff --git a/tools/security-tracker-stats-dashboard/uv.lock b/tools/security-tracker-stats-dashboard/uv.lock index cc990e743..2688e616d 100644 --- a/tools/security-tracker-stats-dashboard/uv.lock +++ b/tools/security-tracker-stats-dashboard/uv.lock @@ -1,9 +1,8 @@ version = 1 revision = 3 -requires-python = ">=3.9" +requires-python = ">=3.11" resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version < '3.10'", + "python_full_version >= '3.11'", ] [options] diff --git a/uv.lock b/uv.lock index b70568a41..f0fe72468 100644 --- a/uv.lock +++ b/uv.lock @@ -369,7 +369,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -495,7 +495,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -945,7 +945,7 @@ provides-extras = ["mcp"] [package.metadata.requires-dev] dev = [ { name = "mcp", specifier = ">=1.28.1" }, - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -1359,7 +1359,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -1570,7 +1570,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -1612,7 +1612,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -1633,7 +1633,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ] @@ -1843,7 +1843,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pytest", specifier = ">=9.1.1" }, { name = "ruff", specifier = ">=0.15.21" }, ]