From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:59:47 +0000 (+0200) Subject: [3.14] gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d42874a1ba62b2ab954c4251c1550a342a5898c0;p=thirdparty%2FPython%2Fcpython.git [3.14] gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine` (GH-148452) (#148532) gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine` (GH-148452) (cherry picked from commit 2662db0c45aa16232136628457a53681b6683c25) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- diff --git a/Lib/configparser.py b/Lib/configparser.py index e76647d339e9..a53ac8727644 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -315,12 +315,15 @@ class ParsingError(Error): def append(self, lineno, line): self.errors.append((lineno, line)) - self.message += '\n\t[line %2d]: %s' % (lineno, repr(line)) + self.message += f'\n\t[line {lineno:2d}]: {line!r}' def combine(self, others): + messages = [self.message] for other in others: - for error in other.errors: - self.append(*error) + for lineno, line in other.errors: + self.errors.append((lineno, line)) + messages.append(f'\n\t[line {lineno:2d}]: {line!r}') + self.message = "".join(messages) return self @staticmethod diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index d7c4f19c1a5e..8d8dd2a2bf27 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -1729,6 +1729,19 @@ class ExceptionPicklingTestCase(unittest.TestCase): self.assertEqual(e1.message, e2.message) self.assertEqual(repr(e1), repr(e2)) + def test_combine_error_linear_complexity(self): + # Ensure that ParsingError.combine() has linear complexity. + # See https://github.com/python/cpython/issues/148370. + n = 50000 + s = '[*]\n' + (err_line := '=\n') * n + p = configparser.ConfigParser(strict=False) + with self.assertRaises(configparser.ParsingError) as cm: + p.read_string(s) + errlines = cm.exception.message.splitlines() + self.assertEqual(len(errlines), n + 1) + self.assertStartsWith(errlines[0], "Source contains parsing errors: ") + self.assertEqual(errlines[42], f"\t[line {43:2d}]: {err_line!r}") + def test_nosectionerror(self): import pickle e1 = configparser.NoSectionError('section') diff --git a/Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst b/Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst new file mode 100644 index 000000000000..3bb662350796 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst @@ -0,0 +1,2 @@ +:mod:`configparser`: prevent quadratic behavior when a :exc:`~configparser.ParsingError` +is raised after a parser fails to parse multiple lines. Patch by Bénédikt Tran.