From 3aac063871a20ff33aed68e0fe63ef8b3bff5f2e Mon Sep 17 00:00:00 2001 From: BlackHat-Dot Date: Mon, 12 Oct 2026 12:02:47 +0530 Subject: [PATCH] BUG: do not mutate install_rpath in fix_rpath In RPATH.fix_rpath(), assigning 'new_rpath = install_rpath' and mutating it via '+=' and '.append()' mutated the caller's install_rpath list in place. Additionally, _Entry defined mutable defaults 'install_rpath: List[str] = []' and 'build_rpath: List[str] = []', sharing mutable state across instances. Copy install_rpath with 'list(install_rpath)' in fix_rpath(), annotate with Sequence[str], and use immutable tuple defaults '()' in _Entry. --- mesonpy/__init__.py | 6 +++--- mesonpy/_rpath.py | 9 ++++++--- tests/test_rpath.py | 48 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 57d218c82..0ed321786 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -138,8 +138,8 @@ def _translate_rpath(path: str, dst: pathlib.Path) -> str: class _Entry(typing.NamedTuple): dst: pathlib.Path src: str - install_rpath: List[str] = [] - build_rpath: List[str] = [] + install_rpath: Sequence[str] = () + build_rpath: Sequence[str] = () def _map_to_wheel(sources: Dict[str, Dict[str, Any]], @@ -490,7 +490,7 @@ def _stable_abi(self) -> Optional[str]: def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, origin: Path, destination: pathlib.Path, - install_rpath: List[str], build_rpath: List[str]) -> None: + install_rpath: Sequence[str], build_rpath: Sequence[str]) -> None: """Add a file to the wheel.""" if self._has_internal_libs and _is_native(origin): diff --git a/mesonpy/_rpath.py b/mesonpy/_rpath.py index 65039c665..d703b82b5 100644 --- a/mesonpy/_rpath.py +++ b/mesonpy/_rpath.py @@ -12,6 +12,7 @@ if typing.TYPE_CHECKING: + from collections.abc import Sequence Path = str | os.PathLike[str] T = typing.TypeVar('T') @@ -33,11 +34,12 @@ def set_rpath(filepath: Path, old: list[str], rpath: list[str]) -> None: raise NotImplementedError @classmethod - def fix_rpath(cls, filepath: Path, install_rpath: list[str], build_rpath: list[str], libs_path: str | None) -> None: + def fix_rpath(cls, filepath: Path, install_rpath: Sequence[str], + build_rpath: Sequence[str], libs_path: str | None) -> None: old_rpath = cls.get_rpath(filepath) # Prepend install_rpath entries. - new_rpath = install_rpath + new_rpath = list(install_rpath) # Merge with existing entries, excluding build_rpath entries. Meson # adds a padding entry to RPATH composed of enough ``X`` characters @@ -66,7 +68,8 @@ def fix_rpath(cls, filepath: Path, install_rpath: list[str], build_rpath: list[s class _Windows(RPATH): @classmethod - def fix_rpath(cls, filepath: Path, install_rpath: list[str], build_rpath: list[str], libs_path: str | None) -> None: + def fix_rpath(cls, filepath: Path, install_rpath: Sequence[str], + build_rpath: Sequence[str], libs_path: str | None) -> None: pass diff --git a/tests/test_rpath.py b/tests/test_rpath.py index d450ea401..83a850fba 100644 --- a/tests/test_rpath.py +++ b/tests/test_rpath.py @@ -2,12 +2,16 @@ # # SPDX-License-Identifier: MIT +import os +import pathlib import sys import pytest import wheel.wheelfile -from mesonpy._rpath import get_rpath, set_rpath +import mesonpy + +from mesonpy._rpath import RPATH, get_rpath, set_rpath @pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='requires RPATH support') @@ -53,3 +57,45 @@ def test_rpath_get_set(wheel_sharedlib_in_package, tmp_path): set_rpath(obj, rpath, new_rpath) rpath = get_rpath(obj) assert rpath == new_rpath + + +def test_fix_rpath(): + calls = [] + + class DummyRPATH(RPATH): + @staticmethod + def get_rpath(filepath): + return ['$ORIGIN/old', 'build/dir', 'XXXXX'] + + @staticmethod + def set_rpath(filepath, old, rpath): + calls.append((filepath, old, rpath)) + + install_rpath = ['$ORIGIN/custom'] + build_rpath = ['build/dir'] + DummyRPATH.fix_rpath('dummy', install_rpath, build_rpath, 'libs') + + expected_new = ['$ORIGIN/custom', '$ORIGIN/old', os.path.join('$ORIGIN', 'libs')] + assert calls == [('dummy', ['$ORIGIN/old', 'build/dir', 'XXXXX'], expected_new)] + + +def test_fix_rpath_sequence(): + calls = [] + + class DummyRPATH(RPATH): + @staticmethod + def get_rpath(filepath): + return ['$ORIGIN/old'] + + @staticmethod + def set_rpath(filepath, old, rpath): + calls.append(rpath) + + DummyRPATH.fix_rpath('dummy', ('$ORIGIN/custom',), (), None) + assert calls == [['$ORIGIN/custom', '$ORIGIN/old']] + + +def test_entry_default(): + entry = mesonpy._Entry(pathlib.Path('dst'), 'src') + assert entry.install_rpath == () + assert entry.build_rpath == ()