summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrince Roshan <princekrroshan01@gmail.com>2024-03-06 14:05:54 (GMT)
committerGitHub <noreply@github.com>2024-03-06 14:05:54 (GMT)
commite800265aa1f3451855a2fc14fbafc4d89392e35c (patch)
tree8cd6eb829895313aacae2c12f33f12b1345aa0c7
parent27858e2a17924dfac9a10efc17caee1f5126ea19 (diff)
downloadcpython-e800265aa1f3451855a2fc14fbafc4d89392e35c.zip
cpython-e800265aa1f3451855a2fc14fbafc4d89392e35c.tar.gz
cpython-e800265aa1f3451855a2fc14fbafc4d89392e35c.tar.bz2
gh-107625: configparser: Raise error if a missing value is continued (GH-107651)
Co-authored-by: Éric <merwok@netwok.org> Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
-rw-r--r--Doc/library/configparser.rst11
-rw-r--r--Lib/configparser.py16
-rw-r--r--Lib/test/test_configparser.py24
-rw-r--r--Misc/NEWS.d/next/Library/2023-08-05-08-41-58.gh-issue-107625.cVSHCT.rst4
4 files changed, 55 insertions, 0 deletions
diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst
index 18e5bc2..445626c 100644
--- a/Doc/library/configparser.rst
+++ b/Doc/library/configparser.rst
@@ -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
diff --git a/Lib/configparser.py b/Lib/configparser.py
index 71362d2..241f10a 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -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:
diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
index 2d7dfbd..5d58e34 100644
--- a/Lib/test/test_configparser.py
+++ b/Lib/test/test_configparser.py
@@ -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
index 0000000..bf779c4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-08-05-08-41-58.gh-issue-107625.cVSHCT.rst
@@ -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).