From 78c70d648da5d17631cb44f10869a3b3ef644cc6 Mon Sep 17 00:00:00 2001 From: hanjinpeng Date: Sun, 13 Sep 2026 21:47:05 -0400 Subject: [PATCH] utils: make tidy() also clean up the first section tidy() started iterating over the top-level lines at index 1, so when a file starts directly with a section header (the common case), that first section was never passed to tidy_section() and its consecutive empty lines were kept: >>> cfg = INIConfig(StringIO('[a]\nx = 1\n\n\n\ny = 2\n')) >>> tidy(cfg); str(cfg) '[a]\nx = 1\n\n\n\ny = 2\n' Start at index 0 and only compare with the previous line when there is one. --- iniparse/utils.py | 5 +++-- tests/test_tidy.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/iniparse/utils.py b/iniparse/utils.py index b3cc59e..8af6cb8 100644 --- a/iniparse/utils.py +++ b/iniparse/utils.py @@ -19,12 +19,13 @@ def tidy(cfg: compat.RawConfigParser): if isinstance(cfg, compat.RawConfigParser): cfg = cfg.data cont = cfg._data.contents - i = 1 + # start at 0 so that a section at the very top of the file is tidied too + i = 0 while i < len(cont): if isinstance(cont[i], LineContainer): tidy_section(cont[i]) i += 1 - elif (isinstance(cont[i-1], EmptyLine) and + elif (i > 0 and isinstance(cont[i-1], EmptyLine) and isinstance(cont[i], EmptyLine)): del cont[i] else: diff --git a/tests/test_tidy.py b/tests/test_tidy.py index bfbdd74..14c9f78 100644 --- a/tests/test_tidy.py +++ b/tests/test_tidy.py @@ -232,3 +232,26 @@ def test_compat(self): c=3 """)) + + def test_first_section_is_tidied(self): + """ + :id: dce50a4b-cb18-4bf7-bf7a-d0260cac1162 + :title: tidy() cleans up a section at the start of the file + :description: + Verifies that tidy() collapses consecutive empty lines in the + first section even when the file starts directly with a + section header. + :tags: Tier 2 + :steps: + 1. Parse a file starting with `[a]` whose options are separated + by several empty lines, followed by a similar section `[b]`. + 2. Call tidy() and render the configuration. + :expectedresults: + 1. File is parsed. + 2. Both sections contain only single empty lines. + """ + cfg = INIConfig(StringIO('[a]\nx = 1\n\n\n\ny = 2\n\n' + '[b]\nz = 3\n\n\n\nw = 4\n')) + tidy(cfg) + self.assertEqual(str(cfg), '[a]\nx = 1\n\ny = 2\n\n' + '[b]\nz = 3\n\nw = 4\n')