diff --git a/CHANGELOG b/CHANGELOG index 792c126..2965c7d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +2.3.1 +===== + +* fix quadratic parse time for values with many continuation lines (#78, regarding #77) +* internal: ``iniconfig._parse.ParsedLine.value`` is now a list of lines + 2.3.0 ===== diff --git a/src/iniconfig/_parse.py b/src/iniconfig/_parse.py index 57b9b44..178bb45 100644 --- a/src/iniconfig/_parse.py +++ b/src/iniconfig/_parse.py @@ -10,7 +10,7 @@ class ParsedLine(NamedTuple): lineno: int section: str | None name: str | None - value: str | None + value: list[str] | None def parse_ini_data( @@ -56,7 +56,7 @@ def parse_ini_data( if name in sections_data[section]: raise ParseError(path, lineno, f"duplicate name {name!r}") assert value is not None - sections_data[section][name] = value + sections_data[section][name] = "\n".join(value) return sections_data, sources @@ -76,7 +76,7 @@ def parse_lines( ) # new value if name is not None and data is not None: - result.append(ParsedLine(lineno, section, name, data)) + result.append(ParsedLine(lineno, section, name, [data] if data else [])) # new section elif name is not None and data is None: if not name: @@ -85,17 +85,10 @@ def parse_lines( result.append(ParsedLine(lineno, section, None, None)) # continuation elif name is None and data is not None: - if not result: + value = result[-1].value if result else None + if value is None: raise ParseError(path, lineno, "unexpected value continuation") - last = result.pop() - if last.name is None: - raise ParseError(path, lineno, "unexpected value continuation") - - if last.value: - last = last._replace(value=f"{last.value}\n{data}") - else: - last = last._replace(value=data) - result.append(last) + value.append(data) return result diff --git a/testing/test_iniconfig.py b/testing/test_iniconfig.py index 85193c5..8a31bbc 100644 --- a/testing/test_iniconfig.py +++ b/testing/test_iniconfig.py @@ -11,36 +11,46 @@ check_tokens: dict[str, tuple[str, list[PL]]] = { "section": ("[section]", [PL(0, "section", None, None)]), - "value": ("value = 1", [PL(0, None, "value", "1")]), + "value": ("value = 1", [PL(0, None, "value", ["1"])]), "value in section": ( "[section]\nvalue=1", - [PL(0, "section", None, None), PL(1, "section", "value", "1")], + [PL(0, "section", None, None), PL(1, "section", "value", ["1"])], ), + "empty value": ("value =", [PL(0, None, "value", [])]), "value with continuation": ( "names =\n Alice\n Bob", - [PL(0, None, "names", "Alice\nBob")], + [PL(0, None, "names", ["Alice", "Bob"])], ), "value with aligned continuation": ( "names = Alice\n Bob", - [PL(0, None, "names", "Alice\nBob")], + [PL(0, None, "names", ["Alice", "Bob"])], + ), + "continuations on several values": ( + "a = 1\n 2\n\n 3\n[s]\nb =\n x\n y\nc = z", + [ + PL(0, None, "a", ["1", "2", "3"]), + PL(4, "s", None, None), + PL(5, "s", "b", ["x", "y"]), + PL(8, "s", "c", ["z"]), + ], ), "blank line": ( "[section]\n\nvalue=1", - [PL(0, "section", None, None), PL(2, "section", "value", "1")], + [PL(0, "section", None, None), PL(2, "section", "value", ["1"])], ), "comment": ("# comment", []), - "comment on value": ("value = 1", [PL(0, None, "value", "1")]), + "comment on value": ("value = 1", [PL(0, None, "value", ["1"])]), "comment on section": ("[section] #comment", [PL(0, "section", None, None)]), "comment2": ("; comment", []), "comment2 on section": ("[section] ;comment", [PL(0, "section", None, None)]), "pseudo section syntax in value": ( "name = value []", - [PL(0, None, "name", "value []")], + [PL(0, None, "name", ["value []"])], ), - "assignment in value": ("value = x = 3", [PL(0, None, "value", "x = 3")]), - "use of colon for name-values": ("name: y", [PL(0, None, "name", "y")]), - "use of colon without space": ("value:y=5", [PL(0, None, "value", "y=5")]), - "equality gets precedence": ("value=xyz:5", [PL(0, None, "value", "xyz:5")]), + "assignment in value": ("value = x = 3", [PL(0, None, "value", ["x = 3"])]), + "use of colon for name-values": ("name: y", [PL(0, None, "name", ["y"])]), + "use of colon without space": ("value:y=5", [PL(0, None, "value", ["y=5"])]), + "equality gets precedence": ("value=xyz:5", [PL(0, None, "value", ["xyz:5"])]), }