gh-116957: configparser: Do post-process values after DuplicateOptionError (GH-116958)

If you catch DuplicateOptionError / DuplicateSectionError when reading a
config file (the intention is to skip invalid config files) and then
attempt to use the ConfigParser instance, any values it *had* read
successfully so far, were stored as a list instead of string! Later
`get` calls would raise "AttributeError: 'list' object has no attribute
'find'" from somewhere deep in the interpolation code.
This commit is contained in:
David Röthlisberger 2024-03-19 09:59:08 +00:00 committed by GitHub
parent a8e93d3dca
commit b1bc37597f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 111 additions and 91 deletions

View File

@ -961,6 +961,7 @@ class RawConfigParser(MutableMapping):
lineno = 0
indent_level = 0
e = None # None, or an exception
try:
for lineno, line in enumerate(fp, start=1):
comment_start = sys.maxsize
# strip inline comments
@ -1056,6 +1057,7 @@ class RawConfigParser(MutableMapping):
# raised at the end of the file and will contain a
# list of all bogus lines
e = self._handle_error(e, fpname, lineno, line)
finally:
self._join_multiline_values()
# if any parsing errors occurred, raise an exception
if e:

View File

@ -646,6 +646,21 @@ boolean {0[0]} NO
"'opt' in section 'Bar' already exists")
self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
def test_get_after_duplicate_option_error(self):
cf = self.newconfig()
ini = textwrap.dedent("""\
[Foo]
x{equals}1
y{equals}2
y{equals}3
""".format(equals=self.delimiters[0]))
if self.strict:
with self.assertRaises(configparser.DuplicateOptionError):
cf.read_string(ini)
else:
cf.read_string(ini)
self.assertEqual(cf.get('Foo', 'x'), '1')
def test_write(self):
config_string = (
"[Long Line]\n"

View File

@ -0,0 +1,3 @@
configparser: Don't leave ConfigParser values in an invalid state (stored as
a list instead of a str) after an earlier read raised DuplicateSectionError
or DuplicateOptionError.