2015-03-19 01:37:24 +01:00
|
|
|
import os
|
2019-05-17 20:06:59 +02:00
|
|
|
import sys
|
2022-06-07 11:52:38 +02:00
|
|
|
from pathlib import Path
|
2021-09-28 11:17:27 +02:00
|
|
|
from typing import Iterator, List, Optional, Tuple
|
2021-02-10 11:38:21 +01:00
|
|
|
from unittest.mock import Mock
|
2015-03-19 01:37:24 +01:00
|
|
|
|
|
|
|
import pytest
|
2017-06-13 14:17:00 +02:00
|
|
|
|
2017-08-31 17:48:18 +02:00
|
|
|
import pip._internal.req.req_uninstall
|
|
|
|
from pip._internal.req.req_uninstall import (
|
2019-07-22 06:45:27 +02:00
|
|
|
StashedUninstallPathSet,
|
|
|
|
UninstallPathSet,
|
2019-07-30 20:36:15 +02:00
|
|
|
UninstallPthEntries,
|
2019-07-22 06:45:27 +02:00
|
|
|
compact,
|
|
|
|
compress_for_output_listing,
|
|
|
|
compress_for_rename,
|
|
|
|
uninstallation_paths,
|
2017-06-05 13:48:41 +02:00
|
|
|
)
|
|
|
|
from tests.lib import create_file
|
2015-03-19 01:37:24 +01:00
|
|
|
|
2015-03-21 00:18:33 +01:00
|
|
|
|
2015-03-21 00:09:34 +01:00
|
|
|
# Pretend all files are local, so UninstallPathSet accepts files in the tmpdir,
|
|
|
|
# outside the virtualenv
|
2023-03-20 18:43:33 +01:00
|
|
|
def mock_permitted(ups: UninstallPathSet, path: str) -> bool:
|
2015-03-21 00:09:34 +01:00
|
|
|
return True
|
2015-03-19 17:35:53 +01:00
|
|
|
|
2015-03-21 00:18:33 +01:00
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_uninstallation_paths() -> None:
|
2020-12-24 22:23:07 +01:00
|
|
|
class dist:
|
2021-09-28 11:17:27 +02:00
|
|
|
def iter_declared_entries(self) -> Optional[Iterator[str]]:
|
|
|
|
yield "file.py"
|
|
|
|
yield "file.pyc"
|
|
|
|
yield "file.so"
|
|
|
|
yield "nopyc.py"
|
2021-08-13 15:23:45 +02:00
|
|
|
|
2016-11-11 00:41:48 +01:00
|
|
|
location = ""
|
|
|
|
|
|
|
|
d = dist()
|
|
|
|
|
|
|
|
paths = list(uninstallation_paths(d))
|
|
|
|
|
|
|
|
expected = [
|
|
|
|
"file.py",
|
|
|
|
"file.pyc",
|
2018-06-21 19:30:20 +02:00
|
|
|
"file.pyo",
|
2016-11-11 00:41:48 +01:00
|
|
|
"file.so",
|
|
|
|
"nopyc.py",
|
2018-06-21 19:30:20 +02:00
|
|
|
"nopyc.pyc",
|
|
|
|
"nopyc.pyo",
|
|
|
|
]
|
2016-11-11 00:41:48 +01:00
|
|
|
|
|
|
|
assert paths == expected
|
|
|
|
|
|
|
|
# Avoid an easy 'unique generator' bug
|
|
|
|
paths2 = list(uninstallation_paths(d))
|
|
|
|
|
|
|
|
assert paths2 == paths
|
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_compressed_listing(tmpdir: Path) -> None:
|
|
|
|
def in_tmpdir(paths: List[str]) -> List[str]:
|
2017-06-05 13:48:41 +02:00
|
|
|
li = []
|
|
|
|
for path in paths:
|
2018-07-23 14:11:57 +02:00
|
|
|
li.append(str(os.path.join(tmpdir, path.replace("/", os.path.sep))))
|
2017-06-05 13:48:41 +02:00
|
|
|
return li
|
|
|
|
|
|
|
|
sample = in_tmpdir(
|
|
|
|
[
|
|
|
|
"lib/mypkg.dist-info/METADATA",
|
|
|
|
"lib/mypkg.dist-info/PKG-INFO",
|
|
|
|
"lib/mypkg/would_be_removed.txt",
|
|
|
|
"lib/mypkg/would_be_skipped.skip.txt",
|
|
|
|
"lib/mypkg/__init__.py",
|
|
|
|
"lib/mypkg/my_awesome_code.py",
|
|
|
|
"lib/mypkg/__pycache__/my_awesome_code-magic.pyc",
|
|
|
|
"lib/mypkg/support/support_file.py",
|
|
|
|
"lib/mypkg/support/more_support.py",
|
|
|
|
"lib/mypkg/support/would_be_skipped.skip.py",
|
|
|
|
"lib/mypkg/support/__pycache__/support_file-magic.pyc",
|
|
|
|
"lib/random_other_place/file_without_a_dot_pyc",
|
|
|
|
"bin/mybin",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
# Create the required files
|
|
|
|
for fname in sample:
|
|
|
|
create_file(fname, "random blub")
|
|
|
|
|
|
|
|
# Remove the files to be skipped from the paths
|
2017-06-13 06:32:40 +02:00
|
|
|
sample = [path for path in sample if ".skip." not in path]
|
2017-06-05 13:48:41 +02:00
|
|
|
|
|
|
|
expected_remove = in_tmpdir(
|
|
|
|
[
|
|
|
|
"bin/mybin",
|
|
|
|
"lib/mypkg.dist-info/*",
|
|
|
|
"lib/mypkg/*",
|
|
|
|
"lib/random_other_place/file_without_a_dot_pyc",
|
|
|
|
]
|
|
|
|
)
|
2021-08-13 15:23:45 +02:00
|
|
|
|
2017-06-05 13:48:41 +02:00
|
|
|
expected_skip = in_tmpdir(
|
|
|
|
[
|
|
|
|
"lib/mypkg/would_be_skipped.skip.txt",
|
|
|
|
"lib/mypkg/support/would_be_skipped.skip.py",
|
|
|
|
]
|
|
|
|
)
|
2021-08-13 15:23:45 +02:00
|
|
|
|
2018-11-21 23:33:15 +01:00
|
|
|
expected_rename = in_tmpdir(
|
|
|
|
[
|
|
|
|
"bin/",
|
|
|
|
"lib/mypkg.dist-info/",
|
|
|
|
"lib/mypkg/would_be_removed.txt",
|
|
|
|
"lib/mypkg/__init__.py",
|
|
|
|
"lib/mypkg/my_awesome_code.py",
|
|
|
|
"lib/mypkg/__pycache__/",
|
|
|
|
"lib/mypkg/support/support_file.py",
|
|
|
|
"lib/mypkg/support/more_support.py",
|
|
|
|
"lib/mypkg/support/__pycache__/",
|
|
|
|
"lib/random_other_place/",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2017-06-05 13:48:41 +02:00
|
|
|
will_remove, will_skip = compress_for_output_listing(sample)
|
2018-11-21 23:33:15 +01:00
|
|
|
will_rename = compress_for_rename(sample)
|
2017-06-05 13:48:41 +02:00
|
|
|
assert sorted(expected_skip) == sorted(compact(will_skip))
|
|
|
|
assert sorted(expected_remove) == sorted(compact(will_remove))
|
2018-11-21 23:33:15 +01:00
|
|
|
assert sorted(expected_rename) == sorted(compact(will_rename))
|
2017-06-05 13:48:41 +02:00
|
|
|
|
|
|
|
|
2020-12-24 22:23:07 +01:00
|
|
|
class TestUninstallPathSet:
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_add(self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
2023-03-23 22:42:30 +01:00
|
|
|
monkeypatch.setattr(
|
|
|
|
pip._internal.req.req_uninstall.UninstallPathSet,
|
|
|
|
"_permitted",
|
|
|
|
mock_permitted,
|
|
|
|
)
|
2015-12-05 19:39:31 +01:00
|
|
|
# Fix case for windows tests
|
|
|
|
file_extant = os.path.normcase(os.path.join(tmpdir, "foo"))
|
2016-06-10 21:27:07 +02:00
|
|
|
file_nonexistent = os.path.normcase(os.path.join(tmpdir, "nonexistent"))
|
2015-03-19 17:35:53 +01:00
|
|
|
with open(file_extant, "w"):
|
|
|
|
pass
|
2015-03-19 01:37:24 +01:00
|
|
|
|
|
|
|
ups = UninstallPathSet(dist=Mock())
|
2021-09-28 12:51:46 +02:00
|
|
|
assert ups._paths == set()
|
2015-03-19 01:37:24 +01:00
|
|
|
ups.add(file_extant)
|
2021-09-28 12:51:46 +02:00
|
|
|
assert ups._paths == {file_extant}
|
2015-03-19 01:37:24 +01:00
|
|
|
|
2016-06-10 21:27:07 +02:00
|
|
|
ups.add(file_nonexistent)
|
2021-09-28 12:51:46 +02:00
|
|
|
assert ups._paths == {file_extant}
|
2015-03-19 01:37:24 +01:00
|
|
|
|
2022-06-07 11:52:38 +02:00
|
|
|
def test_add_pth(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
2023-03-23 22:42:30 +01:00
|
|
|
monkeypatch.setattr(
|
|
|
|
pip._internal.req.req_uninstall.UninstallPathSet,
|
|
|
|
"_permitted",
|
|
|
|
mock_permitted,
|
|
|
|
)
|
2019-05-17 20:06:59 +02:00
|
|
|
# Fix case for windows tests
|
2022-06-07 11:52:38 +02:00
|
|
|
tmpdir = os.path.normcase(tmp_path)
|
2019-05-17 20:06:59 +02:00
|
|
|
on_windows = sys.platform == "win32"
|
|
|
|
pth_file = os.path.join(tmpdir, "foo.pth")
|
|
|
|
relative = "../../example"
|
|
|
|
if on_windows:
|
|
|
|
share = "\\\\example\\share\\"
|
|
|
|
share_com = "\\\\example.com\\share\\"
|
|
|
|
# Create a .pth file for testing
|
|
|
|
with open(pth_file, "w") as f:
|
|
|
|
f.writelines([tmpdir, "\n", relative, "\n"])
|
|
|
|
if on_windows:
|
|
|
|
f.writelines([share, "\n", share_com, "\n"])
|
|
|
|
# Add paths to be removed
|
|
|
|
pth = UninstallPthEntries(pth_file)
|
|
|
|
pth.add(tmpdir)
|
|
|
|
pth.add(relative)
|
|
|
|
if on_windows:
|
|
|
|
pth.add(share)
|
|
|
|
pth.add(share_com)
|
|
|
|
# Check that the paths were added to entries
|
|
|
|
if on_windows:
|
2021-02-10 09:45:29 +01:00
|
|
|
check = {tmpdir, relative, share, share_com}
|
2019-05-17 20:06:59 +02:00
|
|
|
else:
|
2021-02-10 09:45:29 +01:00
|
|
|
check = {tmpdir, relative}
|
2019-05-17 20:06:59 +02:00
|
|
|
assert pth.entries == check
|
|
|
|
|
2015-03-19 01:37:24 +01:00
|
|
|
@pytest.mark.skipif("sys.platform == 'win32'")
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_add_symlink(self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
2023-03-23 22:42:30 +01:00
|
|
|
monkeypatch.setattr(
|
|
|
|
pip._internal.req.req_uninstall.UninstallPathSet,
|
|
|
|
"_permitted",
|
|
|
|
mock_permitted,
|
|
|
|
)
|
2015-03-21 00:09:34 +01:00
|
|
|
f = os.path.join(tmpdir, "foo")
|
2015-03-19 17:35:53 +01:00
|
|
|
with open(f, "w"):
|
|
|
|
pass
|
2018-06-25 13:51:41 +02:00
|
|
|
foo_link = os.path.join(tmpdir, "foo_link")
|
|
|
|
os.symlink(f, foo_link)
|
2015-03-19 01:37:24 +01:00
|
|
|
|
|
|
|
ups = UninstallPathSet(dist=Mock())
|
2018-06-25 13:51:41 +02:00
|
|
|
ups.add(foo_link)
|
2021-09-28 12:51:46 +02:00
|
|
|
assert ups._paths == {foo_link}
|
2015-10-03 18:17:26 +02:00
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_compact_shorter_path(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
2023-03-23 22:42:30 +01:00
|
|
|
monkeypatch.setattr(
|
|
|
|
pip._internal.req.req_uninstall.UninstallPathSet,
|
|
|
|
"_permitted",
|
|
|
|
mock_permitted,
|
|
|
|
)
|
2015-10-03 18:17:26 +02:00
|
|
|
monkeypatch.setattr("os.path.exists", lambda p: True)
|
2015-12-05 14:39:13 +01:00
|
|
|
# This deals with nt/posix path differences
|
|
|
|
short_path = os.path.normcase(
|
|
|
|
os.path.abspath(os.path.join(os.path.sep, "path"))
|
2021-08-13 15:23:45 +02:00
|
|
|
)
|
2015-10-03 18:17:26 +02:00
|
|
|
ups = UninstallPathSet(dist=Mock())
|
2015-12-05 14:39:13 +01:00
|
|
|
ups.add(short_path)
|
|
|
|
ups.add(os.path.join(short_path, "longer"))
|
2021-09-28 12:51:46 +02:00
|
|
|
assert compact(ups._paths) == {short_path}
|
2015-10-03 18:17:26 +02:00
|
|
|
|
|
|
|
@pytest.mark.skipif("sys.platform == 'win32'")
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_detect_symlink_dirs(
|
|
|
|
self, monkeypatch: pytest.MonkeyPatch, tmpdir: Path
|
|
|
|
) -> None:
|
2023-03-23 22:42:30 +01:00
|
|
|
monkeypatch.setattr(
|
|
|
|
pip._internal.req.req_uninstall.UninstallPathSet,
|
|
|
|
"_permitted",
|
|
|
|
mock_permitted,
|
|
|
|
)
|
2015-10-03 18:17:26 +02:00
|
|
|
|
|
|
|
# construct 2 paths:
|
|
|
|
# tmpdir/dir/file
|
|
|
|
# tmpdir/dirlink/file (where dirlink is a link to dir)
|
2019-07-02 07:00:32 +02:00
|
|
|
d = tmpdir.joinpath("dir")
|
2015-10-03 18:17:26 +02:00
|
|
|
d.mkdir()
|
2019-07-02 07:00:32 +02:00
|
|
|
dlink = tmpdir.joinpath("dirlink")
|
2015-10-03 18:17:26 +02:00
|
|
|
os.symlink(d, dlink)
|
2019-07-02 07:00:32 +02:00
|
|
|
d.joinpath("file").touch()
|
|
|
|
path1 = str(d.joinpath("file"))
|
|
|
|
path2 = str(dlink.joinpath("file"))
|
2015-10-03 18:17:26 +02:00
|
|
|
|
|
|
|
ups = UninstallPathSet(dist=Mock())
|
|
|
|
ups.add(path1)
|
|
|
|
ups.add(path2)
|
2021-09-28 12:51:46 +02:00
|
|
|
assert ups._paths == {path1}
|
2019-02-04 23:48:41 +01:00
|
|
|
|
|
|
|
|
2020-12-24 22:23:07 +01:00
|
|
|
class TestStashedUninstallPathSet:
|
2021-08-30 00:43:28 +02:00
|
|
|
WALK_RESULT: List[Tuple[str, List[str], List[str]]] = [
|
2019-02-04 23:48:41 +01:00
|
|
|
("A", ["B", "C"], ["a.py"]),
|
|
|
|
("A/B", ["D"], ["b.py"]),
|
|
|
|
("A/B/D", [], ["c.py"]),
|
|
|
|
("A/C", [], ["d.py", "e.py"]),
|
|
|
|
("A/E", ["F"], ["f.py"]),
|
|
|
|
("A/E/F", [], []),
|
|
|
|
("A/G", ["H"], ["g.py"]),
|
|
|
|
("A/G/H", [], ["h.py"]),
|
|
|
|
]
|
|
|
|
|
|
|
|
@classmethod
|
2021-08-30 00:43:28 +02:00
|
|
|
def mock_walk(cls, root: str) -> Iterator[Tuple[str, List[str], List[str]]]:
|
2019-02-04 23:48:41 +01:00
|
|
|
for dirname, subdirs, files in cls.WALK_RESULT:
|
|
|
|
dirname = os.path.sep.join(dirname.split("/"))
|
|
|
|
if dirname.startswith(root):
|
|
|
|
yield dirname[len(root) + 1 :], subdirs, files
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_compress_for_rename(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
2019-02-04 23:48:41 +01:00
|
|
|
paths = [
|
|
|
|
os.path.sep.join(p.split("/"))
|
|
|
|
for p in [
|
|
|
|
"A/B/b.py",
|
|
|
|
"A/B/D/c.py",
|
|
|
|
"A/C/d.py",
|
|
|
|
"A/E/f.py",
|
|
|
|
"A/G/g.py",
|
|
|
|
]
|
2021-08-13 15:23:45 +02:00
|
|
|
]
|
|
|
|
|
2019-02-04 23:48:41 +01:00
|
|
|
expected_paths = [
|
|
|
|
os.path.sep.join(p.split("/"))
|
|
|
|
for p in [
|
|
|
|
"A/B/", # selected everything below A/B
|
|
|
|
"A/C/d.py", # did not select everything below A/C
|
|
|
|
"A/E/", # only empty folders remain under A/E
|
|
|
|
"A/G/g.py", # non-empty folder remains under A/G
|
|
|
|
]
|
2021-08-13 15:23:45 +02:00
|
|
|
]
|
|
|
|
|
2019-02-04 23:48:41 +01:00
|
|
|
monkeypatch.setattr("os.walk", self.mock_walk)
|
|
|
|
|
|
|
|
actual_paths = compress_for_rename(paths)
|
|
|
|
assert set(expected_paths) == set(actual_paths)
|
|
|
|
|
|
|
|
@classmethod
|
2021-08-30 00:43:28 +02:00
|
|
|
def make_stash(
|
|
|
|
cls, tmpdir: Path, paths: List[str]
|
|
|
|
) -> Tuple[StashedUninstallPathSet, List[Tuple[str, str]]]:
|
2019-02-04 23:48:41 +01:00
|
|
|
for dirname, subdirs, files in cls.WALK_RESULT:
|
|
|
|
root = os.path.join(tmpdir, *dirname.split("/"))
|
|
|
|
if not os.path.exists(root):
|
|
|
|
os.mkdir(root)
|
|
|
|
for d in subdirs:
|
|
|
|
os.mkdir(os.path.join(root, d))
|
|
|
|
for f in files:
|
|
|
|
with open(os.path.join(root, f), "wb"):
|
|
|
|
pass
|
|
|
|
|
|
|
|
pathset = StashedUninstallPathSet()
|
|
|
|
|
|
|
|
paths = [os.path.join(tmpdir, *p.split("/")) for p in paths]
|
|
|
|
stashed_paths = [(p, pathset.stash(p)) for p in paths]
|
|
|
|
|
|
|
|
return pathset, stashed_paths
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_stash(self, tmpdir: Path) -> None:
|
2019-02-04 23:48:41 +01:00
|
|
|
pathset, stashed_paths = self.make_stash(
|
|
|
|
tmpdir,
|
|
|
|
[
|
|
|
|
"A/B/",
|
|
|
|
"A/C/d.py",
|
|
|
|
"A/E/",
|
|
|
|
"A/G/g.py",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
for old_path, new_path in stashed_paths:
|
|
|
|
assert not os.path.exists(old_path)
|
|
|
|
assert os.path.exists(new_path)
|
|
|
|
|
|
|
|
assert stashed_paths == pathset._moves
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_commit(self, tmpdir: Path) -> None:
|
2019-02-04 23:48:41 +01:00
|
|
|
pathset, stashed_paths = self.make_stash(
|
|
|
|
tmpdir,
|
|
|
|
[
|
|
|
|
"A/B/",
|
|
|
|
"A/C/d.py",
|
|
|
|
"A/E/",
|
|
|
|
"A/G/g.py",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
pathset.commit()
|
|
|
|
|
|
|
|
for old_path, new_path in stashed_paths:
|
|
|
|
assert not os.path.exists(old_path)
|
|
|
|
assert not os.path.exists(new_path)
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_rollback(self, tmpdir: Path) -> None:
|
2019-02-04 23:48:41 +01:00
|
|
|
pathset, stashed_paths = self.make_stash(
|
|
|
|
tmpdir,
|
|
|
|
[
|
|
|
|
"A/B/",
|
|
|
|
"A/C/d.py",
|
|
|
|
"A/E/",
|
|
|
|
"A/G/g.py",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
pathset.rollback()
|
|
|
|
|
|
|
|
for old_path, new_path in stashed_paths:
|
|
|
|
assert os.path.exists(old_path)
|
|
|
|
assert not os.path.exists(new_path)
|
2019-08-24 23:27:56 +02:00
|
|
|
|
|
|
|
@pytest.mark.skipif("sys.platform == 'win32'")
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_commit_symlinks(self, tmpdir: Path) -> None:
|
2019-08-24 23:27:56 +02:00
|
|
|
adir = tmpdir / "dir"
|
|
|
|
adir.mkdir()
|
|
|
|
dirlink = tmpdir / "dirlink"
|
|
|
|
dirlink.symlink_to(adir)
|
|
|
|
afile = tmpdir / "file"
|
|
|
|
afile.write_text("...")
|
|
|
|
filelink = tmpdir / "filelink"
|
|
|
|
filelink.symlink_to(afile)
|
|
|
|
|
|
|
|
pathset = StashedUninstallPathSet()
|
|
|
|
stashed_paths = []
|
2022-06-07 11:52:38 +02:00
|
|
|
stashed_paths.append(pathset.stash(os.fspath(dirlink)))
|
|
|
|
stashed_paths.append(pathset.stash(os.fspath(filelink)))
|
2019-08-24 23:27:56 +02:00
|
|
|
for stashed_path in stashed_paths:
|
|
|
|
assert os.path.lexists(stashed_path)
|
|
|
|
assert not os.path.exists(dirlink)
|
|
|
|
assert not os.path.exists(filelink)
|
|
|
|
|
|
|
|
pathset.commit()
|
|
|
|
|
|
|
|
# stash removed, links removed
|
|
|
|
for stashed_path in stashed_paths:
|
|
|
|
assert not os.path.lexists(stashed_path)
|
|
|
|
assert not os.path.lexists(dirlink) and not os.path.isdir(dirlink)
|
|
|
|
assert not os.path.lexists(filelink) and not os.path.isfile(filelink)
|
|
|
|
|
|
|
|
# link targets untouched
|
|
|
|
assert os.path.isdir(adir)
|
|
|
|
assert os.path.isfile(afile)
|
|
|
|
|
|
|
|
@pytest.mark.skipif("sys.platform == 'win32'")
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_rollback_symlinks(self, tmpdir: Path) -> None:
|
2019-08-24 23:27:56 +02:00
|
|
|
adir = tmpdir / "dir"
|
|
|
|
adir.mkdir()
|
|
|
|
dirlink = tmpdir / "dirlink"
|
|
|
|
dirlink.symlink_to(adir)
|
|
|
|
afile = tmpdir / "file"
|
|
|
|
afile.write_text("...")
|
|
|
|
filelink = tmpdir / "filelink"
|
|
|
|
filelink.symlink_to(afile)
|
|
|
|
|
|
|
|
pathset = StashedUninstallPathSet()
|
|
|
|
stashed_paths = []
|
2022-06-07 11:52:38 +02:00
|
|
|
stashed_paths.append(pathset.stash(os.fspath(dirlink)))
|
|
|
|
stashed_paths.append(pathset.stash(os.fspath(filelink)))
|
2019-08-24 23:27:56 +02:00
|
|
|
for stashed_path in stashed_paths:
|
|
|
|
assert os.path.lexists(stashed_path)
|
|
|
|
assert not os.path.lexists(dirlink)
|
|
|
|
assert not os.path.lexists(filelink)
|
|
|
|
|
|
|
|
pathset.rollback()
|
|
|
|
|
|
|
|
# stash removed, links restored
|
|
|
|
for stashed_path in stashed_paths:
|
|
|
|
assert not os.path.lexists(stashed_path)
|
|
|
|
assert os.path.lexists(dirlink) and os.path.isdir(dirlink)
|
|
|
|
assert os.path.lexists(filelink) and os.path.isfile(filelink)
|
|
|
|
|
|
|
|
# link targets untouched
|
|
|
|
assert os.path.isdir(adir)
|
|
|
|
assert os.path.isfile(afile)
|