Skip to content

Commit

Permalink
Correctly normalize paths relative to install path
Browse files Browse the repository at this point in the history
Since the legacy installed-files.txt writes paths relatively to the
egg-info directory, we need to introduce a new property on
BaseDistribution to return that directory's path (analoguous to
pkg_resources's Distribution.egg_info).

Entries in RECORD are normalized with pathlib.Path so they have the
correct path component separator depending on the platform (e.g. '/' on
Windows), to match the previous behavior.
  • Loading branch information
uranusjr committed Jul 26, 2021
1 parent 776a1da commit 21422d4
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
19 changes: 12 additions & 7 deletions src/pip/_internal/commands/show.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import csv
import logging
import os
import pathlib
from optparse import Values
from typing import Iterator, List, NamedTuple, Optional

Expand Down Expand Up @@ -95,19 +95,24 @@ def _get_requiring_packages(current_dist: BaseDistribution) -> List[str]:
}
]

def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[str]]:
def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[pathlib.Path]]:
try:
text = dist.read_text('RECORD')
except FileNotFoundError:
return None
return (row[0] for row in csv.reader(text.splitlines()))
return (pathlib.Path(row[0]) for row in csv.reader(text.splitlines()))

def _files_from_installed_files(dist: BaseDistribution) -> Optional[Iterator[str]]:
def _files_from_legacy(dist: BaseDistribution) -> Optional[Iterator[pathlib.Path]]:
try:
text = dist.read_text('installed-files.txt')
except FileNotFoundError:
return None
return (p for p in text.splitlines(keepends=False) if p)
paths = (p for p in text.splitlines(keepends=False) if p)
root = dist.location
if root is not None:
info = pathlib.Path(dist.metadata_directory)
paths = (info.joinpath(p).resolve().relative_to(root) for p in paths)
return paths

for query_name in query_names:
try:
Expand All @@ -121,11 +126,11 @@ def _files_from_installed_files(dist: BaseDistribution) -> Optional[Iterator[str
except FileNotFoundError:
entry_points = []

files_iter = _files_from_record(dist) or _files_from_installed_files(dist)
files_iter = _files_from_record(dist) or _files_from_legacy(dist)
if files_iter is None:
files: Optional[List[str]] = None
else:
files = sorted(os.path.relpath(p, dist.location) for p in files_iter)
files = sorted(str(p) for p in files_iter)

metadata = dist.metadata

Expand Down
12 changes: 12 additions & 0 deletions src/pip/_internal/metadata/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ def location(self) -> Optional[str]:
"""
raise NotImplementedError()

@property
def metadata_directory(self) -> Optional[str]:
"""Location of the metadata directory.
Similarly to ``location``, a string value is not necessarily a
filesystem path. ``None`` means the distribution is created in-memory.
For a modern .dist-info installation on disk, this should be something
like ``{location}/{raw_name}-{version}.dist-info``.
"""
raise NotImplementedError()

@property
def canonical_name(self) -> "NormalizedName":
raise NotImplementedError()
Expand Down
4 changes: 4 additions & 0 deletions src/pip/_internal/metadata/pkg_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ def from_wheel(cls, path: str, name: str) -> "Distribution":
def location(self) -> Optional[str]:
return self._dist.location

@property
def metadata_directory(self) -> Optional[str]:
return self._dist.egg_info

@property
def canonical_name(self) -> "NormalizedName":
return canonicalize_name(self._dist.project_name)
Expand Down

0 comments on commit 21422d4

Please sign in to comment.