Skip to content

Commit a69b14b

Browse files
[3.13] gh-148370: prevent quadratic behavior in configparser.ParsingError.combine (GH-148452) (#148533)
gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine` (GH-148452) (cherry picked from commit 2662db0) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent d82c491 commit a69b14b

File tree

3 files changed

+21
-3
lines changed

3 files changed

+21
-3
lines changed

Lib/configparser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,15 @@ def __init__(self, source, *args):
316316

317317
def append(self, lineno, line):
318318
self.errors.append((lineno, line))
319-
self.message += '\n\t[line %2d]: %s' % (lineno, repr(line))
319+
self.message += f'\n\t[line {lineno:2d}]: {line!r}'
320320

321321
def combine(self, others):
322+
messages = [self.message]
322323
for other in others:
323-
for error in other.errors:
324-
self.append(*error)
324+
for lineno, line in other.errors:
325+
self.errors.append((lineno, line))
326+
messages.append(f'\n\t[line {lineno:2d}]: {line!r}')
327+
self.message = "".join(messages)
325328
return self
326329

327330
@staticmethod

Lib/test/test_configparser.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,19 @@ def test_error(self):
17291729
self.assertEqual(e1.message, e2.message)
17301730
self.assertEqual(repr(e1), repr(e2))
17311731

1732+
def test_combine_error_linear_complexity(self):
1733+
# Ensure that ParsingError.combine() has linear complexity.
1734+
# See https://github.com/python/cpython/issues/148370.
1735+
n = 50000
1736+
s = '[*]\n' + (err_line := '=\n') * n
1737+
p = configparser.ConfigParser(strict=False)
1738+
with self.assertRaises(configparser.ParsingError) as cm:
1739+
p.read_string(s)
1740+
errlines = cm.exception.message.splitlines()
1741+
self.assertEqual(len(errlines), n + 1)
1742+
self.assertTrue(errlines[0].startswith("Source contains parsing errors: "))
1743+
self.assertEqual(errlines[42], f"\t[line {43:2d}]: {err_line!r}")
1744+
17321745
def test_nosectionerror(self):
17331746
import pickle
17341747
e1 = configparser.NoSectionError('section')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`configparser`: prevent quadratic behavior when a :exc:`~configparser.ParsingError`
2+
is raised after a parser fails to parse multiple lines. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)