From 5c4209aa593f9d406f5c9ce3a74343e5f58a4f42 Mon Sep 17 00:00:00 2001 From: Gaven Lan Ren Date: Wed, 28 Oct 2026 14:34:17 -0400 Subject: [PATCH] fix: accept current-directory files in parent assurance `assure_directory_exists("notes.txt", is_file=True)` attempted to create the empty dirname and raised `FileNotFoundError`, despite the current directory already being present. Interpret an empty parent as `os.curdir` for file inputs. Preserve the helper's return convention, nested parent creation and failures when a parent is an existing regular file. Use a native scripted filesystem fixture for string and pathlib inputs. Two current-directory cases fail before the fix, while six controls pass. All eight cases pass afterward; utility and typing modules pass on both backends (129 passed, 39 skipped each). Applicable pre-commit checks, including shellcheck, pass. Assisted-by: OpenAI Codex (model: unknown, autonomous) --- git/util.py | 2 +- test/test_assure_directory.py | 42 ++++++++++++++++++++++++++++++ tests/fixtures/assure-directory.sh | 4 +++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/test_assure_directory.py create mode 100644 tests/fixtures/assure-directory.sh diff --git a/git/util.py b/git/util.py index 2d4cc7c23..c7450b53d 100644 --- a/git/util.py +++ b/git/util.py @@ -401,7 +401,7 @@ def assure_directory_exists(path: PathLike, is_file: bool = False) -> bool: ``True`` if the directory was created, ``False`` if it already existed. """ if is_file: - path = osp.dirname(path) + path = osp.dirname(path) or osp.curdir # END handle file if not osp.isdir(path): os.makedirs(path, exist_ok=True) diff --git a/test/test_assure_directory.py b/test/test_assure_directory.py new file mode 100644 index 000000000..04de1993f --- /dev/null +++ b/test/test_assure_directory.py @@ -0,0 +1,42 @@ +from pathlib import Path + +import pytest + +from git.util import assure_directory_exists +from test.lib.fixtures import writable_fixture + + +@pytest.fixture +def directory_fixture(monkeypatch): + with writable_fixture("assure-directory.sh") as temporary: + directory = temporary.path().resolve() + with monkeypatch.context() as context: + context.chdir(directory) + yield directory + + +@pytest.mark.parametrize("path_type", [str, Path]) +def test_file_in_current_directory_already_has_parent(directory_fixture, path_type): + assert assure_directory_exists(path_type("notes.txt"), is_file=True) is False + assert not (directory_fixture / "notes.txt").exists() + + +@pytest.mark.parametrize("path_type", [str, Path]) +def test_nested_file_creates_only_missing_parent_directories(directory_fixture, path_type): + filename = path_type("new/nested/notes.txt") + assert assure_directory_exists(filename, is_file=True) is True + assert (directory_fixture / "new/nested").is_dir() + assert not (directory_fixture / "new/nested/notes.txt").exists() + assert assure_directory_exists(filename, is_file=True) is False + + +@pytest.mark.parametrize("path_type", [str, Path]) +def test_existing_directory_is_unchanged(directory_fixture, path_type): + assert assure_directory_exists(path_type("existing")) is False + + +@pytest.mark.parametrize("path_type", [str, Path]) +def test_file_cannot_be_used_as_parent_directory(directory_fixture, path_type): + with pytest.raises(FileExistsError): + assure_directory_exists(path_type("blocked/notes.txt"), is_file=True) + assert (directory_fixture / "blocked").read_text() == "occupied\n" diff --git a/tests/fixtures/assure-directory.sh b/tests/fixtures/assure-directory.sh new file mode 100644 index 000000000..438236991 --- /dev/null +++ b/tests/fixtures/assure-directory.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu +mkdir existing +printf 'occupied\n' > blocked