]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-107625: configparser: Raise error if a missing value is continued (GH-107651)
authorPrince Roshan <princekrroshan01@gmail.com>
Wed, 6 Mar 2024 14:05:54 +0000 (19:35 +0530)
committerGitHub <noreply@github.com>
Wed, 6 Mar 2024 14:05:54 +0000 (14:05 +0000)
Co-authored-by: Éric <merwok@netwok.org>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
Doc/library/configparser.rst
Lib/configparser.py
Lib/test/test_configparser.py
Misc/NEWS.d/next/Library/2023-08-05-08-41-58.gh-issue-107625.cVSHCT.rst [new file with mode: 0644]

index 18e5bc20f3f690457cb1737a54d46f475f53d9cf..445626c267fb6fe31cf61a54de4e4d120651ec1f 100644 (file)
@@ -978,6 +978,10 @@ ConfigParser Objects
       The default *dict_type* is :class:`dict`, since it now preserves
       insertion order.
 
+   .. versionchanged:: 3.13
+      Raise a :exc:`MultilineContinuationError` when *allow_no_value* is
+      ``True``, and a key without a value is continued with an indented line.
+
    .. method:: defaults()
 
       Return a dictionary containing the instance-wide defaults.
@@ -1349,6 +1353,13 @@ Exceptions
       The ``filename`` attribute and :meth:`!__init__` constructor argument were
       removed.  They have been available using the name ``source`` since 3.2.
 
+.. exception:: MultilineContinuationError
+
+   Exception raised when a key without a corresponding value is continued with
+   an indented line.
+
+   .. versionadded:: 3.13
+
 .. rubric:: Footnotes
 
 .. [1] Config parsers allow for heavy customization.  If you are interested in
index 71362d23ec3757686bee78574ada40161310d182..241f10aee93ec497c35e1d99a9f6d2af17185771 100644 (file)
@@ -152,6 +152,7 @@ __all__ = ("NoSectionError", "DuplicateOptionError", "DuplicateSectionError",
            "NoOptionError", "InterpolationError", "InterpolationDepthError",
            "InterpolationMissingOptionError", "InterpolationSyntaxError",
            "ParsingError", "MissingSectionHeaderError",
+           "MultilineContinuationError",
            "ConfigParser", "RawConfigParser",
            "Interpolation", "BasicInterpolation",  "ExtendedInterpolation",
            "SectionProxy", "ConverterMapping",
@@ -322,6 +323,19 @@ class MissingSectionHeaderError(ParsingError):
         self.args = (filename, lineno, line)
 
 
+class MultilineContinuationError(ParsingError):
+    """Raised when a key without value is followed by continuation line"""
+    def __init__(self, filename, lineno, line):
+        Error.__init__(
+            self,
+            "Key without value continued with an indented line.\n"
+            "file: %r, line: %d\n%r"
+            %(filename, lineno, line))
+        self.source = filename
+        self.lineno = lineno
+        self.line = line
+        self.args = (filename, lineno, line)
+
 # Used in parser getters to indicate the default behaviour when a specific
 # option is not found it to raise an exception. Created to enable `None` as
 # a valid fallback value.
@@ -987,6 +1001,8 @@ class RawConfigParser(MutableMapping):
             cur_indent_level = first_nonspace.start() if first_nonspace else 0
             if (cursect is not None and optname and
                 cur_indent_level > indent_level):
+                if cursect[optname] is None:
+                    raise MultilineContinuationError(fpname, lineno, line)
                 cursect[optname].append(value)
             # a section header or option header?
             else:
index 2d7dfbde7082ee352b1442e146cb2ff063d83792..5d58e34740adafe2ab8a2dad3febc4901235460c 100644 (file)
@@ -1555,6 +1555,30 @@ class ReadFileTestCase(unittest.TestCase):
             "'[badbad'"
         )
 
+    def test_keys_without_value_with_extra_whitespace(self):
+        lines = [
+            '[SECT]\n',
+            'KEY1\n',
+            ' KEY2 = VAL2\n', # note the Space before the key!
+        ]
+        parser = configparser.ConfigParser(
+            comment_prefixes="",
+            allow_no_value=True,
+            strict=False,
+            delimiters=('=',),
+            interpolation=None,
+        )
+        with self.assertRaises(configparser.MultilineContinuationError) as dse:
+            parser.read_file(lines)
+        self.assertEqual(
+            str(dse.exception),
+            "Key without value continued with an indented line.\n"
+            "file: '<???>', line: 3\n"
+            "' KEY2 = VAL2\\n'"
+        )
+
+
+
 
 class CoverageOneHundredTestCase(unittest.TestCase):
     """Covers edge cases in the codebase."""
diff --git a/Misc/NEWS.d/next/Library/2023-08-05-08-41-58.gh-issue-107625.cVSHCT.rst b/Misc/NEWS.d/next/Library/2023-08-05-08-41-58.gh-issue-107625.cVSHCT.rst
new file mode 100644 (file)
index 0000000..bf779c4
--- /dev/null
@@ -0,0 +1,4 @@
+Raise :exc:`configparser.ParsingError` from :meth:`~configparser.ConfigParser.read`
+and :meth:`~configparser.ConfigParser.read_file` methods of
+:class:`configparser.ConfigParser` if a key without a corresponding value
+is continued (that is, followed by an indented line).