diff --git a/Lib/configparser.py b/Lib/configparser.py index b7a0b0b56893c5..b127c37bba531d 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -1087,9 +1087,15 @@ def _write_section(self, fp, section_name, section_items, delimiter, unnamed=Fal value = self._interpolation.before_write(self, section_name, key, value) if value is not None or not self._allow_no_value: # Convert all possible line-endings into '\n\t' - value = delimiter + str(value).replace('\r\n', '\n').replace( - '\r', '\n' - ).replace('\n', '\n\t') + str_value = str(value).replace('\r\n', '\n').replace('\r', '\n') + for continuation in str_value.split('\n')[1:]: + if continuation and continuation[0] in ' \t': + raise InvalidWriteError( + f"Cannot write value for key {key!r}; " + f"continuation line has leading whitespace " + f"that would be silently stripped on read-back" + ) + value = delimiter + str_value.replace('\n', '\n\t') else: value = "" fp.write(f"{key}{value}\n") diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 61b8d1c527d347..8239763068b031 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -2498,6 +2498,14 @@ def test_section_bracket_in_key(self): cfg.write(output) output.close() + def test_leading_whitespace_in_multiline_value(self): + cfg = configparser.ConfigParser() + cfg.add_section('section1') + cfg.set('section1', 'key', 'line1\n\tline2') + output = io.StringIO() + with self.assertRaises(configparser.InvalidWriteError): + cfg.write(output) + output.close() class ReDoSTestCase(unittest.TestCase): """Regression tests for quadratic regex backtracking (gh-146333).""" diff --git a/Misc/NEWS.d/next/Library/2026-11-04-18-30-35.gh-issue-161317.4XniSh.rst b/Misc/NEWS.d/next/Library/2026-11-04-18-30-35.gh-issue-161317.4XniSh.rst new file mode 100644 index 00000000000000..baf871a6ffe9d4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-04-18-30-35.gh-issue-161317.4XniSh.rst @@ -0,0 +1,3 @@ +:mod:`configparser` now raises :exc:`~configparser.InvalidWriteError` when +writing a value whose continuation lines contain leading whitespace, since +that whitespace would be silently stripped on read-back.