1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Merge pull request #8054 from deveshks/correct-package-name-while-install

Canonicalize req name while doing pre-install package search
This commit is contained in:
Christopher Hunt 2020-07-06 12:10:33 -04:00 committed by GitHub
commit 334f06e224
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 79 additions and 17 deletions

1
news/5021.bugfix Normal file
View file

@ -0,0 +1 @@
Use canonical package names while looking up already installed packages.

View file

@ -19,7 +19,7 @@ from pip._internal.models.index import PyPI
from pip._internal.network.xmlrpc import PipXmlrpcTransport
from pip._internal.utils.compat import get_terminal_size
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import write_output
from pip._internal.utils.misc import get_distribution, write_output
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
@ -139,7 +139,7 @@ def print_results(hits, name_column_width=None, terminal_width=None):
try:
write_output(line)
if name in installed_packages:
dist = pkg_resources.get_distribution(name)
dist = get_distribution(name)
with indent_log():
if dist.version == latest:
write_output('INSTALLED: %s (latest)', dist.version)

View file

@ -41,6 +41,7 @@ from pip._internal.utils.misc import (
display_path,
dist_in_site_packages,
dist_in_usersite,
get_distribution,
get_installed_version,
hide_url,
redact_auth_from_url,
@ -434,12 +435,17 @@ class InstallRequirement(object):
# evaluate it.
no_marker = Requirement(str(self.req))
no_marker.marker = None
# pkg_resources uses the canonical name to look up packages, but
# the name passed passed to get_distribution is not canonicalized
# so we have to explicitly convert it to a canonical name
no_marker.name = canonicalize_name(no_marker.name)
try:
self.satisfied_by = pkg_resources.get_distribution(str(no_marker))
except pkg_resources.DistributionNotFound:
return
except pkg_resources.VersionConflict:
existing_dist = pkg_resources.get_distribution(
existing_dist = get_distribution(
self.req.name
)
if use_user_site:
@ -679,13 +685,11 @@ class InstallRequirement(object):
"""
assert self.req
try:
dist = pkg_resources.get_distribution(self.req.name)
except pkg_resources.DistributionNotFound:
dist = get_distribution(self.req.name)
if not dist:
logger.warning("Skipping %s as it is not installed.", self.name)
return None
else:
logger.info('Found existing installation: %s', dist)
logger.info('Found existing installation: %s', dist)
uninstalled_pathset = UninstallPathSet.from_dist(dist)
uninstalled_pathset.remove(auto_confirm, verbose)

View file

@ -7,7 +7,6 @@ import logging
import os.path
import sys
from pip._vendor import pkg_resources
from pip._vendor.packaging import version as packaging_version
from pip._vendor.six import ensure_binary
@ -19,7 +18,11 @@ from pip._internal.utils.filesystem import (
check_path_owner,
replace,
)
from pip._internal.utils.misc import ensure_dir, get_installed_version
from pip._internal.utils.misc import (
ensure_dir,
get_distribution,
get_installed_version,
)
from pip._internal.utils.packaging import get_installer
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
@ -110,11 +113,10 @@ def was_installed_by_pip(pkg):
This is used not to display the upgrade message when pip is in fact
installed by system package manager, such as dnf on Fedora.
"""
try:
dist = pkg_resources.get_distribution(pkg)
return "pip" == get_installer(dist)
except pkg_resources.DistributionNotFound:
dist = get_distribution(pkg)
if not dist:
return False
return "pip" == get_installer(dist)
def pip_self_version_check(session, options):

View file

@ -18,6 +18,7 @@ import sys
from collections import deque
from pip._vendor import pkg_resources
from pip._vendor.packaging.utils import canonicalize_name
# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is
# why we ignore the type on this import.
from pip._vendor.retrying import retry # type: ignore
@ -480,6 +481,40 @@ def get_installed_distributions(
]
def search_distribution(req_name):
# Canonicalize the name before searching in the list of
# installed distributions and also while creating the package
# dictionary to get the Distribution object
req_name = canonicalize_name(req_name)
packages = get_installed_distributions(skip=())
pkg_dict = {canonicalize_name(p.key): p for p in packages}
return pkg_dict.get(req_name)
def get_distribution(req_name):
"""Given a requirement name, return the installed Distribution object"""
# Search the distribution by looking through the working set
dist = search_distribution(req_name)
# If distribution could not be found, call working_set.require
# to update the working set, and try to find the distribution
# again.
# This might happen for e.g. when you install a package
# twice, once using setup.py develop and again using setup.py install.
# Now when run pip uninstall twice, the package gets removed
# from the working set in the first uninstall, so we have to populate
# the working set again so that pip knows about it and the packages
# gets picked up and is successfully uninstalled the second time too.
if not dist:
try:
pkg_resources.working_set.require(req_name)
except pkg_resources.DistributionNotFound:
return None
return search_distribution(req_name)
def egg_link_path(dist):
# type: (Distribution) -> Optional[str]
"""

View file

@ -1882,3 +1882,23 @@ def test_install_skip_work_dir_pkg(script, data):
assert 'Requirement already satisfied: simple' not in result.stdout
assert 'Successfully installed simple' in result.stdout
@pytest.mark.parametrize('package_name', ('simple-package', 'simple_package',
'simple.package'))
def test_install_verify_package_name_normalization(script, package_name):
"""
Test that install of a package again using a name which
normalizes to the original package name, is a no-op
since the package is already installed
"""
pkg_path = create_test_package_with_setup(
script, name='simple-package', version='1.0')
result = script.pip('install', '-e', '.',
expect_stderr=True, cwd=pkg_path)
assert 'Successfully installed simple-package' in result.stdout
result = script.pip('install', package_name)
assert 'Requirement already satisfied: {}'.format(
package_name) in result.stdout

View file

@ -168,7 +168,8 @@ def test_latest_prerelease_install_message(caplog, monkeypatch):
dist = pretend.stub(version="1.0.0")
get_dist = pretend.call_recorder(lambda x: dist)
monkeypatch.setattr("pip._vendor.pkg_resources.get_distribution", get_dist)
monkeypatch.setattr("pip._internal.commands.search.get_distribution",
get_dist)
with caplog.at_level(logging.INFO):
print_results(hits)

View file

@ -6,7 +6,6 @@ import sys
import freezegun
import pretend
import pytest
from pip._vendor import pkg_resources
from pip._internal import self_outdated_check
from pip._internal.models.candidate import InstallationCandidate
@ -98,7 +97,7 @@ def test_pip_self_version_check(monkeypatch, stored_time, installed_ver,
pretend.call_recorder(lambda *a, **kw: None))
monkeypatch.setattr(logger, 'debug',
pretend.call_recorder(lambda s, exc_info=None: None))
monkeypatch.setattr(pkg_resources, 'get_distribution',
monkeypatch.setattr(self_outdated_check, 'get_distribution',
lambda name: MockDistribution(installer))
fake_state = pretend.stub(