diff --git a/ci/tools/check_release_notes.py b/ci/tools/check_release_notes.py index 75d2c9871f0..1c99ddb019a 100644 --- a/ci/tools/check_release_notes.py +++ b/ci/tools/check_release_notes.py @@ -18,6 +18,7 @@ import os import re import sys +from pathlib import Path COMPONENT_TO_PACKAGE: dict[str, str] = { "cuda-core": "cuda_core", @@ -62,8 +63,8 @@ def is_post_release(version: str) -> bool: return ".post" in version -def load_backport_branch(repo_root: str = ".") -> str | None: - path = os.path.join(repo_root, "ci", "versions.yml") +def load_backport_branch(repo_root: Path = Path(".")) -> str | None: + path = repo_root / "ci" / "versions.yml" try: with open(path, encoding="utf-8") as f: for line in f: @@ -84,13 +85,16 @@ def is_backport_version(version: str, backport_branch: str) -> bool: return version == backport_branch -def notes_path(package: str, version: str) -> str: - return os.path.join(package, "docs", "source", "release", f"{version}-notes.rst") +def notes_path(package: str, version: str) -> Path: + return Path(package, "docs", "source", "release", f"{version}-notes.rst") -def check_release_notes(git_tag: str, component: str, repo_root: str = ".") -> list[tuple[str, str]]: +def check_release_notes(git_tag: str, component: str, repo_root: Path = Path(".")) -> list[tuple[str | Path, str]]: """Return a list of (path, reason) for missing or empty release notes. + ``path`` is the repo-relative notes path, or a ```` naming the + offending argument when the tag or component itself is the problem. + Returns an empty list when notes are present and non-empty, or when the tag is a .post release (no new notes required). """ @@ -105,10 +109,10 @@ def check_release_notes(git_tag: str, component: str, repo_root: str = ".") -> l return [] path = notes_path(COMPONENT_TO_PACKAGE[component], version) - full = os.path.join(repo_root, path) - if not os.path.isfile(full): + full = repo_root / path + if not full.is_file(): return [(path, "missing")] - if os.path.getsize(full) == 0: + if full.stat().st_size == 0: return [(path, "empty")] return [] @@ -123,7 +127,7 @@ def write_step_summary(message: str) -> None: f.write("\n") -def warn_missing_backport_notes(git_tag: str, component: str, problems: list[tuple[str, str]]) -> None: +def warn_missing_backport_notes(git_tag: str, component: str, problems: list[tuple[str | Path, str]]) -> None: print(f"WARNING: missing or empty release notes for backport tag {git_tag}:") summary_lines = [ "## Release Notes Reminder", @@ -147,8 +151,8 @@ def validate_backport_decision( version: str, backport_git_tag: str, backport_branch: str | None, - repo_root: str, -) -> tuple[int | None, list[tuple[str, str]]]: + repo_root: Path, +) -> tuple[int | None, list[tuple[str | Path, str]]]: if component not in BACKPORT_PLANNING_COMPONENTS or is_post_release(version): return None, [] @@ -205,7 +209,7 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--git-tag", required=True) parser.add_argument("--component", required=True, choices=list(COMPONENT_TO_PACKAGE)) - parser.add_argument("--repo-root", default=".") + parser.add_argument("--repo-root", default=Path("."), type=Path) parser.add_argument("--backport-git-tag", default="") parser.add_argument("--backport-branch", default="") args = parser.parse_args(argv) diff --git a/ci/tools/tests/test_check_release_notes.py b/ci/tools/tests/test_check_release_notes.py index 4f65404eed5..e08eac6610d 100644 --- a/ci/tools/tests/test_check_release_notes.py +++ b/ci/tools/tests/test_check_release_notes.py @@ -3,10 +3,10 @@ from __future__ import annotations -import os import sys +from pathlib import Path -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, str(Path(__file__).parent.parent)) from check_release_notes import ( check_release_notes, is_post_release, @@ -87,43 +87,43 @@ def _make_notes(self, tmp_path, pkg, version, content="Release notes."): def test_present_and_nonempty(self, tmp_path): self._make_notes(tmp_path, "cuda_core", "0.7.0") - problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", str(tmp_path)) + problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", tmp_path) assert problems == [] def test_missing(self, tmp_path): - problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", str(tmp_path)) + problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", tmp_path) assert len(problems) == 1 assert problems[0][1] == "missing" def test_empty(self, tmp_path): self._make_notes(tmp_path, "cuda_core", "0.7.0", content="") - problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", str(tmp_path)) + problems = check_release_notes("cuda-core-v0.7.0", "cuda-core", tmp_path) assert len(problems) == 1 assert problems[0][1] == "empty" def test_post_release_skipped(self, tmp_path): - problems = check_release_notes("v12.6.2.post1", "cuda-bindings", str(tmp_path)) + problems = check_release_notes("v12.6.2.post1", "cuda-bindings", tmp_path) assert problems == [] def test_invalid_tag(self, tmp_path): - problems = check_release_notes("not-a-tag", "cuda-core", str(tmp_path)) + problems = check_release_notes("not-a-tag", "cuda-core", tmp_path) assert len(problems) == 1 assert "cannot parse" in problems[0][1] def test_component_prefix_mismatch(self, tmp_path): # Pass a cuda-core tag with component=cuda-pathfinder; must be rejected. - problems = check_release_notes("cuda-core-v0.7.0", "cuda-pathfinder", str(tmp_path)) + problems = check_release_notes("cuda-core-v0.7.0", "cuda-pathfinder", tmp_path) assert len(problems) == 1 assert "cannot parse" in problems[0][1] def test_unknown_component(self, tmp_path): - problems = check_release_notes("v13.1.0", "bogus", str(tmp_path)) + problems = check_release_notes("v13.1.0", "bogus", tmp_path) assert len(problems) == 1 assert "unknown component" in problems[0][1] def test_plain_v_tag(self, tmp_path): self._make_notes(tmp_path, "cuda_python", "13.1.0") - problems = check_release_notes("v13.1.0", "cuda-python", str(tmp_path)) + problems = check_release_notes("v13.1.0", "cuda-python", tmp_path) assert problems == [] @@ -133,17 +133,17 @@ def test_from_versions_yml(self, tmp_path): d.mkdir(parents=True) (d / "versions.yml").write_text('backport_branch: "12.9.x"\n') - assert load_backport_branch(str(tmp_path)) == "12.9.x" + assert load_backport_branch(tmp_path) == "12.9.x" def test_from_github_ref_name_for_legacy_backport_branch(self, tmp_path, monkeypatch): monkeypatch.setenv("GITHUB_REF_NAME", "12.9.x") - assert load_backport_branch(str(tmp_path)) == "12.9.x" + assert load_backport_branch(tmp_path) == "12.9.x" def test_ignores_non_backport_github_ref_name(self, tmp_path, monkeypatch): monkeypatch.setenv("GITHUB_REF_NAME", "main") - assert load_backport_branch(str(tmp_path)) is None + assert load_backport_branch(tmp_path) is None class TestMain: diff --git a/toolshed/build_static_bitcode_input.py b/toolshed/build_static_bitcode_input.py index e2400100dde..622cba64e8c 100755 --- a/toolshed/build_static_bitcode_input.py +++ b/toolshed/build_static_bitcode_input.py @@ -14,9 +14,9 @@ """ import binascii -import os import sys import textwrap +from pathlib import Path import llvmlite.binding # HINT: pip install llvmlite @@ -24,11 +24,9 @@ def get_minimal_nvvmir_txt_template(): - cuda_bindings_tests_dir = os.path.normpath("cuda_bindings/tests") - assert os.path.isdir(cuda_bindings_tests_dir), ( - "Please run this helper script from the cuda-python top-level directory." - ) - sys.path.insert(0, os.path.abspath(cuda_bindings_tests_dir)) + cuda_bindings_tests_dir = Path("cuda_bindings/tests") + assert cuda_bindings_tests_dir.is_dir(), "Please run this helper script from the cuda-python top-level directory." + sys.path.insert(0, str(cuda_bindings_tests_dir.resolve())) import test_nvvm return test_nvvm.MINIMAL_NVVMIR_TXT_TEMPLATE diff --git a/toolshed/check_generated_file_seals.py b/toolshed/check_generated_file_seals.py index 1a9c45de61b..71fc066af33 100644 --- a/toolshed/check_generated_file_seals.py +++ b/toolshed/check_generated_file_seals.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 import hashlib -import os import re import subprocess import sys @@ -142,7 +141,7 @@ def main(args): returncode = 0 for filepath in args: - if not os.path.isfile(filepath): + if not Path(filepath).is_file(): continue if not validate_generated_file_seal(filepath, previously_sealed_paths): returncode = 1 diff --git a/toolshed/check_spdx.py b/toolshed/check_spdx.py index a2d0c041546..710aa14ae1b 100644 --- a/toolshed/check_spdx.py +++ b/toolshed/check_spdx.py @@ -2,11 +2,10 @@ # SPDX-License-Identifier: Apache-2.0 import datetime -import os import re import subprocess import sys -from pathlib import PureWindowsPath +from pathlib import Path, PureWindowsPath import pathspec @@ -39,7 +38,7 @@ def load_spdx_ignore(): - if os.path.exists(SPDX_IGNORE_FILENAME): + if Path(SPDX_IGNORE_FILENAME).exists(): with open(SPDX_IGNORE_FILENAME, encoding="utf-8") as f: lines = f.readlines() else: diff --git a/toolshed/dump_cutile_b64.py b/toolshed/dump_cutile_b64.py index 422bf95232b..8e58e452e02 100644 --- a/toolshed/dump_cutile_b64.py +++ b/toolshed/dump_cutile_b64.py @@ -9,9 +9,9 @@ """ import base64 -import glob import os import sys +from pathlib import Path import cupy @@ -54,13 +54,13 @@ def main(): raise # Find the .cutile file in current directory - cutile_files = glob.glob("./*.cutile") + cutile_files = list(Path().glob("*.cutile")) if not cutile_files: print("No .cutile file found in current directory", file=sys.stderr) sys.exit(1) # Use the most recently modified one if multiple exist - cutile_path = max(cutile_files, key=os.path.getmtime) + cutile_path = max(cutile_files, key=lambda path: path.stat().st_mtime) # Read the binary content with open(cutile_path, "rb") as f: