From 9026471e76aab96943fe9ed1f661d2928cdbe83a Mon Sep 17 00:00:00 2001 From: Ruediger Pluem <53253255+rpluem-vf@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:25:22 +0100 Subject: [PATCH] Return absolute path without resolving symlinks (#4341) After the rewrite of `util.abs_path` to use `pathlib` instead of `os.path` (000d20060d6081ce73ac12bcacad8e66578faf74, c20dc081d697174d7e526953ff82d4c5e3254506) the function now resolves symlinks in the given path whereas it did not do before. This breaks setups where the `molecule.yml` file in a scenario is a symlink. E.g. /common-files/molecule.yml /roles/role1/molecule/default/molecule.yml -> /common-files/molecule.yml /roles/role1/molecule/some-scenario/molecule.yml -> /common-files/molecule.yml /roles/role2/molecule/default/molecule.yml -> /common-files/molecule.yml Where a common `molecule.yml` can be used for multiple roles / scenarios and where changes to this file should reflect everywhere. In this case `molecule test` executed in `/roles/role1` returns `Scenario 'default' not found. Exiting.` or `molecule test -s some-scenario` executed in `/roles/role1` returns `Scenario 'some-scenario' not found. Exiting.` (see https://github.com/ansible/molecule/blob/f58ac30d5cee79a073ce0893701c8e82ec669078/src/molecule/scenarios.py#L127) Unfortunately `pathlib` is not a drop-in replacement for `os.path` (see https://docs.python.org/3/library/pathlib.html#comparison-to-the-os-and-os-path-modules). [Path.absolute()](https://docs.python.org/3/library/pathlib.html#pathlib.Path.absolute) does not resolve symlink but does not reduce `/../` segments in the path. [Path.resolve()](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve) does reduce `/../` path elements, but also resolves symlinks. There is no method or combination of methods in the `Path` object that can deliver the same functionality as [os.path.abspath()](https://docs.python.org/3/library/os.path.html#os.path.abspath). Hence the code reverts back to using `os.path.abspath()`, but keeps accepting `Path` objects as input and returns the absolute path as such in these cases. --------- Signed-off-by: Ruediger Pluem Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kate Case --- src/molecule/util.py | 8 ++++---- tests/unit/test_util.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/molecule/util.py b/src/molecule/util.py index dbd934da74..a57abc61ea 100644 --- a/src/molecule/util.py +++ b/src/molecule/util.py @@ -402,7 +402,7 @@ def abs_path(path: str | Path | None) -> str | Path: """Return absolute path. Args: - path: File path to resolve absolute path from. + path: File path to create absolute path from. Returns: Absolute path of path. @@ -411,9 +411,9 @@ def abs_path(path: str | Path | None) -> str | Path: return "" output_type = type(path) - if isinstance(path, str): - path = Path(path) - path = path.resolve() + if isinstance(path, Path): + path = str(path) + path = os.path.abspath(path) # noqa: PTH100 return output_type(path) diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py index 095e10dbf9..0b94bd5207 100644 --- a/tests/unit/test_util.py +++ b/tests/unit/test_util.py @@ -21,6 +21,7 @@ import binascii import os +import tempfile import warnings from pathlib import Path @@ -340,6 +341,17 @@ def test_abs_path_with_empty_path() -> None: assert util.abs_path("") == "" +def test_abs_path_with_symlink() -> None: + """Test the `abs_path` function not resolving symlinks.""" + with tempfile.NamedTemporaryFile() as tmp_file: + tmpfile_path = Path(tmp_file.name) + symlink_path = Path(tmp_file.name + "_sym") + symlink_path.symlink_to(tmpfile_path) + abs_path_result = util.abs_path(symlink_path) + symlink_path.unlink() + assert abs_path_result == symlink_path + + @pytest.mark.parametrize( ("a", "b", "x"), [ # noqa: PT007