Skip to content

Commit

Permalink
Distribution.files: Prefer *.egg-info/installed-files.txt to SOURCES.txt
Browse files Browse the repository at this point in the history
When listing the files in a *.egg-info distribution, prefer using
*.egg-info/installed-files.txt instead of *.egg-info/SOURCES.txt.

installed-files.txt is written by pip[1] when installing a package,
whereas the SOURCES.txt is written by setuptools when creating a
source archive[2].

installed-files.txt is only present when the package has been installed
by pip, so we cannot depend on it always being available. However, when
it _is_ available, it is an accurate record of what files are installed.
SOURCES.txt, on the other hand, is always avaiable, but is not always
accurate: Since it is generated from the source archive, it will often
include files (like 'setup.py') that are no longer available after the
package has been installed.

Fixes python#115 for the cases where a installed-files.txt file is available.

[1]: https://pip.pypa.io/en/stable/news/#v0-3
[2]: https://setuptools.pypa.io/en/latest/deprecated/python_eggs.html#sources-txt-source-files-manifest
  • Loading branch information
jherland committed Mar 11, 2023
1 parent 578322a commit 61b0f29
Showing 1 changed file with 33 additions and 4 deletions.
37 changes: 33 additions & 4 deletions importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,8 @@ def files(self):
:return: List of PackagePath for this distribution or None
Result is `None` if the metadata file that enumerates files
(i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
missing.
(i.e. RECORD for dist-info, or installed-files.txt or
SOURCES.txt for egg-info) is missing.
Result may be empty if the metadata exists but is empty.
"""

Expand All @@ -476,7 +476,11 @@ def make_file(name, hash=None, size_str=None):
def make_files(lines):
return list(starmap(make_file, csv.reader(lines)))

return make_files(self._read_files_distinfo() or self._read_files_egginfo())
return make_files(
self._read_files_distinfo()
or self._read_files_egginfo_installed()
or self._read_files_egginfo_sources()
)

def _read_files_distinfo(self):
"""
Expand All @@ -485,10 +489,35 @@ def _read_files_distinfo(self):
text = self.read_text('RECORD')
return text and text.splitlines()

def _read_files_egginfo(self):
def _read_files_egginfo_installed(self):
"""
installed-files.txt might contain literal commas, so wrap
each line in quotes. Also, the entries in installed-files.txt
are relative to the .egg-info/ subdir (not relative to the
parent site-packages directory that make_file() expects).
This file is written when the package is installed by pip,
but it might not be written for other installation methods.
Hence, even if we can assume that this file is accurate
when it exists, we cannot assume that it always exists.
"""
text = self.read_text('installed-files.txt')
# We need to prepend the .egg-info/ subdir to the lines in this file.
# But this subdir is only available in the PathDistribution's self._path
# which is not easily accessible from this base class...
subdir = getattr(self, '_path', None)
return text and subdir and [f'"{subdir}/{line}"' for line in text.splitlines()]

def _read_files_egginfo_sources(self):
"""
SOURCES.txt might contain literal commas, so wrap each line
in quotes.
Note that SOURCES.txt is not a reliable source for what
files are installed by a package. This file is generated
for a source archive, and the files that are present
there (e.g. setup.py) may not correctly reflect the files
that are present after the package has been installed.
"""
text = self.read_text('SOURCES.txt')
return text and map('"{}"'.format, text.splitlines())
Expand Down

0 comments on commit 61b0f29

Please sign in to comment.