Skip to content

Commit

Permalink
Fix rmtree to remove directories with read-only files
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Jul 10, 2019
1 parent 2c402f4 commit 5d32623
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 3 deletions.
2 changes: 2 additions & 0 deletions changelog/5524.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix issue where ``tmp_path`` and ``tmpdir`` would not remove directories containing files marked as read-only,
which could lead to pytest crashing when executed a second time with the ``--basetemp`` option.
11 changes: 8 additions & 3 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,15 @@ def ensure_reset_dir(path):


def rmtree(path, force=False):
def force_writable_and_retry(function, path, excinfo):
import stat

mode = os.stat(path).st_mode
os.chmod(path, mode | stat.S_IWRITE)
function(path)

if force:
# NOTE: ignore_errors might leave dead folders around.
# Python needs a rm -rf as a followup.
shutil.rmtree(str(path), ignore_errors=True)
shutil.rmtree(str(path), onerror=force_writable_and_retry)
else:
shutil.rmtree(str(path))

Expand Down
48 changes: 48 additions & 0 deletions testing/test_tmpdir.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os
import stat
import sys

import attr
Expand Down Expand Up @@ -326,6 +328,32 @@ def test_rmtree(self, tmp_path):
rmtree(adir, force=True)
assert not adir.exists()

def test_rmtree_with_read_only_file(self, tmp_path):
"""Ensure rmtree can remove directories with read-only files in them (#5524)"""
from _pytest.pathlib import rmtree

fn = tmp_path / "dir/foo.txt"
fn.parent.mkdir()

fn.touch()
os.chmod(str(fn), stat.S_IREAD)
rmtree(fn.parent, force=True)

assert not fn.parent.is_dir()

def test_rmtree_with_read_only_directory(self, tmp_path):
"""Ensure rmtree can remove read-only directories (#5524)"""
from _pytest.pathlib import rmtree

adir = tmp_path / "dir"
adir.mkdir()

(adir / "foo.txt").touch()
os.chmod(str(adir), stat.S_IREAD)
rmtree(adir, force=True)

assert not adir.is_dir()

def test_cleanup_ignores_symlink(self, tmp_path):
the_symlink = tmp_path / (self.PREFIX + "current")
attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5"))
Expand All @@ -349,3 +377,23 @@ def attempt_symlink_to(path, to_path):

def test_tmpdir_equals_tmp_path(tmpdir, tmp_path):
assert Path(tmpdir) == tmp_path


def test_basetemp_with_read_only_files(testdir):
"""Integration test for #5524"""
testdir.makepyfile(
"""
import os
import stat
def test(tmp_path):
fn = tmp_path / 'foo.txt'
fn.write_text('hello')
os.chmod(str(fn), stat.S_IREAD)
"""
)
result = testdir.runpytest("--basetemp=tmp")
assert result.ret == 0
# running a second time and ensure we don't crash
result = testdir.runpytest("--basetemp=tmp")
assert result.ret == 0

0 comments on commit 5d32623

Please sign in to comment.