|
21 | 21 |
|
22 | 22 | from ._itertools import unique_everseen |
23 | 23 |
|
24 | | -from configparser import ConfigParser |
25 | 24 | from contextlib import suppress |
26 | 25 | from importlib import import_module |
27 | 26 | from importlib.abc import MetaPathFinder |
@@ -114,21 +113,18 @@ def extras(self): |
114 | 113 | match = self.pattern.match(self.value) |
115 | 114 | return list(re.finditer(r'\w+', match.group('extras') or '')) |
116 | 115 |
|
117 | | - @classmethod |
118 | | - def _from_config(cls, config): |
119 | | - return ( |
120 | | - cls(name, value, group) |
121 | | - for group in config.sections() |
122 | | - for name, value in config.items(group) |
123 | | - ) |
124 | | - |
125 | 116 | @classmethod |
126 | 117 | def _from_text(cls, text): |
127 | | - config = ConfigParser(delimiters='=') |
128 | | - # case sensitive: https://stackoverflow.com/q/1611799/812183 |
129 | | - config.optionxform = str |
130 | | - config.read_string(text) |
131 | | - return cls._from_config(config) |
| 118 | + # A hand-rolled parser is much faster than ConfigParser. |
| 119 | + if not text: |
| 120 | + return |
| 121 | + group = None |
| 122 | + for line in filter(None, map(str.strip, text.splitlines())): |
| 123 | + if line.startswith("["): |
| 124 | + group = line[1:-1] |
| 125 | + else: |
| 126 | + name, value = map(str.strip, line.split("=", 1)) |
| 127 | + yield cls(name, value, group) |
132 | 128 |
|
133 | 129 | @classmethod |
134 | 130 | def _from_text_for(cls, text, dist): |
|
0 commit comments