From 946d8b0a7baca591debd6fc88716312f0f0f48ef Mon Sep 17 00:00:00 2001 From: Aryan Putta Date: Sun, 16 Aug 2026 12:48:01 -0400 Subject: [PATCH] pathfinder: make LocatedHeaderDir immutable locate_nvidia_header_directory() is functools.cache-backed and returns a mutable dataclass, so a caller that writes to the returned object changes what every later lookup of that libname returns for the life of the process. Freeze it, matching LocatedStaticLib and LocatedBitcodeLib, which are the other two public Located* return types. Signed-off-by: Aryan Putta --- .../_headers/find_nvidia_headers.py | 4 +-- .../docs/source/release/1.6.1-notes.rst | 6 ++++ .../tests/test_find_nvidia_headers.py | 31 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py index f5f56817141..a7af7b07467 100644 --- a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py +++ b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py @@ -31,13 +31,13 @@ # --------------------------------------------------------------------------- -@dataclass +@dataclass(frozen=True) class LocatedHeaderDir: abs_path: str | None found_via: str def __post_init__(self) -> None: - self.abs_path = _abs_norm(self.abs_path) + object.__setattr__(self, "abs_path", _abs_norm(self.abs_path)) #: Type alias for a header find step callable. diff --git a/cuda_pathfinder/docs/source/release/1.6.1-notes.rst b/cuda_pathfinder/docs/source/release/1.6.1-notes.rst index 4b6e7a0a55e..9a4da0ab77c 100644 --- a/cuda_pathfinder/docs/source/release/1.6.1-notes.rst +++ b/cuda_pathfinder/docs/source/release/1.6.1-notes.rst @@ -35,6 +35,12 @@ Highlights architecture-specific executable targets using the native Windows machine architecture. +* Make ``LocatedHeaderDir`` immutable, matching ``LocatedStaticLib`` and + ``LocatedBitcodeLib``. ``locate_nvidia_header_directory`` is + ``functools.cache``-backed, so writing to a returned object previously + changed what every later lookup of that ``libname`` returned. The type is + now hashable as well. + Internal maintenance -------------------- diff --git a/cuda_pathfinder/tests/test_find_nvidia_headers.py b/cuda_pathfinder/tests/test_find_nvidia_headers.py index 3e045dae265..6136011f180 100644 --- a/cuda_pathfinder/tests/test_find_nvidia_headers.py +++ b/cuda_pathfinder/tests/test_find_nvidia_headers.py @@ -11,6 +11,7 @@ # sudo apt install libnvshmem3-cuda-12 libnvshmem3-dev-cuda-12 # sudo apt install libnvshmem3-cuda-13 libnvshmem3-dev-cuda-13 +import dataclasses import functools import glob import importlib.metadata @@ -264,3 +265,33 @@ def test_locate_ctk_headers_canary_probe_errors_are_not_masked(monkeypatch, mock locate_nvidia_header_directory("cudart") with pytest.raises(RuntimeError, match="canary probe failed"): find_nvidia_header_directory("cudart") + + +@pytest.mark.agent_authored(model="claude-opus-5") +@pytest.mark.usefixtures("clear_locate_nvidia_header_cache") +def test_located_header_dir_is_immutable(tmp_path, monkeypatch, mocker): + cuda_home = tmp_path / "cuda-home" + expected_hdr_dir = _create_ctk_header(cuda_home, "cudart") + + monkeypatch.delenv("CONDA_PREFIX", raising=False) + monkeypatch.setenv("CUDA_HOME", str(cuda_home)) + monkeypatch.delenv("CUDA_PATH", raising=False) + mocker.patch.object(find_nvidia_headers_module, "find_sub_dirs_all_sitepackages", return_value=[]) + + first = locate_nvidia_header_directory("cudart") + assert first is not None + assert first.abs_path == expected_hdr_dir + + with pytest.raises(dataclasses.FrozenInstanceError): + first.abs_path = str(tmp_path / "somewhere-else") + + assert locate_nvidia_header_directory("cudart").abs_path == expected_hdr_dir + assert find_nvidia_header_directory("cudart") == expected_hdr_dir + + +@pytest.mark.agent_authored(model="claude-opus-5") +def test_located_header_dir_is_hashable(tmp_path): + include_dir = str(tmp_path / "include") + hdr_dir = LocatedHeaderDir(abs_path=include_dir, found_via="conda") + assert hdr_dir in {hdr_dir} + assert hdr_dir == LocatedHeaderDir(abs_path=include_dir, found_via="conda")