https://github.com/python/cpython/commit/9ac916cd42abcb05af6005d16a6861c6d2c74cbc
commit: 9ac916cd42abcb05af6005d16a6861c6d2c74cbc
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: Yhg1s <[email protected]>
date: 2026-09-08T15:29:02+02:00
summary:

[3.12] gh-156353: Fix configparser space delimiter parsing (GH-156382) (#156560)

gh-156353: Fix configparser space delimiter parsing (GH-156382)
(cherry picked from commit f973bd9d383e99eef58846772c69afa8ebeaf9a9)

Signed-off-by: sundeep8967 <[email protected]>
Co-authored-by: sundeep8967 <[email protected]>
Co-authored-by: Petr Viktorin <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-08-26-02-30-00.gh-issue-156353.abcdef.rst
M Lib/configparser.py
M Lib/test/test_configparser.py

diff --git a/Lib/configparser.py b/Lib/configparser.py
index 14f30d6c124edd9..3373aba3ab19bd5 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -550,7 +550,8 @@ class RawConfigParser(MutableMapping):
     _OPT_TMPL = r"""
         (?P<option>                        # very permissive!
             (?:(?!{delim})\S)*             # non-delimiter non-whitespace
-            (?:\s+(?:(?!{delim})\S)+)*)    # optionally more words
+            (?:(?:(?!{delim})\s)+          # optionally more
+             (?:(?!{delim})\S)+)*)         #   space-separated words
         \s*(?P<vi>{delim})\s*              # any number of space/tab,
                                            # followed by any of the
                                            # allowed delimiters,
@@ -560,7 +561,8 @@ class RawConfigParser(MutableMapping):
     _OPT_NV_TMPL = r"""
         (?P<option>                        # very permissive!
             (?:(?!{delim})\S)*             # non-delimiter non-whitespace
-            (?:\s+(?:(?!{delim})\S)+)*)    # optionally more words
+            (?:(?:(?!{delim})\s)+          # optionally more
+             (?:(?!{delim})\S)+)*)         #   space-separated words
         \s*(?:                             # any number of space/tab,
         (?P<vi>{delim})\s*                 # optionally followed by
                                            # any of the allowed
diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
index 8cf5d2d50dc1a31..030a64a747768df 100644
--- a/Lib/test/test_configparser.py
+++ b/Lib/test/test_configparser.py
@@ -44,7 +44,7 @@ class CfgParserTestCaseClass:
     default_section = configparser.DEFAULTSECT
     interpolation = configparser._UNSET
 
-    def newconfig(self, defaults=None):
+    def newconfig(self, defaults=None, **kwargs):
         arguments = dict(
             defaults=defaults,
             allow_no_value=self.allow_no_value,
@@ -57,6 +57,7 @@ def newconfig(self, defaults=None):
             default_section=self.default_section,
             interpolation=self.interpolation,
         )
+        arguments.update(kwargs)
         instance = self.config_class(**arguments)
         return instance
 
@@ -359,6 +360,32 @@ def test_basic(self):
                 the larch {0[1]} 1
             """.format(self.delimiters)))
 
+    @support.subTests('data', [
+        'foo bar=baz',
+        'foo   bar=baz',
+        'foo=bar=baz',
+        'foo = bar=baz',
+        'foo\t \t=\t \tbar=baz',
+    ])
+    def test_space_delimiter(self, data):
+        # gh-156353: Space should be accepted as a delimiter
+        cf = self.newconfig(delimiters=(' ', '='))
+        cf.read_string(f"[all]\n{data}")
+        self.assertEqual(cf.options('all'), ['foo'])
+        self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
+
+    @support.subTests('delimiter', ' =:;#x\t\0\N{RS}\N{CEDILLA}\N{CAT}')
+    @support.subTests('space_before', ['', ' ', '\t', ' \t'])
+    @support.subTests('space_after', ['', ' ', '\t', ' \t'])
+    def test_any_delimiter(self, delimiter, space_before, space_after):
+        cf = self.newconfig(
+            delimiters=(delimiter,),
+            inline_comment_prefixes=None,
+        )
+        
cf.read_string(f"[all]\nfoo{space_before}{delimiter}{space_after}bar=baz")
+        self.assertEqual(cf.options('all'), ['foo'])
+        self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
+
     def test_basic_from_dict(self):
         config = {
             "Foo Bar": {
@@ -1956,8 +1983,8 @@ class ConvertersTestCase(BasicTestCase, 
unittest.TestCase):
 
     config_class = configparser.ConfigParser
 
-    def newconfig(self, defaults=None):
-        instance = super().newconfig(defaults=defaults)
+    def newconfig(self, defaults=None, **kwargs):
+        instance = super().newconfig(defaults=defaults, **kwargs)
         instance.converters['list'] = lambda v: [e.strip() for e in v.split()
                                                  if e.strip()]
         return instance
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-26-02-30-00.gh-issue-156353.abcdef.rst 
b/Misc/NEWS.d/next/Library/2026-08-26-02-30-00.gh-issue-156353.abcdef.rst
new file mode 100644
index 000000000000000..0dd925cf596277a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-26-02-30-00.gh-issue-156353.abcdef.rst
@@ -0,0 +1 @@
+Fix :mod:`configparser` parsing when using whitespace in *delimiters*.

_______________________________________________
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