Skip to content

Commit

Permalink
Merge pull request #8659 from uranusjr/fix-get-distribution-dot-in-name
Browse files Browse the repository at this point in the history
Canonicalize name in check_if_exists
  • Loading branch information
pradyunsg committed Aug 4, 2020
1 parent ed205bd commit 864e2ee
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 19 deletions.
2 changes: 2 additions & 0 deletions news/8645.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Correctly find already-installed distributions with dot (``.``) in the name
and uninstall them when needed.
28 changes: 9 additions & 19 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,25 +429,13 @@ def check_if_exists(self, use_user_site):
"""
if self.req is None:
return
# get_distribution() will resolve the entire list of requirements
# anyway, and we've already determined that we need the requirement
# in question, so strip the marker so that we don't try to
# 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:
existing_dist = get_distribution(self.req.name)
if not existing_dist:
return
except pkg_resources.VersionConflict:
existing_dist = get_distribution(
self.req.name
)

existing_version = existing_dist.parsed_version
if not self.req.specifier.contains(existing_version, prereleases=True):
self.satisfied_by = None
if use_user_site:
if dist_in_usersite(existing_dist):
self.should_reinstall = True
Expand All @@ -461,11 +449,13 @@ def check_if_exists(self, use_user_site):
else:
self.should_reinstall = True
else:
if self.editable and self.satisfied_by:
if self.editable:
self.should_reinstall = True
# when installing editables, nothing pre-existing should ever
# satisfy
self.satisfied_by = None
else:
self.satisfied_by = existing_dist

# Things valid for wheels
@property
Expand Down
33 changes: 33 additions & 0 deletions tests/functional/test_install_upgrade.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import itertools
import os
import sys
import textwrap
Expand All @@ -7,6 +8,7 @@
from tests.lib import pyversion # noqa: F401
from tests.lib import assert_all_changes
from tests.lib.local_repos import local_checkout
from tests.lib.wheel import make_wheel


@pytest.mark.network
Expand Down Expand Up @@ -439,3 +441,34 @@ def prep_ve(self, script, version, pip_src, distribute=False):
cwd=pip_src,
expect_stderr=True,
)


@pytest.mark.parametrize("req1, req2", list(itertools.product(
["foo.bar", "foo_bar", "foo-bar"], ["foo.bar", "foo_bar", "foo-bar"],
)))
def test_install_find_existing_package_canonicalize(script, req1, req2):
"""Ensure an already-installed dist is found no matter how the dist name
was normalized on installation. (pypa/pip#8645)
"""
# Create and install a package that's not available in the later stage.
req_container = script.scratch_path.joinpath("foo-bar")
req_container.mkdir()
req_path = make_wheel("foo_bar", "1.0").save_to_dir(req_container)
script.pip("install", "--no-index", req_path)

# Depend on the previously installed, but now unavailable package.
pkg_container = script.scratch_path.joinpath("pkg")
pkg_container.mkdir()
make_wheel(
"pkg",
"1.0",
metadata_updates={"Requires-Dist": req2},
).save_to_dir(pkg_container)

# Ensure the previously installed package can be correctly used to match
# the dependency.
result = script.pip(
"install", "--no-index", "--find-links", pkg_container, "pkg",
)
satisfied_message = "Requirement already satisfied: {}".format(req2)
assert satisfied_message in result.stdout, str(result)

0 comments on commit 864e2ee

Please sign in to comment.