From 8026db2b63274af61d32007c3d7e8fc8b57bdd26 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Fri, 10 Mar 2023 14:45:19 +0100 Subject: [PATCH] Add tests for egg-info package with .files from inaccurate SOURCES.txt As established in previous commits, the SOURCES.txt file is not always an accurate source of files that are present after a package has been installed. One situation where this inaccuracy is problematic is when top_level.txt is also missing, and packages_distributions() is forced to infer the provided import names based on Distribution.files. In this situation we end up with incorrect mappings between import packages and distribution packages, including import packages that clearly do not exist at all. For example, a SOURCES.txt that lists setup.py (which is used _when_ installing, but is not available after installation), will see that setup.py returned from .files, which then will cause packages_distributions() to claim a mapping from the non-existent 'setup' import name to this distribution. This commit adds EggInfoPkgSourcesFallback which demostrates such a scenario, and adds this new class to a couple of relevant tests. A couple of these tests are currently failing, to demonstrate the issue at hand. These test failures will be fixed in the next commit. See the python/importlib_metadata#115 issue for more details. --- tests/fixtures.py | 26 ++++++++++++++++++++++++++ tests/test_api.py | 2 ++ tests/test_main.py | 7 +++++++ 3 files changed, 35 insertions(+) diff --git a/tests/fixtures.py b/tests/fixtures.py index aa6ffac9..bbd9854b 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -243,6 +243,32 @@ def setUp(self): build_files(EggInfoPkgPipInstalledNoModules.files, prefix=self.site_dir) +class EggInfoPkgSourcesFallback(OnSysPath, SiteDir): + files: FilesDef = { + "starved_egg_pkg.egg-info": { + "PKG-INFO": "Name: starved_egg-pkg", + # SOURCES.txt is made from the source archive, and contains files + # (setup.py) that are not present after installation. + "SOURCES.txt": """ + starved_egg_pkg.py + setup.py + starved_egg_pkg.egg-info/PKG-INFO + starved_egg_pkg.egg-info/SOURCES.txt + """, + # missing installed-files.txt (i.e. not installed by pip) + # missing top_level.txt + }, + "starved_egg_pkg.py": """ + def main(): + print("hello world") + """, + } + + def setUp(self): + super().setUp() + build_files(EggInfoPkgSourcesFallback.files, prefix=self.site_dir) + + class EggInfoFile(OnSysPath, SiteDir): files: FilesDef = { "egginfo_file.egg-info": """ diff --git a/tests/test_api.py b/tests/test_api.py index 3bf4a41f..1f0f79ab 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -28,6 +28,7 @@ def suppress_known_deprecation(): class APITests( fixtures.EggInfoPkg, fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, fixtures.DistInfoPkg, fixtures.DistInfoPkgWithDot, fixtures.EggInfoFile, @@ -185,6 +186,7 @@ def test_files_dist_info(self): def test_files_egg_info(self): self._test_files(files('egginfo-pkg')) self._test_files(files('empty_egg-pkg')) + self._test_files(files('starved_egg-pkg')) def test_version_egg_info_file(self): self.assertEqual(version('egginfo-file'), '0.1') diff --git a/tests/test_main.py b/tests/test_main.py index 4d28fa26..e08a7609 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -173,6 +173,7 @@ def test_metadata_loads_egg_info(self): class DiscoveryTests( fixtures.EggInfoPkg, fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, fixtures.DistInfoPkg, unittest.TestCase, ): @@ -181,6 +182,7 @@ def test_package_discovery(self): assert all(isinstance(dist, Distribution) for dist in dists) assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists) assert any(dist.metadata['Name'] == 'empty_egg-pkg' for dist in dists) + assert any(dist.metadata['Name'] == 'starved_egg-pkg' for dist in dists) assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) def test_invalid_usage(self): @@ -312,6 +314,7 @@ def test_packages_distributions_example2(self): class PackagesDistributionsTest( fixtures.EggInfoPkg, fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase, @@ -353,3 +356,7 @@ def import_names_from_package(package_name): # empty_egg-pkg should not be associated with any import names # (top_level.txt is empty, and installed-files.txt has no .py files) assert import_names_from_package('empty_egg-pkg') == set() + + # starved_egg-pkg has one import ('starved_egg_pkg') inferred + # from SOURCES.txt (top_level.txt is missing) + assert import_names_from_package('starved_egg-pkg') == {'starved_egg_pkg'}