Skip to content

Commit

Permalink
gh-107625: configparser: Raise error if a missing value is continued (G…
Browse files Browse the repository at this point in the history
…H-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>
  • Loading branch information
4 people committed Mar 6, 2024
1 parent 27858e2 commit e800265
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Doc/library/configparser.rst
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions Lib/configparser.py
Expand Up @@ -152,6 +152,7 @@
"NoOptionError", "InterpolationError", "InterpolationDepthError",
"InterpolationMissingOptionError", "InterpolationSyntaxError",
"ParsingError", "MissingSectionHeaderError",
"MultilineContinuationError",
"ConfigParser", "RawConfigParser",
"Interpolation", "BasicInterpolation", "ExtendedInterpolation",
"SectionProxy", "ConverterMapping",
Expand Down Expand Up @@ -322,6 +323,19 @@ def __init__(self, filename, lineno, line):
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.
Expand Down Expand Up @@ -987,6 +1001,8 @@ def _read(self, fp, fpname):
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:
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_configparser.py
Expand Up @@ -1555,6 +1555,30 @@ def test_source_as_bytes(self):
"'[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."""
Expand Down
@@ -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).

0 comments on commit e800265

Please sign in to comment.