From 08d7deffcbe0aa46acb1cc828a09cb22c22a0f11 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 13 Aug 2021 18:09:52 -0400 Subject: [PATCH 01/10] Fix test that assumed entries was a dict. Now expect entries is EntryPoints. Tests didn't fail because it was a negative test and a namedtuple always compares unequal to a string. --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 6bf25b3c..5ab9feb9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -115,7 +115,7 @@ def test_entry_points_unique_packages(self): for ep in entries ) # ns:sub doesn't exist in alt_pkg - assert 'ns:sub' not in entries + assert 'ns:sub' not in entries.names def test_entry_points_missing_name(self): with self.assertRaises(KeyError): From 6ffdcb9f247aeafdfc49860d93a38ea50e0dfa4d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 15:11:42 -0400 Subject: [PATCH 02/10] Extract ZipFixtures --- tests/fixtures.py | 25 +++++++++++++++++++++++++ tests/test_zip.py | 35 ++++------------------------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 8ef54f84..a281145b 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -10,6 +10,14 @@ from .py39compat import FS_NONASCII from typing import Dict, Union +try: + from importlib import resources + + getattr(resources, 'files') + getattr(resources, 'as_file') +except (ImportError, AttributeError): + import importlib_resources as resources # type: ignore + @contextlib.contextmanager def tempdir(): @@ -285,3 +293,20 @@ def DALS(str): class NullFinder: def find_module(self, name): pass + + +class ZipFixtures: + root = 'tests.data' + + def _fixture_on_path(self, filename): + pkg_file = resources.files(self.root).joinpath(filename) + file = self.resources.enter_context(resources.as_file(pkg_file)) + assert file.name.startswith('example-'), file.name + sys.path.insert(0, str(file)) + self.resources.callback(sys.path.pop, 0) + + def setUp(self): + # Add self.zip_name to the front of sys.path. + self.resources = contextlib.ExitStack() + self.addCleanup(self.resources.close) + self._fixture_on_path(self.zip_name) diff --git a/tests/test_zip.py b/tests/test_zip.py index 8a4c9458..e78bb330 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -1,7 +1,7 @@ import sys import unittest -from contextlib import ExitStack +from . import fixtures from importlib_metadata import ( PackageNotFoundError, distribution, @@ -11,31 +11,9 @@ version, ) -try: - from importlib import resources - getattr(resources, 'files') - getattr(resources, 'as_file') -except (ImportError, AttributeError): - import importlib_resources as resources # type: ignore - - -class TestZip(unittest.TestCase): - root = 'tests.data' - - def _fixture_on_path(self, filename): - pkg_file = resources.files(self.root).joinpath(filename) - file = self.resources.enter_context(resources.as_file(pkg_file)) - assert file.name.startswith('example-'), file.name - sys.path.insert(0, str(file)) - self.resources.callback(sys.path.pop, 0) - - def setUp(self): - # Find the path to the example-*.whl so we can add it to the front of - # sys.path, where we'll then try to find the metadata thereof. - self.resources = ExitStack() - self.addCleanup(self.resources.close) - self._fixture_on_path('example-21.12-py3-none-any.whl') +class TestZip(fixtures.ZipFixtures, unittest.TestCase): + zip_name = 'example-21.12-py3-none-any.whl' def test_zip_version(self): self.assertEqual(version('example'), '21.12') @@ -68,12 +46,7 @@ def test_one_distribution(self): class TestEgg(TestZip): - def setUp(self): - # Find the path to the example-*.egg so we can add it to the front of - # sys.path, where we'll then try to find the metadata thereof. - self.resources = ExitStack() - self.addCleanup(self.resources.close) - self._fixture_on_path('example-21.12-py3.6.egg') + zip_name = 'example-21.12-py3.6.egg' def test_files(self): for file in files('example'): From 0c24c31d0c50d761ebe8254f61c16d03e3bf308d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 15:17:54 -0400 Subject: [PATCH 03/10] Allow _fixture_on_path to be called imperatively. --- tests/fixtures.py | 1 - tests/test_zip.py | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index a281145b..03c44006 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -309,4 +309,3 @@ def setUp(self): # Add self.zip_name to the front of sys.path. self.resources = contextlib.ExitStack() self.addCleanup(self.resources.close) - self._fixture_on_path(self.zip_name) diff --git a/tests/test_zip.py b/tests/test_zip.py index e78bb330..01aba6df 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -13,7 +13,9 @@ class TestZip(fixtures.ZipFixtures, unittest.TestCase): - zip_name = 'example-21.12-py3-none-any.whl' + def setUp(self): + super().setUp() + self._fixture_on_path('example-21.12-py3-none-any.whl') def test_zip_version(self): self.assertEqual(version('example'), '21.12') @@ -46,7 +48,9 @@ def test_one_distribution(self): class TestEgg(TestZip): - zip_name = 'example-21.12-py3.6.egg' + def setUp(self): + super().setUp() + self._fixture_on_path('example-21.12-py3.6.egg') def test_files(self): for file in files('example'): From b26db5e2fa87a1bd85eb8bcd5edd9f0af8a29874 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 14:48:39 -0400 Subject: [PATCH 04/10] Add an 'example2' project built by trampolim. --- prepare/example2/example2/__init__.py | 2 ++ prepare/example2/pyproject.toml | 10 ++++++++++ tests/data/example2-1.0.0-py3-none-any.whl | Bin 0 -> 1167 bytes 3 files changed, 12 insertions(+) create mode 100644 prepare/example2/example2/__init__.py create mode 100644 prepare/example2/pyproject.toml create mode 100644 tests/data/example2-1.0.0-py3-none-any.whl diff --git a/prepare/example2/example2/__init__.py b/prepare/example2/example2/__init__.py new file mode 100644 index 00000000..de645c2e --- /dev/null +++ b/prepare/example2/example2/__init__.py @@ -0,0 +1,2 @@ +def main(): + return "example" diff --git a/prepare/example2/pyproject.toml b/prepare/example2/pyproject.toml new file mode 100644 index 00000000..011f4751 --- /dev/null +++ b/prepare/example2/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +build-backend = 'trampolim' +requires = ['trampolim'] + +[project] +name = 'example2' +version = '1.0.0' + +[project.scripts] +example = 'example2:main' diff --git a/tests/data/example2-1.0.0-py3-none-any.whl b/tests/data/example2-1.0.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..5ca93657f819601b83a57762db71d03dad12f78a GIT binary patch literal 1167 zcmWIWW@Zs#U|`^2h@Q$9ETws{LJ`OVVUTEQMPhD2PO6c9e0*kJW=VX!UO}a|r*H5H z-!o?}U)b^}h@olGl}VQt*|2`S(skrc)0~h}J%#|dk(*2Tg5y7Yys8g0$^>dG%t&2B zJp&+2$t*6>&CE;7*Y|Y|addGEas2G7>*?dE<9UVGTUYDcne&^23@#Zz`s97acWaQx zT8}fjCp=9bHJz4xCHX?KfDz5MzpI@lg#c{|1!6fM#%Wu)hpVfP=qX381_c3z1H0GX z;9nzg_m4wKcZZds58V*ju&HNFk+AXS9XpUc4~)G%DhBEQfRCIbeChj-ICy*M3ZtsFSrmBkh=j1cEb;Q7d&Ar$@L#zb9$Vue#oPCb0aL5c71K zRb5sBzwheY+{SQ7@b}`L8Pcy__>+KgTMyzlO=zO+(sU7k_Wpmh%gXI+`S`plJ6=}#w3+U_l2vF`iB zqo-v9ADM30!*%9D@)Xl3zF2Lkwa0c{FP|5G)j?_Q$FqTtW^_tTQu-Ku&G~Ua*rn%X zk01E{s@r+&z`y6NP4SD4uqS+B4)A7V5@E)j^nu|A21^=26f7MfX9Q#uu%&c}eg=jH z#$Y59aHe~7K4X*_)LYQ Vz5s7lHjoBZAZ!3C|H%a60RRCxggXEL literal 0 HcmV?d00001 From b379ff6754b11d8d046f3e91592b347e0b7b1a6c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 15:19:07 -0400 Subject: [PATCH 05/10] Add unit test for packages_distributions. --- tests/test_main.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_main.py b/tests/test_main.py index f7c9c518..af9615eb 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -17,6 +17,7 @@ distributions, entry_points, metadata, + packages_distributions, version, ) @@ -282,3 +283,9 @@ def test_unicode_dir_on_sys_path(self): prefix=self.site_dir, ) list(distributions()) + + +class PackagesDistributionsTest(fixtures.ZipFixtures, unittest.TestCase): + def test_packages_distributions_example(self): + self._fixture_on_path('example-21.12-py3-none-any.whl') + assert packages_distributions()['example'] == ['example'] From 43afaebc2d2eca353045e34d029bdc0ebc1f9099 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 15:23:49 -0400 Subject: [PATCH 06/10] Add failing test illustrating missed expectation. Ref #330. --- tests/fixtures.py | 2 +- tests/test_main.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 03c44006..a5be07c8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -301,7 +301,7 @@ class ZipFixtures: def _fixture_on_path(self, filename): pkg_file = resources.files(self.root).joinpath(filename) file = self.resources.enter_context(resources.as_file(pkg_file)) - assert file.name.startswith('example-'), file.name + assert file.name.startswith('example'), file.name sys.path.insert(0, str(file)) self.resources.callback(sys.path.pop, 0) diff --git a/tests/test_main.py b/tests/test_main.py index af9615eb..8426c48f 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -289,3 +289,11 @@ class PackagesDistributionsTest(fixtures.ZipFixtures, unittest.TestCase): def test_packages_distributions_example(self): self._fixture_on_path('example-21.12-py3-none-any.whl') assert packages_distributions()['example'] == ['example'] + + def test_packages_distributions_example2(self): + """ + Test packages_distributions on a wheel built + by trampolim. + """ + self._fixture_on_path('example2-1.0.0-py3-none-any.whl') + assert packages_distributions()['example2'] == ['example2'] From 1a8e808d5347707e195357df27677c85978bd1dd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 14 Aug 2021 15:28:22 -0400 Subject: [PATCH 07/10] Add fallback to infer top-level names for a distribution. Fixes #330. --- importlib_metadata/__init__.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9f5bf347..48a91c34 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -1013,6 +1013,18 @@ def packages_distributions() -> Mapping[str, List[str]]: """ pkg_to_dist = collections.defaultdict(list) for dist in distributions(): - for pkg in (dist.read_text('top_level.txt') or '').split(): + for pkg in _top_level_declared(dist) or _top_level_inferred(dist): pkg_to_dist[pkg].append(dist.metadata['Name']) return dict(pkg_to_dist) + + +def _top_level_declared(dist): + return (dist.read_text('top_level.txt') or '').split() + + +def _top_level_inferred(dist): + return { + f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name + for f in dist.files + if f.suffix == ".py" + } From 1fcdb3cc642a17dca241acd3e36675f5e895389f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 18 Aug 2021 12:52:45 +0300 Subject: [PATCH 08/10] Upgrade Python syntax with pyupgrade --py36-plus --- docs/conf.py | 1 - importlib_metadata/__init__.py | 4 ++-- importlib_metadata/_text.py | 4 ++-- tests/fixtures.py | 16 ++++++++-------- tests/test_main.py | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f619facc..bcd9f6c8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9f5bf347..82dcaf03 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -339,7 +339,7 @@ def names(self): """ Return the set of all names of all entry points. """ - return set(ep.name for ep in self) + return {ep.name for ep in self} @property def groups(self): @@ -350,7 +350,7 @@ def groups(self): >>> EntryPoints().groups set() """ - return set(ep.group for ep in self) + return {ep.group for ep in self} @classmethod def _from_text_for(cls, text, dist): diff --git a/importlib_metadata/_text.py b/importlib_metadata/_text.py index 766979d9..c88cfbb2 100644 --- a/importlib_metadata/_text.py +++ b/importlib_metadata/_text.py @@ -80,7 +80,7 @@ def __hash__(self): return hash(self.lower()) def __contains__(self, other): - return super(FoldedCase, self).lower().__contains__(other.lower()) + return super().lower().__contains__(other.lower()) def in_(self, other): "Does self appear in other?" @@ -89,7 +89,7 @@ def in_(self, other): # cache lower since it's likely to be called frequently. @method_cache def lower(self): - return super(FoldedCase, self).lower() + return super().lower() def index(self, sub): return self.lower().index(sub.lower()) diff --git a/tests/fixtures.py b/tests/fixtures.py index 03c44006..1b6c1753 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -62,7 +62,7 @@ def setUp(self): class SiteDir(Fixtures): def setUp(self): - super(SiteDir, self).setUp() + super().setUp() self.site_dir = self.fixtures.enter_context(tempdir()) @@ -77,7 +77,7 @@ def add_sys_path(dir): sys.path.remove(str(dir)) def setUp(self): - super(OnSysPath, self).setUp() + super().setUp() self.fixtures.enter_context(self.add_sys_path(self.site_dir)) @@ -114,7 +114,7 @@ def main(): } def setUp(self): - super(DistInfoPkg, self).setUp() + super().setUp() build_files(DistInfoPkg.files, self.site_dir) def make_uppercase(self): @@ -139,7 +139,7 @@ class DistInfoPkgWithDot(OnSysPath, SiteDir): } def setUp(self): - super(DistInfoPkgWithDot, self).setUp() + super().setUp() build_files(DistInfoPkgWithDot.files, self.site_dir) @@ -160,13 +160,13 @@ class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir): } def setUp(self): - super(DistInfoPkgWithDotLegacy, self).setUp() + super().setUp() build_files(DistInfoPkgWithDotLegacy.files, self.site_dir) class DistInfoPkgOffPath(SiteDir): def setUp(self): - super(DistInfoPkgOffPath, self).setUp() + super().setUp() build_files(DistInfoPkg.files, self.site_dir) @@ -206,7 +206,7 @@ def main(): } def setUp(self): - super(EggInfoPkg, self).setUp() + super().setUp() build_files(EggInfoPkg.files, prefix=self.site_dir) @@ -227,7 +227,7 @@ class EggInfoFile(OnSysPath, SiteDir): } def setUp(self): - super(EggInfoFile, self).setUp() + super().setUp() build_files(EggInfoFile.files, prefix=self.site_dir) diff --git a/tests/test_main.py b/tests/test_main.py index f7c9c518..e74435aa 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -208,7 +208,7 @@ class InaccessibleSysPath(fixtures.OnSysPath, ffs.TestCase): site_dir = '/access-denied' def setUp(self): - super(InaccessibleSysPath, self).setUp() + super().setUp() self.setUpPyfakefs() self.fs.create_dir(self.site_dir, perm_bits=000) @@ -222,7 +222,7 @@ def test_discovery(self): class TestEntryPoints(unittest.TestCase): def __init__(self, *args): - super(TestEntryPoints, self).__init__(*args) + super().__init__(*args) self.ep = importlib_metadata.EntryPoint('name', 'value', 'group') def test_entry_point_pickleable(self): From 2a504fdb0cb6f3525fb143f60d972b31d309d0ba Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 18 Aug 2021 12:54:49 +0300 Subject: [PATCH 09/10] Remove redundant Python 2 code --- tests/test_api.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 5ab9feb9..75d4184d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -206,14 +206,8 @@ def _test_files(files): file.read_text() def test_file_hash_repr(self): - try: - assertRegex = self.assertRegex - except AttributeError: - # Python 2 - assertRegex = self.assertRegexpMatches - util = [p for p in files('distinfo-pkg') if p.name == 'mod.py'][0] - assertRegex(repr(util.hash), '') + self.assertRegex(repr(util.hash), '') def test_files_dist_info(self): self._test_files(files('distinfo-pkg')) From 73e5d6ed32b8d7c50f44999b3889b982c524b776 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Aug 2021 20:27:19 -0400 Subject: [PATCH 10/10] Update changelog. --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0faf0a47..2f20b890 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v4.7.0 +====== + +* #330: In ``packages_distributions``, now infer top-level + names from ``.files()`` when a ``top-level.txt`` + (Setuptools-specific metadata) is not present. + v4.6.4 ======