mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.12] gh-116957: configparser: Do post-process values after DuplicateOptionError (GH-116958) (GH-117013)
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.
(cherry picked from commit b1bc37597f
)
This commit is contained in:
parent
688623d402
commit
0fc8ae4e28
3 changed files with 109 additions and 89 deletions
|
@ -995,6 +995,7 @@ class RawConfigParser(MutableMapping):
|
||||||
lineno = 0
|
lineno = 0
|
||||||
indent_level = 0
|
indent_level = 0
|
||||||
e = None # None, or an exception
|
e = None # None, or an exception
|
||||||
|
try:
|
||||||
for lineno, line in enumerate(fp, start=1):
|
for lineno, line in enumerate(fp, start=1):
|
||||||
comment_start = sys.maxsize
|
comment_start = sys.maxsize
|
||||||
# strip inline comments
|
# strip inline comments
|
||||||
|
@ -1088,6 +1089,7 @@ class RawConfigParser(MutableMapping):
|
||||||
# raised at the end of the file and will contain a
|
# raised at the end of the file and will contain a
|
||||||
# list of all bogus lines
|
# list of all bogus lines
|
||||||
e = self._handle_error(e, fpname, lineno, line)
|
e = self._handle_error(e, fpname, lineno, line)
|
||||||
|
finally:
|
||||||
self._join_multiline_values()
|
self._join_multiline_values()
|
||||||
# if any parsing errors occurred, raise an exception
|
# if any parsing errors occurred, raise an exception
|
||||||
if e:
|
if e:
|
||||||
|
|
|
@ -647,6 +647,21 @@ boolean {0[0]} NO
|
||||||
"'opt' in section 'Bar' already exists")
|
"'opt' in section 'Bar' already exists")
|
||||||
self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
|
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):
|
def test_write(self):
|
||||||
config_string = (
|
config_string = (
|
||||||
"[Long Line]\n"
|
"[Long Line]\n"
|
||||||
|
|
|
@ -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.
|
Loading…
Add table
Add a link
Reference in a new issue