From 3e6ef0fd68b0c5de1ddd4ddfd5289918704605ef Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 19 Dec 2025 13:42:26 +0100 Subject: [PATCH] release: fix pyproject.toml version update for PEP 621 projects Update version handling to correctly modify [project].version in pyproject.toml. The previous implementation only matched top-level version assignments and failed for PEP 621 layouts. - Restrict update to the [project] section - Allow leading whitespace in version lines - Replace sys.exit() with proper exceptions - Remove unused sys import https://chatgpt.com/share/69454836-4698-800f-9d19-7e67e8e789d6 --- src/pkgmgr/actions/release/files.py | 57 +++++++++++------------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/pkgmgr/actions/release/files.py b/src/pkgmgr/actions/release/files.py index b3af0fe..17dddee 100644 --- a/src/pkgmgr/actions/release/files.py +++ b/src/pkgmgr/actions/release/files.py @@ -19,7 +19,6 @@ from __future__ import annotations import os import re import subprocess -import sys import tempfile from datetime import date, datetime from typing import Optional, Tuple @@ -85,53 +84,42 @@ def _open_editor_for_changelog(initial_message: Optional[str] = None) -> str: # --------------------------------------------------------------------------- -def update_pyproject_version( - pyproject_path: str, - new_version: str, - preview: bool = False, -) -> None: - """ - Update the version in pyproject.toml with the new version. - - The function looks for a line matching: - - version = "X.Y.Z" - - and replaces the version part with the given new_version string. - - If the file does not exist, it is skipped without failing the release. - """ +def update_pyproject_version(pyproject_path: str, new_version: str, preview: bool = False) -> None: if not os.path.exists(pyproject_path): - print( - f"[INFO] pyproject.toml not found at: {pyproject_path}, " - "skipping version update." - ) + print(f"[INFO] pyproject.toml not found at: {pyproject_path}, skipping version update.") return try: with open(pyproject_path, "r", encoding="utf-8") as f: content = f.read() except OSError as exc: - print( - f"[WARN] Could not read pyproject.toml at {pyproject_path}: {exc}. " - "Skipping version update." - ) + print(f"[WARN] Could not read pyproject.toml at {pyproject_path}: {exc}. Skipping version update.") return - pattern = r'^(version\s*=\s*")([^"]+)(")' - new_content, count = re.subn( - pattern, - lambda m: f"{m.group(1)}{new_version}{m.group(3)}", - content, - flags=re.MULTILINE, + # Find [project] block (PEP 621) + m = re.search(r"(?ms)^\s*\[project\]\s*$.*?(?=^\s*\[|\Z)", content) + if not m: + print("[ERROR] Could not find [project] section in pyproject.toml") + raise RuntimeError("Missing [project] section in pyproject.toml") + + project_block = m.group(0) + + # Replace version line inside that block (allow leading whitespace) + ver_pat = r'(?m)^(\s*version\s*=\s*")([^"]+)(")\s*$' + new_project_block, count = re.subn( + ver_pat, + lambda mm: f"{mm.group(1)}{new_version}{mm.group(3)}", + project_block, ) if count == 0: - print("[ERROR] Could not find version line in pyproject.toml") - sys.exit(1) + print("[ERROR] Could not find version = \"...\" in [project] section of pyproject.toml") + raise RuntimeError("Missing version key in [project] section") + + new_content = content[: m.start()] + new_project_block + content[m.end() :] if preview: - print(f"[PREVIEW] Would update pyproject.toml version to {new_version}") + print(f"[PREVIEW] Would update pyproject.toml [project].version to {new_version}") return with open(pyproject_path, "w", encoding="utf-8") as f: @@ -139,7 +127,6 @@ def update_pyproject_version( print(f"Updated pyproject.toml version to {new_version}") - def update_flake_version( flake_path: str, new_version: str,