.. method:: add_section(section)
- Add a section named *section* to the instance. If a section by the given
- name already exists, :exc:`DuplicateSectionError` is raised. If the
- *default section* name is passed, :exc:`ValueError` is raised.
+ Add a section named *section* or :const:`UNNAMED_SECTION` to the instance.
+
+ If the given section already exists, :exc:`DuplicateSectionError` is
+ raised. If the *default section* name is passed, :exc:`ValueError` is
+ raised. If :const:`UNNAMED_SECTION` is passed and support is disabled,
+ :exc:`UnnamedSectionDisabledError` is raised.
Type of *section* is not checked which lets users create non-string named
sections. This behaviour is unsupported and may cause internal errors.
+ .. versionchanged:: 3.14
+ Added support for :const:`UNNAMED_SECTION`.
+
.. method:: set(section, option, value)
Exception raised when attempting to parse a file which has no section
headers.
-
.. exception:: ParsingError
Exception raised when errors occur attempting to parse a file.
.. versionadded:: 3.13
+.. exception:: UnnamedSectionDisabledError
+
+ Exception raised when attempting to use the
+ :const:`UNNAMED_SECTION` without enabling it.
+
+ .. versionadded:: 3.14
+
.. rubric:: Footnotes
.. [1] Config parsers allow for heavy customization. If you are interested in
"NoOptionError", "InterpolationError", "InterpolationDepthError",
"InterpolationMissingOptionError", "InterpolationSyntaxError",
"ParsingError", "MissingSectionHeaderError",
- "MultilineContinuationError",
+ "MultilineContinuationError", "UnnamedSectionDisabledError",
"ConfigParser", "RawConfigParser",
"Interpolation", "BasicInterpolation", "ExtendedInterpolation",
"SectionProxy", "ConverterMapping",
self.line = line
self.args = (filename, lineno, line)
+
+class UnnamedSectionDisabledError(Error):
+ """Raised when an attempt to use UNNAMED_SECTION is made with the
+ feature disabled."""
+ def __init__(self):
+ Error.__init__(self, "Support for UNNAMED_SECTION is disabled.")
+
+
class _UnnamedSection:
def __repr__(self):
if section == self.default_section:
raise ValueError('Invalid section name: %r' % section)
+ if section is UNNAMED_SECTION:
+ if not self._allow_unnamed_section:
+ raise UnnamedSectionDisabledError
+
if section in self._sections:
raise DuplicateSectionError(section)
self._sections[section] = self._dict()
return self.BOOLEAN_STATES[value.lower()]
def _validate_value_types(self, *, section="", option="", value=""):
- """Raises a TypeError for non-string values.
+ """Raises a TypeError for illegal non-string values.
- The only legal non-string value if we allow valueless
- options is None, so we need to check if the value is a
- string if:
- - we do not allow valueless options, or
- - we allow valueless options but the value is not None
+ Legal non-string values are UNNAMED_SECTION and falsey values if
+ they are allowed.
For compatibility reasons this method is not used in classic set()
for RawConfigParsers. It is invoked in every case for mapping protocol
access and in ConfigParser.set().
"""
- if not isinstance(section, str):
- raise TypeError("section names must be strings")
+ if section is UNNAMED_SECTION:
+ if not self._allow_unnamed_section:
+ raise UnnamedSectionDisabledError
+ elif not isinstance(section, str):
+ raise TypeError("section names must be strings or UNNAMED_SECTION")
if not isinstance(option, str):
raise TypeError("option keys must be strings")
if not self._allow_no_value or value:
self.assertEqual('1', cfg2[configparser.UNNAMED_SECTION]['a'])
self.assertEqual('2', cfg2[configparser.UNNAMED_SECTION]['b'])
+ def test_add_section(self):
+ cfg = configparser.ConfigParser(allow_unnamed_section=True)
+ cfg.add_section(configparser.UNNAMED_SECTION)
+ cfg.set(configparser.UNNAMED_SECTION, 'a', '1')
+ self.assertEqual('1', cfg[configparser.UNNAMED_SECTION]['a'])
+
+ def test_disabled_error(self):
+ with self.assertRaises(configparser.MissingSectionHeaderError):
+ configparser.ConfigParser().read_string("a = 1")
+
+ with self.assertRaises(configparser.UnnamedSectionDisabledError):
+ configparser.ConfigParser().add_section(configparser.UNNAMED_SECTION)
+
class MiscTestCase(unittest.TestCase):
def test__all__(self):