diff --git a/changelog/4135.bugfix.rst b/changelog/4135.bugfix.rst new file mode 100644 index 000000000..d4f8a851d --- /dev/null +++ b/changelog/4135.bugfix.rst @@ -0,0 +1 @@ +pathlib based tmpdir cleanup now correctly handles symlinks in the folder. diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 439f4d9ba..cda5e9947 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -173,6 +173,8 @@ def delete_a_numbered_dir(path): def ensure_deletable(path, consider_lock_dead_if_created_before): """checks if a lock exists and breaks it if its considered dead""" + if path.is_symlink(): + return False lock = get_lock_path(path) if not lock.exists(): return True diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 9244e309d..9f4158eb7 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,7 +1,10 @@ from __future__ import absolute_import, division, print_function import sys -import py + +import six + import pytest +from _pytest.pathlib import Path def test_tmpdir_fixture(testdir): @@ -65,10 +68,6 @@ def test_basetemp(testdir): assert mytemp.join("hello").check() -@pytest.mark.skipif( - not hasattr(py.path.local, "mksymlinkto"), - reason="symlink not available on this platform", -) def test_tmpdir_always_is_realpath(testdir): # the reason why tmpdir should be a realpath is that # when you cd to it and do "os.getcwd()" you will anyway @@ -78,7 +77,7 @@ def test_tmpdir_always_is_realpath(testdir): # os.environ["PWD"] realtemp = testdir.tmpdir.mkdir("myrealtemp") linktemp = testdir.tmpdir.join("symlinktemp") - linktemp.mksymlinkto(realtemp) + attempt_symlink_to(linktemp, str(realtemp)) p = testdir.makepyfile( """ def test_1(tmpdir): @@ -232,7 +231,7 @@ class TestNumberedDir(object): assert not lock.exists() - def test_cleanup_keep(self, tmp_path): + def _do_cleanup(self, tmp_path): self.test_make(tmp_path) from _pytest.pathlib import cleanup_numbered_dir @@ -242,6 +241,9 @@ class TestNumberedDir(object): keep=2, consider_lock_dead_if_created_before=0, ) + + def test_cleanup_keep(self, tmp_path): + self._do_cleanup(tmp_path) a, b = tmp_path.iterdir() print(a, b) @@ -275,3 +277,19 @@ class TestNumberedDir(object): rmtree(adir, force=True) assert not adir.exists() + + def test_cleanup_symlink(self, tmp_path): + the_symlink = tmp_path / (self.PREFIX + "current") + attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5")) + self._do_cleanup(tmp_path) + + +def attempt_symlink_to(path, to_path): + """Try to make a symlink from "path" to "to_path", skipping in case this platform + does not support it or we don't have sufficient privileges (common on Windows).""" + if sys.platform.startswith("win") and six.PY2: + pytest.skip("pathlib for some reason cannot make symlinks on Python 2") + try: + Path(path).symlink_to(Path(to_path)) + except OSError: + pytest.skip("could not create symbolic link")