Skip to content

Commit

Permalink
Fix editable-or-not egg-info uninstallation
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Nov 18, 2021
1 parent e0f41ae commit 35ec437
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
8 changes: 8 additions & 0 deletions src/pip/_internal/metadata/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ def canonical_name(self) -> NormalizedName:
def version(self) -> DistributionVersion:
raise NotImplementedError()

@property
def setuptools_filename(self) -> str:
"""Convert a project name to its setuptools-compatible filename.
This is a copy of ``pkg_resources.to_filename()`` for compatibility.
"""
return self.raw_name.replace("-", "_")

@property
def direct_url(self) -> Optional[DirectUrl]:
"""Obtain a DirectUrl from this distribution.
Expand Down
25 changes: 17 additions & 8 deletions src/pip/_internal/req/req_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,21 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
paths_to_remove = cls(dist)
develop_egg_link = egg_link_path_from_location(dist.raw_name)

# Distribution is installed with metadata in a "flat" .egg-info
# directory. This means it is not a modern .dist-info installation, an
# egg, or legacy editable.
setuptools_flat_installation = (
dist.installed_with_setuptools_egg_info
and info_location is not None
and os.path.exists(info_location)
# If dist is editable and the location points to a ``.egg-info``,
# we are in fact in the legacy editable case.
and not info_location.endswith(f"{dist.setuptools_filename}.egg-info")
)

# Uninstall cases order do matter as in the case of 2 installs of the
# same package, pip needs to uninstall the currently detected version
if dist.installed_with_setuptools_egg_info and not dist.editable:
# if dist is editable and the location points to a ``.egg-info``,
# we are in fact in the ``.egg_link`` case.
if setuptools_flat_installation:
if info_location is not None:
paths_to_remove.add(info_location)
installed_files = dist.iter_declared_entries()
Expand Down Expand Up @@ -512,15 +522,14 @@ def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
for path in uninstallation_paths(dist):
paths_to_remove.add(path)

elif dist.editable and develop_egg_link:
elif develop_egg_link:
# PEP 660 modern editable is handled in the ``.dist-info`` case
# above, so this only covers the setuptools-style editable.
with open(develop_egg_link) as fh:
link_pointer = os.path.normcase(fh.readline().strip())
assert (
link_pointer == dist_location
), "Egg-link {} does not match installed location of {} (at {})".format(
link_pointer, dist.raw_name, dist_location
assert link_pointer == dist_location, (
f"Egg-link {link_pointer} does not match installed location of "
f"{dist.raw_name} (at {dist_location})"
)
paths_to_remove.add(develop_egg_link)
easy_install_pth = os.path.join(
Expand Down

0 comments on commit 35ec437

Please sign in to comment.