From d62bef43623eeada2eef810c824d95b4797e3c70 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 17 May 2019 11:06:59 -0700 Subject: [PATCH] Document caveats for UNC paths in uninstall and add .pth unit tests --- news/6516.doc | 1 + src/pip/_internal/req/req_uninstall.py | 5 ++++ tests/unit/test_req_uninstall.py | 35 +++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 news/6516.doc diff --git a/news/6516.doc b/news/6516.doc new file mode 100644 index 00000000000..e8b880ef4b4 --- /dev/null +++ b/news/6516.doc @@ -0,0 +1 @@ +Document caveats for UNC paths in uninstall and add .pth unit tests. diff --git a/src/pip/_internal/req/req_uninstall.py b/src/pip/_internal/req/req_uninstall.py index 733301cee6c..359fb7cded7 100644 --- a/src/pip/_internal/req/req_uninstall.py +++ b/src/pip/_internal/req/req_uninstall.py @@ -593,6 +593,11 @@ def add(self, entry): # backslashes. This is correct for entries that describe absolute # paths outside of site-packages, but all the others use forward # slashes. + # os.path.splitdrive is used instead of os.path.isabs because isabs + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or + # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. if WINDOWS and not os.path.splitdrive(entry)[0]: entry = entry.replace('\\', '/') self.entries.add(entry) diff --git a/tests/unit/test_req_uninstall.py b/tests/unit/test_req_uninstall.py index 37edb7dfa42..99b315b1550 100644 --- a/tests/unit/test_req_uninstall.py +++ b/tests/unit/test_req_uninstall.py @@ -1,11 +1,12 @@ import os +import sys import pytest from mock import Mock import pip._internal.req.req_uninstall from pip._internal.req.req_uninstall import ( - StashedUninstallPathSet, UninstallPathSet, compact, + StashedUninstallPathSet, UninstallPathSet, UninstallPthEntries, compact, compress_for_output_listing, compress_for_rename, uninstallation_paths, ) from tests.lib import create_file @@ -129,6 +130,38 @@ def test_add(self, tmpdir, monkeypatch): ups.add(file_nonexistent) assert ups.paths == {file_extant} + def test_add_pth(self, tmpdir, monkeypatch): + monkeypatch.setattr(pip._internal.req.req_uninstall, 'is_local', + mock_is_local) + # Fix case for windows tests + tmpdir = os.path.normcase(tmpdir) + 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: + check = set([tmpdir, relative, share, share_com]) + else: + check = set([tmpdir, relative]) + assert pth.entries == check + @pytest.mark.skipif("sys.platform == 'win32'") def test_add_symlink(self, tmpdir, monkeypatch): monkeypatch.setattr(pip._internal.req.req_uninstall, 'is_local',