]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148370: prevent quadratic behavior in `configparser.ParsingError.combine` (#148452)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Mon, 13 Apr 2026 22:32:54 +0000 (00:32 +0200)
committerGitHub <noreply@github.com>
Mon, 13 Apr 2026 22:32:54 +0000 (00:32 +0200)
Lib/configparser.py
Lib/test/test_configparser.py
Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst [new file with mode: 0644]

index e76647d339e9138618b43884fe8453f8d4355aa5..a53ac87276445adbd4c7222bc88b68b06798866d 100644 (file)
@@ -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
index d7c4f19c1a5ef029885d66d8d07004e8270493de..8d8dd2a2bf27fbf358489850dcb22cbc7af46845 100644 (file)
@@ -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 (file)
index 0000000..3bb6623
--- /dev/null
@@ -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.