From 653a3b0f2e0a4891eed4651c0297dbb5d25c42d1 Mon Sep 17 00:00:00 2001 From: Herman Snevajs Date: Fri, 9 Oct 2026 13:25:49 +0200 Subject: [PATCH] use abspath for destination file --- mergin/client.py | 2 +- mergin/client_pull.py | 4 ++-- mergin/report.py | 2 +- mergin/test/test_client_pull.py | 19 ++++++++++++++++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index a23863d8..76b9bcc6 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -1274,7 +1274,7 @@ def get_file_diff(self, project_dir, file_path, output_diff, version_from, versi diffs.append(mp.fpath_cache(file_history["history"][v]["diff"]["path"], v)) # concatenate diffs, if needed - output_dir = os.path.dirname(output_diff) + output_dir = os.path.abspath(os.path.dirname(output_diff)) if len(diffs) >= 1: os.makedirs(output_dir, exist_ok=True) if len(diffs) > 1: diff --git a/mergin/client_pull.py b/mergin/client_pull.py index 0b15dab6..1301e009 100644 --- a/mergin/client_pull.py +++ b/mergin/client_pull.py @@ -182,7 +182,7 @@ def __init__(self, dest_file, downloaded_items: typing.List[DownloadQueueItem], def from_chunks(self): """Merges downloaded chunks into a single file at dest_file path""" - file_dir = os.path.dirname(self.dest_file) + file_dir = os.path.abspath(os.path.dirname(self.dest_file)) os.makedirs(file_dir, exist_ok=True) with open(self.dest_file, "wb") as final: @@ -437,7 +437,7 @@ def apply(self, directory, mp: Union[MerginProject, "DownloadScratchContext"]): file_dir = os.path.dirname(os.path.normpath(os.path.join(directory, self.file_path))) dest_file_path = os.path.join(file_dir, basename) else: - file_dir = os.path.dirname(os.path.normpath(self.destination_file)) + file_dir = os.path.dirname(os.path.abspath(self.destination_file)) dest_file_path = self.destination_file os.makedirs(file_dir, exist_ok=True) diff --git a/mergin/report.py b/mergin/report.py index 5b9cae43..bbc23690 100644 --- a/mergin/report.py +++ b/mergin/report.py @@ -284,7 +284,7 @@ def create_report(mc, directory, since, to, out_file): raise ClientError("Reporting failed, please check log for details") # export report to csv file - out_dir = os.path.dirname(out_file) + out_dir = os.path.abspath(os.path.dirname(out_file)) os.makedirs(out_dir, exist_ok=True) with open(out_file, "w", newline="") as f_csv: writer = csv.DictWriter(f_csv, fieldnames=headers) diff --git a/mergin/test/test_client_pull.py b/mergin/test/test_client_pull.py index c20fe44b..95393087 100644 --- a/mergin/test/test_client_pull.py +++ b/mergin/test/test_client_pull.py @@ -3,7 +3,7 @@ import pytest from mergin.common import DeltaChangeType, CHUNK_SIZE from mergin.models import ProjectDeltaChange, ProjectDeltaItemDiff -from mergin.client_pull import get_download_diff_files, get_download_items +from mergin.client_pull import DownloadQueueItem, UpdateTask, get_download_diff_files, get_download_items def test_get_diff_download_files(): @@ -74,3 +74,20 @@ def test_get_download_items(): assert items[0].file_path == "base.gpkg" assert items[0].size == 50 assert items[0].download_file_path == os.path.join(tmp_dir, "diff_file.0") + + +@pytest.mark.parametrize("output_file", ["out.txt", "./out.txt"]) +def test_update_task_apply_to_bare_filename(output_file, monkeypatch): + """Single file download must accept an output path without a directory part.""" + with tempfile.TemporaryDirectory() as tmp_dir: + chunk_path = os.path.join(tmp_dir, "data.txt.0") + with open(chunk_path, "wb") as f: + f.write(b"content") + item = DownloadQueueItem("data.txt", 7, "v1", False, 0, chunk_path) + task = UpdateTask("data.txt", [item], destination_file=output_file) + + monkeypatch.chdir(tmp_dir) + task.apply(tmp_dir, None) + + with open(os.path.join(tmp_dir, "out.txt"), "rb") as f: + assert f.read() == b"content"