diff --git a/Lib/configparser.py b/Lib/configparser.py index 88015ef6086569..6b38cdc0511a16 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -997,6 +997,13 @@ def _write_section(self, fp, section_name, section_items, delimiter, unnamed=Fal # Convert all possible line-endings into '\n\t' value = (delimiter + str(value).replace('\r\n', '\n') .replace('\r', '\n').replace('\n', '\n\t')) + if value == delimiter and not self._delimiters[0][-1:].isspace(): + # The value is empty, so the space that + # `space_around_delimiters` appends would be left + # dangling at the end of the line. Keep it only when + # the delimiter itself ends in whitespace, where it is + # needed to read the option back. + value = value.rstrip(' ') else: value = "" fp.write("{}{}\n".format(key, value)) diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 7d1e68fe38100e..b8bbfcc4c5c614 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -752,6 +752,27 @@ def test_write(self): ) self.assertEqual(output.getvalue(), expect_string) + def test_write_empty_value(self): + # gh-157466: an empty value must not leave the space that + # `space_around_delimiters` appends dangling at end of line, + # while a value that really ends in whitespace keeps it. + cf = self.newconfig() + cf.add_section('sect') + cf.set('sect', 'empty', '') + cf.set('sect', 'padded', 'value ') + for space_around_delimiters in (True, False): + delimiter = self.delimiters[0] + if space_around_delimiters: + delimiter = " {} ".format(delimiter) + output = io.StringIO() + cf.write(output, space_around_delimiters=space_around_delimiters) + self.assertEqual( + output.getvalue(), + "[sect]\n" + "empty{}\n" + "padded{}value \n" + "\n".format(delimiter.rstrip(' '), delimiter)) + def test_set_string_types(self): cf = self.fromstring("[sect]\n" "option1{eq}foo\n".format(eq=self.delimiters[0])) diff --git a/Misc/NEWS.d/next/Library/2026-09-14-11-30-00.gh-issue-157466.gkzXKm.rst b/Misc/NEWS.d/next/Library/2026-09-14-11-30-00.gh-issue-157466.gkzXKm.rst new file mode 100644 index 00000000000000..39fe20f3b68bde --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-14-11-30-00.gh-issue-157466.gkzXKm.rst @@ -0,0 +1,5 @@ +Fix :mod:`configparser` leaving a trailing space at the end of the line when +:meth:`~configparser.ConfigParser.write` writes an option with an empty +value and ``space_around_delimiters`` is true. The space is still written +when the delimiter itself ends in whitespace, where it is needed to read the +option back.