From 8463394d2cf99795b9ce722a82ef8ae837f45e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Thu, 19 Dec 2024 14:14:12 +0100 Subject: [PATCH] util/path: add `join_abs()` to join potentially absolute paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out that in many cases, stages need to join two absolute paths, the pipeline tree path and the path on a booted system. However, the standard `os.path.join()` function can't handle such situation as just prepending the root to the subsequent paths. Add a new helper function, which is able to join any paths together, regardless if any of them is absolute or not. If the root is not absolute, the result will be made absolute to the filesystem root `/`. Signed-off-by: Tomáš Hozza --- osbuild/util/path.py | 17 ++++++++++++++++- test/mod/test_util_path.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/osbuild/util/path.py b/osbuild/util/path.py index 9f5ef246..035b385f 100644 --- a/osbuild/util/path.py +++ b/osbuild/util/path.py @@ -2,7 +2,7 @@ import errno import os import os.path -from typing import Optional +from typing import Optional, Union from .ctx import suppress_oserror @@ -41,3 +41,18 @@ def in_tree(path: str, tree: str, must_exist: bool = False) -> bool: if path.startswith(tree): return not must_exist or os.path.exists(path) return False + + +def join_abs(root: Union[str, os.PathLike], *paths: Union[str, os.PathLike]) -> str: + """ + Join root and paths together, handling the case where paths are absolute paths. + In that case, paths are just appended to root as if they were relative paths. + The result is always an absolute path relative to the filesystem root '/'. + """ + final_path = root + for path in paths: + if os.path.isabs(path): + final_path = os.path.join(final_path, os.path.relpath(path, os.sep)) + else: + final_path = os.path.join(final_path, path) + return os.path.normpath(os.path.join(os.sep, final_path)) diff --git a/test/mod/test_util_path.py b/test/mod/test_util_path.py index 017671b2..8e3a29f7 100644 --- a/test/mod/test_util_path.py +++ b/test/mod/test_util_path.py @@ -68,3 +68,41 @@ def test_in_tree(): for args, expected in cases.items(): assert path.in_tree(*args) == expected, args + + +@pytest.mark.parametrize("test_case", ( + { + "args": ("",), + "expected": "/" + }, + { + "args": ("", "file"), + "expected": "/file" + }, + { + "args": ("", "/file"), + "expected": "/file" + }, + { + "args": ("/tmp", "/file"), + "expected": "/tmp/file" + }, + { + "args": ("/tmp", "/foo", "/bar"), + "expected": "/tmp/foo/bar" + }, + { + "args": ("/", "/foo"), + "expected": "/foo" + }, + { + "args": ("/", "/foo", "/bar"), + "expected": "/foo/bar" + }, + { + "args": (Path("/tmp"), "/foo", "/bar"), + "expected": "/tmp/foo/bar" + }, +)) +def test_join_abs(test_case): + assert path.join_abs(*test_case["args"]) == test_case["expected"]