Skip to content

Commit 9426bd9

Browse files
committed
Apply always_iterable to .files to ensure iterable even when None.
1 parent f00d5c3 commit 9426bd9

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

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,))

0 commit comments

Comments
 (0)