Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-importlib-metadata for openSUSE:Factory checked in at 2023-04-16 16:06:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-importlib-metadata (Old) and /work/SRC/openSUSE:Factory/.python-importlib-metadata.new.19717 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-importlib-metadata" Sun Apr 16 16:06:41 2023 rev:18 rq:1079571 version:6.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-importlib-metadata/python-importlib-metadata.changes 2023-04-04 21:16:58.847874613 +0200 +++ /work/SRC/openSUSE:Factory/.python-importlib-metadata.new.19717/python-importlib-metadata.changes 2023-04-16 16:06:45.844709325 +0200 @@ -1,0 +2,12 @@ +Sat Apr 15 10:16:48 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 6.3.0: + * #115: Support ``installed-files.txt`` for + ``Distribution.files`` when present. + * #442: Fixed issue introduced in v6.1.0 where non-importable + * names (metadata dirs) began appearing in ``packages_distributions``. + * #384: ``PackageMetadata`` now stipulates an additional + ``get`` method allowing for easy querying of metadata keys that may + not be present + +------------------------------------------------------------------- Old: ---- importlib_metadata-6.1.0.tar.gz New: ---- importlib_metadata-6.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-importlib-metadata.spec ++++++ --- /var/tmp/diff_new_pack.dcZrDv/_old 2023-04-16 16:06:46.352712253 +0200 +++ /var/tmp/diff_new_pack.dcZrDv/_new 2023-04-16 16:06:46.356712276 +0200 @@ -27,7 +27,7 @@ %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 Name: python-importlib-metadata%{psuffix} -Version: 6.1.0 +Version: 6.3.0 Release: 0 Summary: Read metadata from Python packages License: Apache-2.0 ++++++ importlib_metadata-6.1.0.tar.gz -> importlib_metadata-6.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/.github/workflows/main.yml new/importlib_metadata-6.3.0/.github/workflows/main.yml --- old/importlib_metadata-6.1.0/.github/workflows/main.yml 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/.github/workflows/main.yml 2023-04-10 04:27:13.000000000 +0200 @@ -2,6 +2,9 @@ on: [push, pull_request] +permissions: + contents: read + env: # Environment variables to support color support (jaraco/skeleton#66): # Request colored output from CLI tools supporting it. Different tools @@ -130,6 +133,8 @@ TOXENV: diffcov release: + permissions: + contents: write needs: - check if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/CHANGES.rst new/importlib_metadata-6.3.0/CHANGES.rst --- old/importlib_metadata-6.1.0/CHANGES.rst 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/CHANGES.rst 2023-04-10 04:27:13.000000000 +0200 @@ -1,3 +1,23 @@ +v6.3.0 +====== + +* #115: Support ``installed-files.txt`` for ``Distribution.files`` + when present. + +v6.2.1 +====== + +* #442: Fixed issue introduced in v6.1.0 where non-importable + names (metadata dirs) began appearing in + ``packages_distributions``. + +v6.2.0 +====== + +* #384: ``PackageMetadata`` now stipulates an additional ``get`` + method allowing for easy querying of metadata keys that may not + be present. + v6.1.0 ====== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/PKG-INFO new/importlib_metadata-6.3.0/PKG-INFO --- old/importlib_metadata-6.1.0/PKG-INFO 2023-03-18 18:10:38.770994400 +0100 +++ new/importlib_metadata-6.3.0/PKG-INFO 2023-04-10 04:27:32.096812500 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: importlib_metadata -Version: 6.1.0 +Version: 6.3.0 Summary: Read metadata from Python packages Home-page: https://github.com/python/importlib_metadata Author: Jason R. Coombs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/importlib_metadata/__init__.py new/importlib_metadata-6.3.0/importlib_metadata/__init__.py --- old/importlib_metadata-6.1.0/importlib_metadata/__init__.py 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/importlib_metadata/__init__.py 2023-04-10 04:27:13.000000000 +0200 @@ -12,6 +12,7 @@ import functools import itertools import posixpath +import contextlib import collections import inspect @@ -461,8 +462,8 @@ :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. """ @@ -475,9 +476,19 @@ @pass_none def make_files(lines): - return list(starmap(make_file, csv.reader(lines))) + return starmap(make_file, csv.reader(lines)) - return make_files(self._read_files_distinfo() or self._read_files_egginfo()) + @pass_none + def skip_missing_files(package_paths): + return list(filter(lambda path: path.locate().exists(), package_paths)) + + return skip_missing_files( + make_files( + self._read_files_distinfo() + or self._read_files_egginfo_installed() + or self._read_files_egginfo_sources() + ) + ) def _read_files_distinfo(self): """ @@ -486,10 +497,43 @@ text = self.read_text('RECORD') return text and text.splitlines() - def _read_files_egginfo(self): + def _read_files_egginfo_installed(self): """ - SOURCES.txt might contain literal commas, so wrap each line - in quotes. + Read installed-files.txt and return lines in a similar + CSV-parsable format as RECORD: each file must be placed + relative to the site-packages directory, and must also be + quoted (since file names can contain literal commas). + + 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) + if not text or not subdir: + return + with contextlib.suppress(Exception): + ret = [ + str((subdir / line).resolve().relative_to(self.locate_file(''))) + for line in text.splitlines() + ] + return map('"{}"'.format, ret) + + def _read_files_egginfo_sources(self): + """ + Read SOURCES.txt and return lines in a similar CSV-parsable + format as RECORD: each file name must be quoted (since it + might contain literal commas). + + 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()) @@ -902,4 +946,9 @@ f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f) for f in always_iterable(dist.files) } - return filter(None, opt_names) + + @pass_none + def importable_name(name): + return '.' not in name + + return filter(importable_name, opt_names) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/importlib_metadata/_meta.py new/importlib_metadata-6.3.0/importlib_metadata/_meta.py --- old/importlib_metadata-6.1.0/importlib_metadata/_meta.py 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/importlib_metadata/_meta.py 2023-04-10 04:27:13.000000000 +0200 @@ -18,6 +18,14 @@ def __iter__(self) -> Iterator[str]: ... # pragma: no cover + @overload + def get(self, name: str, failobj: None = None) -> Optional[str]: + ... # pragma: no cover + + @overload + def get(self, name: str, failobj: _T) -> Union[str, _T]: + ... # pragma: no cover + # overload per python/importlib_metadata#435 @overload def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/importlib_metadata.egg-info/PKG-INFO new/importlib_metadata-6.3.0/importlib_metadata.egg-info/PKG-INFO --- old/importlib_metadata-6.1.0/importlib_metadata.egg-info/PKG-INFO 2023-03-18 18:10:38.000000000 +0100 +++ new/importlib_metadata-6.3.0/importlib_metadata.egg-info/PKG-INFO 2023-04-10 04:27:32.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: importlib-metadata -Version: 6.1.0 +Version: 6.3.0 Summary: Read metadata from Python packages Home-page: https://github.com/python/importlib_metadata Author: Jason R. Coombs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/importlib_metadata.egg-info/SOURCES.txt new/importlib_metadata-6.3.0/importlib_metadata.egg-info/SOURCES.txt --- old/importlib_metadata-6.1.0/importlib_metadata.egg-info/SOURCES.txt 2023-03-18 18:10:38.000000000 +0100 +++ new/importlib_metadata-6.3.0/importlib_metadata.egg-info/SOURCES.txt 2023-04-10 04:27:32.000000000 +0200 @@ -44,6 +44,7 @@ prepare/example2/pyproject.toml prepare/example2/example2/__init__.py tests/__init__.py +tests/_path.py tests/fixtures.py tests/py39compat.py tests/test_api.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/tests/_path.py new/importlib_metadata-6.3.0/tests/_path.py --- old/importlib_metadata-6.1.0/tests/_path.py 1970-01-01 01:00:00.000000000 +0100 +++ new/importlib_metadata-6.3.0/tests/_path.py 2023-04-10 04:27:13.000000000 +0200 @@ -0,0 +1,104 @@ +# from jaraco.path 3.5 + +import functools +import pathlib +from typing import Dict, Union + +try: + from typing import Protocol, runtime_checkable +except ImportError: # pragma: no cover + # Python 3.7 + from typing_extensions import Protocol, runtime_checkable # type: ignore + + +FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore + + +@runtime_checkable +class TreeMaker(Protocol): + def __truediv__(self, *args, **kwargs): + ... # pragma: no cover + + def mkdir(self, **kwargs): + ... # pragma: no cover + + def write_text(self, content, **kwargs): + ... # pragma: no cover + + def write_bytes(self, content): + ... # pragma: no cover + + +def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker: + return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore + + +def build( + spec: FilesSpec, + prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore +): + """ + Build a set of files/directories, as described by the spec. + + Each key represents a pathname, and the value represents + the content. Content may be a nested directory. + + >>> spec = { + ... 'README.txt': "A README file", + ... "foo": { + ... "__init__.py": "", + ... "bar": { + ... "__init__.py": "", + ... }, + ... "baz.py": "# Some code", + ... } + ... } + >>> target = getfixture('tmp_path') + >>> build(spec, target) + >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8') + '# Some code' + """ + for name, contents in spec.items(): + create(contents, _ensure_tree_maker(prefix) / name) + + +@functools.singledispatch +def create(content: Union[str, bytes, FilesSpec], path): + path.mkdir(exist_ok=True) + build(content, prefix=path) # type: ignore + + +@create.register +def _(content: bytes, path): + path.write_bytes(content) + + +@create.register +def _(content: str, path): + path.write_text(content, encoding='utf-8') + + +class Recording: + """ + A TreeMaker object that records everything that would be written. + + >>> r = Recording() + >>> build({'foo': {'foo1.txt': 'yes'}, 'bar.txt': 'abc'}, r) + >>> r.record + ['foo/foo1.txt', 'bar.txt'] + """ + + def __init__(self, loc=pathlib.PurePosixPath(), record=None): + self.loc = loc + self.record = record if record is not None else [] + + def __truediv__(self, other): + return Recording(self.loc / other, self.record) + + def write_text(self, content, **kwargs): + self.record.append(str(self.loc)) + + write_bytes = write_text + + def mkdir(self, **kwargs): + return diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/tests/fixtures.py new/importlib_metadata-6.3.0/tests/fixtures.py --- old/importlib_metadata-6.1.0/tests/fixtures.py 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/tests/fixtures.py 2023-04-10 04:27:13.000000000 +0200 @@ -11,6 +11,9 @@ from .py39compat import FS_NONASCII from typing import Dict, Union +from . import _path + + try: from importlib import resources # type: ignore @@ -83,8 +86,10 @@ # Except for python/mypy#731, prefer to define -# FilesDef = Dict[str, Union['FilesDef', str]] -FilesDef = Dict[str, Union[Dict[str, Union[Dict[str, str], str]], str]] +# FilesDef = Dict[str, Union['FilesDef', str, bytes]] +FilesDef = Dict[ + str, Union[Dict[str, Union[Dict[str, Union[str, bytes]], str, bytes]], str, bytes] +] class DistInfoPkg(OnSysPath, SiteDir): @@ -211,6 +216,97 @@ build_files(EggInfoPkg.files, prefix=self.site_dir) +class EggInfoPkgPipInstalledNoToplevel(OnSysPath, SiteDir): + files: FilesDef = { + "egg_with_module_pkg.egg-info": { + "PKG-INFO": "Name: egg_with_module-pkg", + # SOURCES.txt is made from the source archive, and contains files + # (setup.py) that are not present after installation. + "SOURCES.txt": """ + egg_with_module.py + setup.py + egg_with_module_pkg.egg-info/PKG-INFO + egg_with_module_pkg.egg-info/SOURCES.txt + egg_with_module_pkg.egg-info/top_level.txt + """, + # installed-files.txt is written by pip, and is a strictly more + # accurate source than SOURCES.txt as to the installed contents of + # the package. + "installed-files.txt": """ + ../egg_with_module.py + PKG-INFO + SOURCES.txt + top_level.txt + """, + # missing top_level.txt (to trigger fallback to installed-files.txt) + }, + "egg_with_module.py": """ + def main(): + print("hello world") + """, + } + + def setUp(self): + super().setUp() + build_files(EggInfoPkgPipInstalledNoToplevel.files, prefix=self.site_dir) + + +class EggInfoPkgPipInstalledNoModules(OnSysPath, SiteDir): + files: FilesDef = { + "egg_with_no_modules_pkg.egg-info": { + "PKG-INFO": "Name: egg_with_no_modules-pkg", + # SOURCES.txt is made from the source archive, and contains files + # (setup.py) that are not present after installation. + "SOURCES.txt": """ + setup.py + egg_with_no_modules_pkg.egg-info/PKG-INFO + egg_with_no_modules_pkg.egg-info/SOURCES.txt + egg_with_no_modules_pkg.egg-info/top_level.txt + """, + # installed-files.txt is written by pip, and is a strictly more + # accurate source than SOURCES.txt as to the installed contents of + # the package. + "installed-files.txt": """ + PKG-INFO + SOURCES.txt + top_level.txt + """, + # top_level.txt correctly reflects that no modules are installed + "top_level.txt": b"\n", + }, + } + + def setUp(self): + super().setUp() + build_files(EggInfoPkgPipInstalledNoModules.files, prefix=self.site_dir) + + +class EggInfoPkgSourcesFallback(OnSysPath, SiteDir): + files: FilesDef = { + "sources_fallback_pkg.egg-info": { + "PKG-INFO": "Name: sources_fallback-pkg", + # SOURCES.txt is made from the source archive, and contains files + # (setup.py) that are not present after installation. + "SOURCES.txt": """ + sources_fallback.py + setup.py + sources_fallback_pkg.egg-info/PKG-INFO + sources_fallback_pkg.egg-info/SOURCES.txt + """, + # missing installed-files.txt (i.e. not installed by pip) and + # missing top_level.txt (to trigger fallback to SOURCES.txt) + }, + "sources_fallback.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": """ @@ -266,6 +362,16 @@ f.write(DALS(contents)) +def build_record(file_defs): + return ''.join(f'{name},,\n' for name in record_names(file_defs)) + + +def record_names(file_defs): + recording = _path.Recording() + _path.build(file_defs, recording) + return recording.record + + class FileBuilder: def unicode_filename(self): return FS_NONASCII or self.skip("File system does not support non-ascii.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/tests/test_api.py new/importlib_metadata-6.3.0/tests/test_api.py --- old/importlib_metadata-6.1.0/tests/test_api.py 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/tests/test_api.py 2023-04-10 04:27:13.000000000 +0200 @@ -27,6 +27,9 @@ class APITests( fixtures.EggInfoPkg, + fixtures.EggInfoPkgPipInstalledNoToplevel, + fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, fixtures.DistInfoPkg, fixtures.DistInfoPkgWithDot, fixtures.EggInfoFile, @@ -62,15 +65,28 @@ distribution(prefix) def test_for_top_level(self): - self.assertEqual( - distribution('egginfo-pkg').read_text('top_level.txt').strip(), 'mod' - ) + tests = [ + ('egginfo-pkg', 'mod'), + ('egg_with_no_modules-pkg', ''), + ] + for pkg_name, expect_content in tests: + with self.subTest(pkg_name): + self.assertEqual( + distribution(pkg_name).read_text('top_level.txt').strip(), + expect_content, + ) def test_read_text(self): - top_level = [ - path for path in files('egginfo-pkg') if path.name == 'top_level.txt' - ][0] - self.assertEqual(top_level.read_text(), 'mod\n') + tests = [ + ('egginfo-pkg', 'mod\n'), + ('egg_with_no_modules-pkg', '\n'), + ] + for pkg_name, expect_content in tests: + with self.subTest(pkg_name): + top_level = [ + path for path in files(pkg_name) if path.name == 'top_level.txt' + ][0] + self.assertEqual(top_level.read_text(), expect_content) def test_entry_points(self): eps = entry_points() @@ -148,6 +164,20 @@ with suppress_known_deprecation(): assert md['does-not-exist'] is None + def test_get_key(self): + """ + Getting a key gets the key. + """ + md = metadata('egginfo-pkg') + assert md.get('Name') == 'egginfo-pkg' + + def test_get_missing_key(self): + """ + Requesting a missing key will return None. + """ + md = metadata('distinfo-pkg') + assert md.get('does-not-exist') is None + @staticmethod def _test_files(files): root = files[0].root @@ -170,6 +200,9 @@ def test_files_egg_info(self): self._test_files(files('egginfo-pkg')) + self._test_files(files('egg_with_module-pkg')) + self._test_files(files('egg_with_no_modules-pkg')) + self._test_files(files('sources_fallback-pkg')) def test_version_egg_info_file(self): self.assertEqual(version('egginfo-file'), '0.1') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-6.1.0/tests/test_main.py new/importlib_metadata-6.3.0/tests/test_main.py --- old/importlib_metadata-6.1.0/tests/test_main.py 2023-03-18 18:10:20.000000000 +0100 +++ new/importlib_metadata-6.3.0/tests/test_main.py 2023-04-10 04:27:13.000000000 +0200 @@ -3,6 +3,7 @@ import unittest import importlib import importlib_metadata +import itertools import pyfakefs.fake_filesystem_unittest as ffs from . import fixtures @@ -170,11 +171,21 @@ assert meta['Description'] == 'pôrËtend' -class DiscoveryTests(fixtures.EggInfoPkg, fixtures.DistInfoPkg, unittest.TestCase): +class DiscoveryTests( + fixtures.EggInfoPkg, + fixtures.EggInfoPkgPipInstalledNoToplevel, + fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, + fixtures.DistInfoPkg, + unittest.TestCase, +): def test_package_discovery(self): dists = list(distributions()) 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'] == 'egg_with_module-pkg' for dist in dists) + assert any(dist.metadata['Name'] == 'egg_with_no_modules-pkg' for dist in dists) + assert any(dist.metadata['Name'] == 'sources_fallback-pkg' for dist in dists) assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) def test_invalid_usage(self): @@ -328,28 +339,73 @@ Test top-level modules detected on a package without 'top-level.txt'. """ suffixes = importlib.machinery.all_suffixes() - fixtures.build_files( - { - 'all_distributions-1.0.0.dist-info': { - 'METADATA': """ - Name: all_distributions - Version: 1.0.0 - """, - 'RECORD': ''.join( - f'{i}-top-level{suffix},,\n' - f'{i}-in-namespace/mod{suffix},,\n' - f'{i}-in-package/__init__.py,,\n' - f'{i}-in-package/mod{suffix},,\n' - for i, suffix in enumerate(suffixes) - ), - }, - }, - prefix=self.site_dir, + metadata = dict( + METADATA=""" + Name: all_distributions + Version: 1.0.0 + """, ) + files = { + 'all_distributions-1.0.0.dist-info': metadata, + } + for i, suffix in enumerate(suffixes): + files.update( + { + f'importable-name {i}{suffix}': '', + f'in_namespace_{i}': { + f'mod{suffix}': '', + }, + f'in_package_{i}': { + '__init__.py': '', + f'mod{suffix}': '', + }, + } + ) + metadata.update(RECORD=fixtures.build_record(files)) + fixtures.build_files(files, prefix=self.site_dir) distributions = packages_distributions() for i in range(len(suffixes)): - assert distributions[f'{i}-top-level'] == ['all_distributions'] - assert distributions[f'{i}-in-namespace'] == ['all_distributions'] - assert distributions[f'{i}-in-package'] == ['all_distributions'] + assert distributions[f'importable-name {i}'] == ['all_distributions'] + assert distributions[f'in_namespace_{i}'] == ['all_distributions'] + assert distributions[f'in_package_{i}'] == ['all_distributions'] + + assert not any(name.endswith('.dist-info') for name in distributions) + + +class PackagesDistributionsEggTest( + fixtures.EggInfoPkg, + fixtures.EggInfoPkgPipInstalledNoToplevel, + fixtures.EggInfoPkgPipInstalledNoModules, + fixtures.EggInfoPkgSourcesFallback, + unittest.TestCase, +): + def test_packages_distributions_on_eggs(self): + """ + Test old-style egg packages with a variation of 'top_level.txt', + 'SOURCES.txt', and 'installed-files.txt', available. + """ + distributions = packages_distributions() + + def import_names_from_package(package_name): + return { + import_name + for import_name, package_names in distributions.items() + if package_name in package_names + } + + # egginfo-pkg declares one import ('mod') via top_level.txt + assert import_names_from_package('egginfo-pkg') == {'mod'} + + # egg_with_module-pkg has one import ('egg_with_module') inferred from + # installed-files.txt (top_level.txt is missing) + assert import_names_from_package('egg_with_module-pkg') == {'egg_with_module'} + + # egg_with_no_modules-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('egg_with_no_modules-pkg') == set() + + # sources_fallback-pkg has one import ('sources_fallback') inferred from + # SOURCES.txt (top_level.txt and installed-files.txt is missing) + assert import_names_from_package('sources_fallback-pkg') == {'sources_fallback'}