diff --git a/git/util.py b/git/util.py index 8e77533fb..46891ec30 100644 --- a/git/util.py +++ b/git/util.py @@ -211,7 +211,7 @@ def rmfile(path: PathLike) -> None: treatment.""" if osp.isfile(path): if sys.platform == "win32": - os.chmod(path, 0o777) + os.chmod(path, stat.S_IWUSR) os.remove(path) diff --git a/test/test_util.py b/test/test_util.py index bfde35aea..ab233feeb 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -41,6 +41,7 @@ is_cygwin_git, join_path, remove_password_if_present, + rmfile, rmtree, stream_copy, ) @@ -213,6 +214,21 @@ def preen_dir2(): assert old_mode == new_mode, f"Should stay {old_mode:#o}, became {new_mode:#o}." +class TestRmfile: + """Tests for :func:`git.util.rmfile`.""" + + def test_deletes_readonly_file(self): + # Automatically works on Unix, but requires special handling on Windows. + with writable_fixture("rmfile.sh") as temporary: + file = temporary.path().resolve() / "readonly" + # rmfile ignores missing paths, so check that the fixture made a read-only file. + assert file.is_file() + assert not file.stat().st_mode & stat.S_IWUSR + + rmfile(file) + assert not file.exists() + + def _xfail_param(*values, **xfail_kwargs): """Build a pytest.mark.parametrize parameter that carries an xfail mark.""" return pytest.param(*values, marks=pytest.mark.xfail(**xfail_kwargs)) diff --git a/tests/fixtures/rmfile.sh b/tests/fixtures/rmfile.sh new file mode 100644 index 000000000..147315e3a --- /dev/null +++ b/tests/fixtures/rmfile.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu +touch readonly +chmod a-w readonly