From 9edafb0736616a9aa76f015bf26759e80524a2fb Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:36:57 +0200 Subject: [PATCH 1/4] Replace local file with https://peps.python.org/api/release-cycle.json --- .pre-commit-config.yaml | 2 - _tools/generate_release_cycle.py | 5 +- conf.py | 5 +- include/release-cycle.json | 146 ------------------------------- 4 files changed, 6 insertions(+), 152 deletions(-) delete mode 100644 include/release-cycle.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ae27fd1f2..c5beee4a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,14 +7,12 @@ repos: args: [--exit-non-zero-on-fix] - id: ruff-format name: Run Ruff (format) - args: [--check] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-case-conflict - id: check-merge-conflict - - id: check-json - id: check-yaml - id: debug-statements - id: end-of-file-fixer diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index dd0a3a7c6..6c234a703 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -7,6 +7,7 @@ import csv import datetime as dt import json +from urllib.request import urlopen import jinja2 @@ -41,8 +42,8 @@ class Versions: """For converting JSON to CSV and SVG.""" def __init__(self, *, limit_to_active=False, special_py27=False) -> None: - with open("include/release-cycle.json", encoding="UTF-8") as in_file: - self.versions = json.load(in_file) + with urlopen("https://peps.python.org/api/release-cycle.json") as in_file: + self.versions = json.loads(in_file.read().decode("utf-8")) # Generate a few additional fields for key, version in self.versions.items(): diff --git a/conf.py b/conf.py index 0f6a820d8..c80797621 100644 --- a/conf.py +++ b/conf.py @@ -1,4 +1,5 @@ import json +from urllib.request import urlopen extensions = [ 'notfound.extension', @@ -178,8 +179,8 @@ # Dynamically expose the Python version associated with the "main" branch. # Exactly one entry in ``release-cycle.json`` should have ``"branch": "main"``. -with open("include/release-cycle.json", encoding="UTF-8") as _f: - _cycle = json.load(_f) +with urlopen("https://peps.python.org/api/release-cycle.json") as _f: + _cycle = json.loads(_f.read().decode("utf-8")) _main_version = next( version for version, data in _cycle.items() if data.get("branch") == "main" diff --git a/include/release-cycle.json b/include/release-cycle.json deleted file mode 100644 index 26436bf00..000000000 --- a/include/release-cycle.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "3.15": { - "branch": "main", - "pep": 790, - "status": "feature", - "first_release": "2026-10-01", - "end_of_life": "2031-10", - "release_manager": "Hugo van Kemenade" - }, - "3.14": { - "branch": "3.14", - "pep": 745, - "status": "bugfix", - "first_release": "2025-10-07", - "end_of_life": "2030-10", - "release_manager": "Hugo van Kemenade" - }, - "3.13": { - "branch": "3.13", - "pep": 719, - "status": "bugfix", - "first_release": "2024-10-07", - "end_of_life": "2029-10", - "release_manager": "Thomas Wouters" - }, - "3.12": { - "branch": "3.12", - "pep": 693, - "status": "security", - "first_release": "2023-10-02", - "end_of_life": "2028-10", - "release_manager": "Thomas Wouters" - }, - "3.11": { - "branch": "3.11", - "pep": 664, - "status": "security", - "first_release": "2022-10-24", - "end_of_life": "2027-10", - "release_manager": "Pablo Galindo Salgado" - }, - "3.10": { - "branch": "3.10", - "pep": 619, - "status": "security", - "first_release": "2021-10-04", - "end_of_life": "2026-10", - "release_manager": "Pablo Galindo Salgado" - }, - "3.9": { - "branch": "3.9", - "pep": 596, - "status": "end-of-life", - "first_release": "2020-10-05", - "end_of_life": "2025-10-31", - "release_manager": "Łukasz Langa" - }, - "3.8": { - "branch": "3.8", - "pep": 569, - "status": "end-of-life", - "first_release": "2019-10-14", - "end_of_life": "2024-10-07", - "release_manager": "Łukasz Langa" - }, - "3.7": { - "branch": "3.7", - "pep": 537, - "status": "end-of-life", - "first_release": "2018-06-27", - "end_of_life": "2023-06-27", - "release_manager": "Ned Deily" - }, - "3.6": { - "branch": "3.6", - "pep": 494, - "status": "end-of-life", - "first_release": "2016-12-23", - "end_of_life": "2021-12-23", - "release_manager": "Ned Deily" - }, - "3.5": { - "branch": "3.5", - "pep": 478, - "status": "end-of-life", - "first_release": "2015-09-13", - "end_of_life": "2020-09-30", - "release_manager": "Larry Hastings" - }, - "3.4": { - "branch": "3.4", - "pep": 429, - "status": "end-of-life", - "first_release": "2014-03-16", - "end_of_life": "2019-03-18", - "release_manager": "Larry Hastings" - }, - "3.3": { - "branch": "3.3", - "pep": 398, - "status": "end-of-life", - "first_release": "2012-09-29", - "end_of_life": "2017-09-29", - "release_manager": "Georg Brandl, Ned Deily (3.3.7+)" - }, - "3.2": { - "branch": "3.2", - "pep": 392, - "status": "end-of-life", - "first_release": "2011-02-20", - "end_of_life": "2016-02-20", - "release_manager": "Georg Brandl" - }, - "2.7": { - "branch": "2.7", - "pep": 373, - "status": "end-of-life", - "first_release": "2010-07-03", - "end_of_life": "2020-01-01", - "release_manager": "Benjamin Peterson" - }, - "3.1": { - "branch": "3.1", - "pep": 375, - "status": "end-of-life", - "first_release": "2009-06-27", - "end_of_life": "2012-04-09", - "release_manager": "Benjamin Peterson" - }, - "3.0": { - "branch": "3.0", - "pep": 361, - "status": "end-of-life", - "first_release": "2008-12-03", - "end_of_life": "2009-06-27", - "release_manager": "Barry Warsaw" - }, - "2.6": { - "branch": "2.6", - "pep": 361, - "status": "end-of-life", - "first_release": "2008-10-01", - "end_of_life": "2013-10-29", - "release_manager": "Barry Warsaw" - } -} From bf47e6d9c2bd02b439d8a5646333d51a5fa3c660 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:21:09 +0200 Subject: [PATCH 2/4] Make output dir if needed --- _tools/generate_release_cycle.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index 6c234a703..e21212ca8 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -7,6 +7,7 @@ import csv import datetime as dt import json +from pathlib import Path from urllib.request import urlopen import jinja2 @@ -198,6 +199,8 @@ def main() -> None: versions = Versions() assert len(versions.versions) > 10 + Path("include").mkdir(exist_ok=True) + versions.write_csv() versions.write_svg(args.today, "include/release-cycle-all.svg") From 0dec21436c00fb9c610c926bb119013d97d1cb25 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:00:17 +0200 Subject: [PATCH 3/4] Cache response --- _tools/generate_release_cycle.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/_tools/generate_release_cycle.py b/_tools/generate_release_cycle.py index e21212ca8..f9df49bab 100644 --- a/_tools/generate_release_cycle.py +++ b/_tools/generate_release_cycle.py @@ -7,6 +7,7 @@ import csv import datetime as dt import json +from functools import cache from pathlib import Path from urllib.request import urlopen @@ -39,12 +40,17 @@ def parse_version(ver: str) -> list[int]: return [int(i) for i in ver["key"].split(".")] +@cache +def get_versions() -> str: + with urlopen("https://peps.python.org/api/release-cycle.json") as in_file: + return json.loads(in_file.read().decode("utf-8")) + + class Versions: """For converting JSON to CSV and SVG.""" def __init__(self, *, limit_to_active=False, special_py27=False) -> None: - with urlopen("https://peps.python.org/api/release-cycle.json") as in_file: - self.versions = json.loads(in_file.read().decode("utf-8")) + self.versions = get_versions() # Generate a few additional fields for key, version in self.versions.items(): From 0e5cb84d1cb920d94190a1ab442360766a82b963 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:04:58 +0200 Subject: [PATCH 4/4] Only call generate_release_cycle.py once via 'make html' --- Makefile | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 3d485ae2d..a3e176438 100644 --- a/Makefile +++ b/Makefile @@ -103,15 +103,20 @@ _ensure-pre-commit: lint: _ensure-pre-commit $(VENVDIR)/bin/python3 -m pre_commit run --all-files -# Defined so that "include/release-cycle.json" -# doesn't fall through to the catch-all target. -include/release-cycle.json: - @exit - -$(_RELEASE_CYCLE): include/release-cycle.json +# Generate all release cycle files together with a single script invocation +# Use branches.csv as the primary target, others depend on it +include/branches.csv: $(VENVDIR)/bin/python3 _tools/generate_release_cycle.py @echo Release cycle data generated. +# Other files are generated together with branches.csv +include/end-of-life.csv: include/branches.csv + @: +include/release-cycle-all.svg: include/branches.csv + @: +include/release-cycle.svg: include/branches.csv + @: + # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. .PHONY: Makefile