Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Doc/library/configparser.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1213,8 +1213,10 @@ RawConfigParser Objects
default_section=configparser.DEFAULTSECT[, \
interpolation])

Legacy variant of the :class:`ConfigParser` with interpolation disabled
by default and unsafe ``add_section`` and ``set`` methods.
Legacy variant of the :class:`ConfigParser`. It has interpolation
disabled by default and allows for non-string section names, option
names, and values via its unsafe ``add_section`` and ``set`` methods,
as well as the legacy ``defaults=`` keyword argument handling.

.. note::
Consider using :class:`ConfigParser` instead which checks types of
Expand Down
13 changes: 12 additions & 1 deletion Lib/configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ def __init__(self, defaults=None, dict_type=_default_dict,
if converters is not _UNSET:
self._converters.update(converters)
if defaults:
self.read_dict({default_section: defaults})
self._read_defaults(defaults)

def defaults(self):
return self._defaults
Expand Down Expand Up @@ -1121,6 +1121,12 @@ def _join_multiline_values(self):
section,
name, val)

def _read_defaults(self, defaults):
"""Read the defaults passed in the initializer.
Note: values can be non-string."""
for key, value in defaults.items():
self._defaults[self.optionxform(key)] = value

def _handle_error(self, exc, fpname, lineno, line):
if not exc:
exc = ParsingError(fpname)
Expand Down Expand Up @@ -1198,6 +1204,11 @@ def add_section(self, section):
self._validate_value_types(section=section)
super().add_section(section)

def _read_defaults(self, defaults):
"""Reads the defaults passed in the initializer, implicitly converting
values to strings like the rest of the API."""
self.read_dict({self.default_section: defaults})


class SafeConfigParser(ConfigParser):
"""ConfigParser alias for backwards compatibility purposes."""
Expand Down
27 changes: 18 additions & 9 deletions Lib/test/test_configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,15 +855,6 @@ def test_invalid_multiline_value(self):
self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
self.assertEqual(cf['DEFAULT']['test'], 'test')

def test_defaults_keyword(self):
# test that bpo-23835 is fixed
cf = self.newconfig(defaults={1: 2.4})
self.assertEqual(cf[self.default_section]['1'], '2.4')
self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
cf = self.newconfig(defaults={"A": 5.2})
self.assertEqual(cf[self.default_section]['a'], '5.2')
self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)


class StrictTestCase(BasicTestCase, unittest.TestCase):
config_class = configparser.RawConfigParser
Expand Down Expand Up @@ -959,6 +950,15 @@ def test_add_section_default(self):
cf = self.newconfig()
self.assertRaises(ValueError, cf.add_section, self.default_section)

def test_defaults_keyword(self):
"""bpo-23835 fix for ConfigParser"""
cf = self.newconfig(defaults={1: 2.4})
self.assertEqual(cf[self.default_section]['1'], '2.4')
self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
cf = self.newconfig(defaults={"A": 5.2})
self.assertEqual(cf[self.default_section]['a'], '5.2')
self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)


class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
config_class = configparser.ConfigParser
Expand Down Expand Up @@ -1099,6 +1099,15 @@ def test_set_nonstring_types(self):
cf.set('non-string', 1, 1)
self.assertEqual(cf.get('non-string', 1), 1)

def test_defaults_keyword(self):
"""bpo-23835 legacy behavior for RawConfigParser"""
with self.assertRaises(AttributeError) as ctx:
self.newconfig(defaults={1: 2.4})
err = ctx.exception
self.assertEqual(str(err), "'int' object has no attribute 'lower'")
cf = self.newconfig(defaults={"A": 5.2})
self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)


class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
delimiters = (':=', '$')
Expand Down