From d4c72aa37d16e19c0f157e86203a66cad32a9c80 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Wed, 16 Feb 2022 08:36:21 +0100 Subject: [PATCH] Prevent removing of the system packages installed under /usr/lib when pip install --upgrade is executed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: rhbz#1550368 Co-Authored-By: Michal Cyprian Co-Authored-By: Victor Stinner Co-Authored-By: Petr Viktorin Co-Authored-By: Lumir Balhar Co-Authored-By: Miro HronĨok Co-Authored-By: Karolina Surma --- src/pip/_internal/metadata/base.py | 12 +++++++++++- src/pip/_internal/req/req_install.py | 2 +- src/pip/_internal/resolution/legacy/resolver.py | 4 +++- src/pip/_internal/resolution/resolvelib/factory.py | 12 ++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/metadata/base.py b/src/pip/_internal/metadata/base.py index 1a5a781..3df4ab2 100644 --- a/src/pip/_internal/metadata/base.py +++ b/src/pip/_internal/metadata/base.py @@ -24,7 +24,7 @@ from pip._vendor.packaging.utils import NormalizedName from pip._vendor.packaging.version import LegacyVersion, Version from pip._internal.exceptions import NoneMetadataError -from pip._internal.locations import site_packages, user_site +from pip._internal.locations import get_scheme, site_packages, user_site from pip._internal.models.direct_url import ( DIRECT_URL_METADATA_NAME, DirectUrl, @@ -441,6 +441,16 @@ class BaseDistribution(Protocol): or self._iter_declared_entries_from_legacy() ) + @property + def in_install_path(self) -> bool: + """ + Return True if given Distribution is installed in + path matching distutils_scheme layout. + """ + norm_path = normalize_path(self.installed_location) + return norm_path.startswith(normalize_path( + get_scheme("").purelib.split('python')[0])) + class BaseEnvironment: """An environment containing distributions to introspect.""" diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 02dbda1..a952357 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -406,7 +406,7 @@ class InstallRequirement: f"lack sys.path precedence to {existing_dist.raw_name} " f"in {existing_dist.location}" ) - else: + elif existing_dist.in_install_path: self.should_reinstall = True else: if self.editable: diff --git a/src/pip/_internal/resolution/legacy/resolver.py b/src/pip/_internal/resolution/legacy/resolver.py index 8c149d4..a944906 100644 --- a/src/pip/_internal/resolution/legacy/resolver.py +++ b/src/pip/_internal/resolution/legacy/resolver.py @@ -203,7 +203,9 @@ class Resolver(BaseResolver): """ # Don't uninstall the conflict if doing a user install and the # conflict is not a user install. - if not self.use_user_site or req.satisfied_by.in_usersite: + if ((not self.use_user_site + or req.satisfied_by.in_usersite) + and req.satisfied_by.in_install_path): req.should_reinstall = True req.satisfied_by = None diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 261d8d5..d1b7728 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -1,6 +1,8 @@ import contextlib import functools import logging +import sys +import sysconfig from typing import ( TYPE_CHECKING, Dict, @@ -564,6 +566,16 @@ class Factory: if dist is None: # Not installed, no uninstallation required. return None + # Prevent uninstalling packages from /usr + try: + if dist.installed_location in ( + sysconfig.get_path('purelib', scheme='rpm_prefix', vars={'base': sys.base_prefix}), + sysconfig.get_path('platlib', scheme='rpm_prefix', vars={'base': sys.base_prefix}), + ): + return None + except KeyError: # this Python doesn't have 'rpm_prefix' scheme yet + pass + # We're installing into global site. The current installation must # be uninstalled, no matter it's in global or user site, because the # user site installation has precedence over global. -- 2.35.1