From d4fbf8f6feabc5b57211600c5e1a9dd4241ec7a3 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Fri, 24 Jul 2026 20:13:19 +0800 Subject: [PATCH] gh-154596: Fix configparser write() losing unnamed-section items to DEFAULT --- Lib/configparser.py | 4 ++-- Lib/test/test_configparser.py | 11 +++++++++++ .../2026-07-24-20-00-00.gh-issue-154596.CfgWrt.rst | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-24-20-00-00.gh-issue-154596.CfgWrt.rst diff --git a/Lib/configparser.py b/Lib/configparser.py index 3c452afe8ade485..9a85d2325c0337b 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -971,11 +971,11 @@ def write(self, fp, space_around_delimiters=True): d = " {} ".format(self._delimiters[0]) else: d = self._delimiters[0] + if UNNAMED_SECTION in self._sections and self._sections[UNNAMED_SECTION]: + self._write_section(fp, UNNAMED_SECTION, self._sections[UNNAMED_SECTION].items(), d, unnamed=True) if self._defaults: self._write_section(fp, self.default_section, self._defaults.items(), d) - if UNNAMED_SECTION in self._sections and self._sections[UNNAMED_SECTION]: - self._write_section(fp, UNNAMED_SECTION, self._sections[UNNAMED_SECTION].items(), d, unnamed=True) for section in self._sections: if section is UNNAMED_SECTION: diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 4783943f71a1092..974d75cbc402b66 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -2250,6 +2250,17 @@ def test_add_section(self): cfg.write(output) self.assertEqual(output.getvalue(), 'a = 1\n\n') + def test_write_roundtrip_with_default(self): + US = configparser.UNNAMED_SECTION + cfg = configparser.ConfigParser(allow_unnamed_section=True) + cfg.read_string('key1 = val1\n[DEFAULT]\ndkey = dval\n[sect1]\nkey2 = val2\n') + out = io.StringIO() + cfg.write(out) + cfg2 = configparser.ConfigParser(allow_unnamed_section=True) + cfg2.read_string(out.getvalue()) + self.assertIn('key1', cfg2[US]) + self.assertNotIn('key1', cfg2.defaults()) + def test_disabled_error(self): with self.assertRaises(configparser.MissingSectionHeaderError): configparser.ConfigParser().read_string("a = 1") diff --git a/Misc/NEWS.d/next/Library/2026-07-24-20-00-00.gh-issue-154596.CfgWrt.rst b/Misc/NEWS.d/next/Library/2026-07-24-20-00-00.gh-issue-154596.CfgWrt.rst new file mode 100644 index 000000000000000..79d6a6f7219f1ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-24-20-00-00.gh-issue-154596.CfgWrt.rst @@ -0,0 +1,3 @@ +Fix :meth:`configparser.ConfigParser.write` outputting unnamed-section +items after the ``[DEFAULT]`` header, causing them to be absorbed into +DEFAULT on re-read. Patch by tonghuaroot.