From a69b14bc01a23aa4024660e6f1e52fc78308fafa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 14 Apr 2026 01:05:38 +0200 Subject: [PATCH] [3.13] gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine` (GH-148452) (#148533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- Lib/configparser.py | 9 ++++++--- Lib/test/test_configparser.py | 13 +++++++++++++ .../2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst diff --git a/Lib/configparser.py b/Lib/configparser.py index 05b86acb919..71f842b3902 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -316,12 +316,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 d4fdd92bdc6..bef3c11b941 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.assertTrue(errlines[0].startswith("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 00000000000..3bb66235079 --- /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.