Skip to content

Commit 5806de1

Browse files
committed
Unify section parser behavior. Removes reliance on regex (ref #292). Fixes #295.
1 parent 50a1549 commit 5806de1

File tree

1 file changed

+29
-43
lines changed

1 file changed

+29
-43
lines changed

importlib_metadata/__init__.py

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,25 @@ class Sectioned:
6565
A simple entry point config parser for performance
6666
6767
>>> res = Sectioned.get_sections(Sectioned._sample)
68-
>>> sec, values = next(res)
68+
>>> sec, pair = next(res)
6969
>>> sec
7070
'sec1'
71-
>>> [(key, value) for key, value in values]
72-
[('a', '1'), ('b', '2')]
73-
>>> sec, values = next(res)
71+
>>> tuple(pair)
72+
('a', '1')
73+
>>> sec, pair = next(res)
74+
>>> tuple(pair)
75+
('b', '2')
76+
>>> sec, pair = next(res)
7477
>>> sec
7578
'sec2'
76-
>>> [(key, value) for key, value in values]
77-
[('a', '2')]
79+
>>> tuple(pair)
80+
('a', '2')
7881
>>> list(res)
7982
[]
8083
"""
8184

85+
Pair = collections.namedtuple('Pair', 'name value')
86+
8287
_sample = textwrap.dedent(
8388
"""
8489
[sec1]
@@ -91,25 +96,25 @@ class Sectioned:
9196
"""
9297
).lstrip()
9398

94-
def __init__(self):
95-
self.section = None
96-
97-
def __call__(self, line):
98-
if line.startswith('[') and line.endswith(']'):
99-
# new section
100-
self.section = line.strip('[]')
101-
return
102-
return self.section
103-
10499
@classmethod
105100
def get_sections(cls, text):
106-
lines = filter(cls.valid, map(str.strip, text.splitlines()))
107101
return (
108-
(section, map(cls.parse_value, values))
109-
for section, values in itertools.groupby(lines, cls())
110-
if section is not None
102+
(section.name, cls.parse_value(section.value))
103+
for section in cls.read(text, filter_=cls.valid)
104+
if section.name is not None
111105
)
112106

107+
@staticmethod
108+
def read(text, filter_=None):
109+
lines = filter(filter_, map(str.strip, text.splitlines()))
110+
name = None
111+
for value in lines:
112+
section_match = value.startswith('[') and value.endswith(']')
113+
if section_match:
114+
name = value.strip('[]')
115+
continue
116+
yield Sectioned.Pair(name, value)
117+
113118
@staticmethod
114119
def valid(line):
115120
return line and not line.startswith('#')
@@ -256,8 +261,7 @@ def _from_text(cls, text):
256261
def _parse_groups(text):
257262
return (
258263
(name, value, section)
259-
for section, values in Sectioned.get_sections(text)
260-
for name, value in values
264+
for section, (name, value) in Sectioned.get_sections(text)
261265
)
262266

263267

@@ -573,24 +577,7 @@ def _read_egg_info_reqs(self):
573577

574578
@classmethod
575579
def _deps_from_requires_text(cls, source):
576-
section_pairs = cls._read_sections(source.splitlines())
577-
sections = {
578-
section: list(map(operator.itemgetter('line'), results))
579-
for section, results in itertools.groupby(
580-
section_pairs, operator.itemgetter('section')
581-
)
582-
}
583-
return cls._convert_egg_info_reqs_to_simple_reqs(sections)
584-
585-
@staticmethod
586-
def _read_sections(lines):
587-
section = None
588-
for line in filter(None, lines):
589-
section_match = re.match(r'\[(.*)\]$', line)
590-
if section_match:
591-
section = section_match.group(1)
592-
continue
593-
yield locals()
580+
return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
594581

595582
@staticmethod
596583
def _convert_egg_info_reqs_to_simple_reqs(sections):
@@ -615,9 +602,8 @@ def parse_condition(section):
615602
conditions = list(filter(None, [markers, make_condition(extra)]))
616603
return '; ' + ' and '.join(conditions) if conditions else ''
617604

618-
for section, deps in sections.items():
619-
for dep in deps:
620-
yield dep + parse_condition(section)
605+
for section in sections:
606+
yield section.value + parse_condition(section.name)
621607

622608

623609
class DistributionFinder(MetaPathFinder):

0 commit comments

Comments
 (0)