Skip to content

gh-157466: Fix configparser writing a trailing space for empty values - #157467

Open
jang-hs wants to merge 2 commits into
python:mainfrom
jang-hs:fix/issue-157466-configparser-empty-value-trailing-space
Open

gh-157466: Fix configparser writing a trailing space for empty values#157467
jang-hs wants to merge 2 commits into
python:mainfrom
jang-hs:fix/issue-157466-configparser-empty-value-trailing-space

Conversation

@jang-hs

@jang-hs jang-hs commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

RawConfigParser.write() builds the delimiter as " {} ".format(self._delimiters[0]) when space_around_delimiters is true, and _write_section() concatenates it unconditionally. An option with an empty value therefore gets only the delimiter, and the padding space is left dangling at the end of the line:

>>> c = configparser.ConfigParser()
>>> c["DEFAULT"] = {"ok": "value", "error": ""}
>>> buf = io.StringIO(); c.write(buf); buf.getvalue()
'[DEFAULT]\nok = value\nerror = \n\n'

This drops the space when the value is empty, giving error =.

Two cases it deliberately leaves alone:

  • Delimiters that end in whitespace (delimiters=(' ',)). There the trailing whitespace is the separator, not padding, so stripping it would produce a bare key that cannot be read back. The guard checks self._delimiters[0] rather than the padded delimiter for exactly this reason.
  • Values that genuinely end in whitespace ({"k": "v "}k = v ). Stripping the whole line, as the issue suggests, would silently truncate them.

write() is the only caller of _write_section() and all three call sites pass the same delimiter, so one guard there covers every path; no sibling caller has the same bug. The behaviour dates to 96a60ae (2010), which introduced space_around_delimiters and concatenated the padded delimiter without an empty-value case - the trailing space looks like an artifact rather than a deliberate choice.

Output for non-empty values, for space_around_delimiters=False, and for valueless options under allow_no_value is byte-for-byte unchanged.

Fixes #157466

Testing

test_write_empty_value is added next to test_write, so it runs across the existing delimiter and parser variants (13 parameterisations). It pins both halves: the empty value loses the space, the whitespace-ending value keeps it. It fails on main in all 13 and passes with the change.

Also run: full test_configparser (396 tests), plus test_logging test_collections test_sysconfig test_venv test_importlib test_zipfile test_email (4,540 tests) - all pass. make patchcheck and the ruff-check configs CI uses for Lib/ and Lib/test/ are clean. IDLE is the other stdlib consumer that writes config files; its tests could not run here because _tkinter was not built, but idle_test.test_config only asserts file existence, not written text.

I did not touch the whitespace-delimiter round-trip failure visible in that area - that is #157456, and gh-157457 is already fixing it on the parse side.

Found with AI assistance; I reproduced and verified the behaviour and the fix on a locally built interpreter and understand them.

…values

RawConfigParser._write_section() always concatenates the padded
delimiter, so an option with an empty value is written as "key = ",
leaving a space at the end of the line.

Drop that space when the value is empty, but keep it when the
delimiter itself ends in whitespace (delimiters=(' ',)), where it is
the separator and is needed to read the option back. Values that
genuinely end in whitespace are untouched.

write() is the only caller of _write_section(), and all three call
sites pass the same delimiter, so the guard belongs there.
@jang-hs
jang-hs force-pushed the fix/issue-157466-configparser-empty-value-trailing-space branch from 59093b5 to 204e82d Compare September 14, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

configparser: whitespace not stripped when writing empty values

1 participant