1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 From f740c160e0e133d889e674046bc249d08c22bb34 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Wed, 3 Dec 2014 11:18:11 -0600 Subject: Prevent pip from removing system packages. Adjust is_local() to consider OS-owned paths non-local. Fix the error message for is_local() in the non-virtualenv case. Author: Geoffrey Thomas Bug-Debian: http://bugs.debian.org/771794 Origin: https://github.com/geofft/pip.git Forwarded: not-needed Reviewed-By: Donald Stufft Reviewed-By: Scott Kitterman Last-Update: 2014-12-04 Patch-Name: hands-off-system-packages.patch --- pip/utils/__init__.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pip/utils/__init__.py b/pip/utils/__init__.py index 4768ecb..7bf8de4 100644 --- a/pip/utils/__init__.py +++ b/pip/utils/__init__.py @@ -275,22 +275,40 @@ def renames(old, new): def is_local(path): """ - Return True if path is within sys.prefix, if we're running in a virtualenv. + Return True if this is a path pip is allowed to modify. - If we're not in a virtualenv, all paths are considered "local." + If we're in a virtualenv, sys.prefix points to the virtualenv's + prefix; only sys.prefix is considered local. + + If we're not in a virtualenv, in general we can modify anything. + However, if the OS vendor has configured distutils to install + somewhere other than sys.prefix (which could be a subdirectory of + sys.prefix, e.g. /usr/local), we consider sys.prefix itself nonlocal + and the domain of the OS vendor. (In other words, everything _other + than_ sys.prefix is considered local.) """ - if not running_under_virtualenv(): - return True - return normalize_path(path).startswith(normalize_path(sys.prefix)) + + path = normalize_path(path) + prefix = normalize_path(sys.prefix) + + if running_under_virtualenv(): + return path.startswith(normalize_path(sys.prefix)) + else: + from pip.locations import distutils_scheme + if path.startswith(prefix): + for local_path in distutils_scheme("").values(): + if path.startswith(normalize_path(local_path)): + return True + return False + else: + return True def dist_is_local(dist): """ - Return True if given Distribution object is installed locally - (i.e. within current virtualenv). - - Always True if we're not in a virtualenv. + Return True if given Distribution object is installed somewhere pip + is allowed to modify. """ return is_local(dist_location(dist))