From f00d5c3be68cc35412facd6af8cc0960966dd347 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 26 Aug 2021 10:01:57 -0400 Subject: [PATCH 1/3] Add test capturing failure. Ref #344. --- tests/test_main.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 081e9fa1..e73af818 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -285,7 +285,7 @@ def test_unicode_dir_on_sys_path(self): list(distributions()) -class PackagesDistributionsTest(fixtures.ZipFixtures, unittest.TestCase): +class PackagesDistributionsPrebuiltTest(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'] @@ -297,3 +297,24 @@ def test_packages_distributions_example2(self): """ self._fixture_on_path('example2-1.0.0-py3-none-any.whl') assert packages_distributions()['example2'] == ['example2'] + + +class PackagesDistributionsTest( + fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase +): + def test_packages_distributions_neither_toplevel_nor_files(self): + """ + Test a package built without 'top-level.txt' or a file list. + """ + fixtures.build_files( + { + 'trim_example-1.0.0.dist-info': { + 'METADATA': """ + Name: trim_example + Version: 1.0.0 + """, + } + }, + prefix=self.site_dir, + ) + packages_distributions() From 9426bd954ad39b06df9b89d597bdba299d1d839f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 26 Aug 2021 10:18:46 -0400 Subject: [PATCH 2/3] Apply always_iterable to .files to ensure iterable even when None. --- importlib_metadata/__init__.py | 4 +-- importlib_metadata/_itertools.py | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9bb06412..6c554558 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -23,7 +23,7 @@ pypy_partial, ) from ._functools import method_cache -from ._itertools import unique_everseen +from ._itertools import always_iterable, unique_everseen from ._meta import PackageMetadata, SimplePath from contextlib import suppress @@ -1025,6 +1025,6 @@ def _top_level_declared(dist): def _top_level_inferred(dist): return { f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name - for f in dist.files + for f in always_iterable(dist.files) if f.suffix == ".py" } diff --git a/importlib_metadata/_itertools.py b/importlib_metadata/_itertools.py index dd45f2f0..d4ca9b91 100644 --- a/importlib_metadata/_itertools.py +++ b/importlib_metadata/_itertools.py @@ -17,3 +17,57 @@ def unique_everseen(iterable, key=None): if k not in seen: seen_add(k) yield element + + +# copied from more_itertools 8.8 +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) From 131c1d2b4c9d720aad79d96cbba19779d42db5e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 26 Aug 2021 10:20:33 -0400 Subject: [PATCH 3/3] Update changelog. --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2f20b890..d5d1d016 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v4.7.1 +====== + +* #344: Fixed regression in ``packages_distributions`` when + neither top-level.txt nor a files manifest is present. + v4.7.0 ======