https://github.com/python/cpython/commit/d42874a1ba62b2ab954c4251c1550a342a5898c0
commit: d42874a1ba62b2ab954c4251c1550a342a5898c0
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: picnixz <[email protected]>
date: 2026-04-13T22:59:47Z
summary:

[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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-04-12-16-40-11.gh-issue-148370.0Li2EK.rst
M Lib/configparser.py
M Lib/test/test_configparser.py

diff --git a/Lib/configparser.py b/Lib/configparser.py
index e76647d339e913..a53ac87276445a 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -315,12 +315,15 @@ def __init__(self, source, *args):
 
     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 d7c4f19c1a5ef0..8d8dd2a2bf27fb 100644
--- a/Lib/test/test_configparser.py
+++ b/Lib/test/test_configparser.py
@@ -1729,6 +1729,19 @@ def test_error(self):
             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 00000000000000..3bb662350796f6
--- /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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to