Skip to content

Commit 0bc774f

Browse files
authored
Merge pull request #345 from python/bugfix/344-test
Wrap .files call in always_iterable per recommendation
2 parents 15df009 + 131c1d2 commit 0bc774f

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
v4.7.1
2+
======
3+
4+
* #344: Fixed regression in ``packages_distributions`` when
5+
neither top-level.txt nor a files manifest is present.
6+
17
v4.7.0
28
======
39

importlib_metadata/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
pypy_partial,
2424
)
2525
from ._functools import method_cache
26-
from ._itertools import unique_everseen
26+
from ._itertools import always_iterable, unique_everseen
2727
from ._meta import PackageMetadata, SimplePath
2828

2929
from contextlib import suppress
@@ -1025,6 +1025,6 @@ def _top_level_declared(dist):
10251025
def _top_level_inferred(dist):
10261026
return {
10271027
f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name
1028-
for f in dist.files
1028+
for f in always_iterable(dist.files)
10291029
if f.suffix == ".py"
10301030
}

importlib_metadata/_itertools.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,57 @@ def unique_everseen(iterable, key=None):
1717
if k not in seen:
1818
seen_add(k)
1919
yield element
20+
21+
22+
# copied from more_itertools 8.8
23+
def always_iterable(obj, base_type=(str, bytes)):
24+
"""If *obj* is iterable, return an iterator over its items::
25+
26+
>>> obj = (1, 2, 3)
27+
>>> list(always_iterable(obj))
28+
[1, 2, 3]
29+
30+
If *obj* is not iterable, return a one-item iterable containing *obj*::
31+
32+
>>> obj = 1
33+
>>> list(always_iterable(obj))
34+
[1]
35+
36+
If *obj* is ``None``, return an empty iterable:
37+
38+
>>> obj = None
39+
>>> list(always_iterable(None))
40+
[]
41+
42+
By default, binary and text strings are not considered iterable::
43+
44+
>>> obj = 'foo'
45+
>>> list(always_iterable(obj))
46+
['foo']
47+
48+
If *base_type* is set, objects for which ``isinstance(obj, base_type)``
49+
returns ``True`` won't be considered iterable.
50+
51+
>>> obj = {'a': 1}
52+
>>> list(always_iterable(obj)) # Iterate over the dict's keys
53+
['a']
54+
>>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
55+
[{'a': 1}]
56+
57+
Set *base_type* to ``None`` to avoid any special handling and treat objects
58+
Python considers iterable as iterable:
59+
60+
>>> obj = 'foo'
61+
>>> list(always_iterable(obj, base_type=None))
62+
['f', 'o', 'o']
63+
"""
64+
if obj is None:
65+
return iter(())
66+
67+
if (base_type is not None) and isinstance(obj, base_type):
68+
return iter((obj,))
69+
70+
try:
71+
return iter(obj)
72+
except TypeError:
73+
return iter((obj,))

tests/test_main.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def test_unicode_dir_on_sys_path(self):
285285
list(distributions())
286286

287287

288-
class PackagesDistributionsTest(fixtures.ZipFixtures, unittest.TestCase):
288+
class PackagesDistributionsPrebuiltTest(fixtures.ZipFixtures, unittest.TestCase):
289289
def test_packages_distributions_example(self):
290290
self._fixture_on_path('example-21.12-py3-none-any.whl')
291291
assert packages_distributions()['example'] == ['example']
@@ -297,3 +297,24 @@ def test_packages_distributions_example2(self):
297297
"""
298298
self._fixture_on_path('example2-1.0.0-py3-none-any.whl')
299299
assert packages_distributions()['example2'] == ['example2']
300+
301+
302+
class PackagesDistributionsTest(
303+
fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase
304+
):
305+
def test_packages_distributions_neither_toplevel_nor_files(self):
306+
"""
307+
Test a package built without 'top-level.txt' or a file list.
308+
"""
309+
fixtures.build_files(
310+
{
311+
'trim_example-1.0.0.dist-info': {
312+
'METADATA': """
313+
Name: trim_example
314+
Version: 1.0.0
315+
""",
316+
}
317+
},
318+
prefix=self.site_dir,
319+
)
320+
packages_distributions()

0 commit comments

Comments
 (0)