diff --git a/release.py b/release.py index e6fa52d3..bba95dcc 100755 --- a/release.py +++ b/release.py @@ -20,9 +20,11 @@ import subprocess import sys import tempfile +import urllib.request from collections.abc import Generator, Sequence from contextlib import contextmanager from dataclasses import dataclass +from functools import cache from pathlib import Path from typing import ( Any, @@ -462,6 +464,22 @@ def tweak_patchlevel( print("done") +@cache +def get_pep_number(version: str) -> str: + """Fetch PEP number for a Python version from peps.python.org. + + Returns the PEP number as a string, or "TODO" if not found. + """ + url = "https://peps.python.org/api/release-cycle.json" + with urllib.request.urlopen(url, timeout=10) as response: + data = json.loads(response.read().decode()) + if version in data: + pep = data[version].get("pep") + if pep: + return str(pep) + return "TODO" + + def tweak_readme(tag: Tag, filename: str = "README.rst") -> None: print(f"Updating {filename}...", end=" ") readme = Path(filename) @@ -473,8 +491,37 @@ def tweak_readme(tag: Tag, filename: str = "README.rst") -> None: underline = "=" * len(this_is) lines[0] = this_is lines[1] = underline + content = "\n".join(lines) + + DOCS_URL = r"https://docs\.python\.org/" + X_Y = r"\d+\.\d+" + + # Replace in: 3.14 `_ + content = re.sub( + rf"{X_Y} (<{DOCS_URL}){X_Y}(/whatsnew/){X_Y}(\.html>`_)", + rf"{tag.basic_version} \g<1>{tag.basic_version}\g<2>{tag.basic_version}\g<3>", + content, + ) + + # Replace in: `Documentation for Python 3.14 `_ + content = re.sub( + rf"(`Documentation for Python ){X_Y}( <{DOCS_URL}){X_Y}(/>`_)", + rf"\g<1>{tag.basic_version}\g<2>{tag.basic_version}\g<3>", + content, + ) + + # Get PEP number for this version + pep_number = get_pep_number(tag.basic_version) + pep_padded = pep_number.zfill(4) if pep_number != "TODO" else "TODO" + + # Replace in: `PEP 745 `__ for Python 3.14 + content = re.sub( + rf"(`PEP )\d+( `__ for Python ){X_Y}", + rf"\g<1>{pep_number}\g<2>{pep_padded}\g<3>{tag.basic_version}", + content, + ) - readme.write_text("\n".join(lines)) + readme.write_text(content) print("done") diff --git a/tests/test_release.py b/tests/test_release.py index c72289ae..35c26089 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -71,32 +71,57 @@ def test_tweak_patchlevel(tmp_path: Path) -> None: @pytest.mark.parametrize( - ["test_tag", "expected_version", "expected_underline"], + [ + "test_tag", + "expected_version", + "expected_underline", + "expected_whatsnew", + "expected_docs", + "expected_pep_line", + ], [ ( "3.14.0a6", "This is Python version 3.14.0 alpha 6", "=====================================", + "3.14 `_", + "`Documentation for Python 3.14 `_", + "`PEP 745 `__ for Python 3.14", ), ( "3.14.0b2", "This is Python version 3.14.0 beta 2", "====================================", + "3.14 `_", + "`Documentation for Python 3.14 `_", + "`PEP 745 `__ for Python 3.14", ), ( "3.14.0rc2", "This is Python version 3.14.0 release candidate 2", "=================================================", + "3.14 `_", + "`Documentation for Python 3.14 `_", + "`PEP 745 `__ for Python 3.14", ), ( - "3.14.1", - "This is Python version 3.14.1", + "3.15.1", + "This is Python version 3.15.1", "=============================", + "3.15 `_", + "`Documentation for Python 3.15 `_", + "`PEP 790 `__ for Python 3.15", ), ], ) def test_tweak_readme( - tmp_path: Path, test_tag: str, expected_version: str, expected_underline: str + tmp_path: Path, + test_tag: str, + expected_version: str, + expected_underline: str, + expected_whatsnew: str, + expected_docs: str, + expected_pep_line: str, ) -> None: # Arrange tag = release.Tag(test_tag) @@ -110,11 +135,12 @@ def test_tweak_readme( release.tweak_readme(tag, filename=str(readme_file)) # Assert - original_lines = original_contents.split("\n") new_contents = readme_file.read_text() new_lines = new_contents.split("\n") assert new_lines[0] == expected_version assert new_lines[1] == expected_underline - assert new_lines[2:] == original_lines[2:] + assert expected_whatsnew in new_contents + assert expected_docs in new_contents + assert expected_pep_line in new_contents assert original_contents.endswith("\n") assert new_contents.endswith("\n")