From 2c3f3a590ddfc151a456b44a5f96f0f603d178e9 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 151fd6d..f9109cd 100644 --- a/src/pip/_internal/metadata/base.py +++ b/src/pip/_internal/metadata/base.py @@ -28,7 +28,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, @@ -560,6 +560,16 @@ class BaseDistribution(Protocol): for extra in self._iter_egg_info_extras(): metadata["Provides-Extra"] = extra + @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 a1e376c..ed7facf 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -416,7 +416,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 fb49d41..040f2c1 100644 --- a/src/pip/_internal/resolution/legacy/resolver.py +++ b/src/pip/_internal/resolution/legacy/resolver.py @@ -325,7 +325,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 a4c24b5..e7e2da9 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, @@ -549,6 +551,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.3