]> granicus.if.org Git - python/commitdiff
bpo-23835: Restore legacy defaults= behavior for RawConfigParser (#3191)
authorŁukasz Langa <lukasz@langa.pl>
Thu, 24 Aug 2017 16:43:53 +0000 (09:43 -0700)
committerGitHub <noreply@github.com>
Thu, 24 Aug 2017 16:43:53 +0000 (09:43 -0700)
The fix for bpo-23835 fixed ConfigParser behavior in defaults= handling.
Unfortunately, it caused a backwards compatibility regression with
RawConfigParser objects which allow for non-string values.

This commit restores the legacy behavior for RawConfigParser only.

Doc/library/configparser.rst
Lib/configparser.py
Lib/test/test_configparser.py

index c31a7e94651898d10975146f33af1e41b93dfeb2..acca64701110b26cfcc9b61672d73238f61b5041 100644 (file)
@@ -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
index 360924008dcb7d619a6834e77ba12d732aa922de..e172ac8b36f990dab3c3a0965178fc2cbe895b05 100644 (file)
@@ -635,7 +635,7 @@ class RawConfigParser(MutableMapping):
         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
@@ -1121,6 +1121,12 @@ class RawConfigParser(MutableMapping):
                                                                 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)
@@ -1198,6 +1204,11 @@ class ConfigParser(RawConfigParser):
         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."""
index be22fa4031075e939cae68b55ae932728fcc9f9a..d8969efc4db937d88cfb933f0be7960f047009a8 100644 (file)
@@ -855,15 +855,6 @@ boolean {0[0]} NO
         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
@@ -959,6 +950,15 @@ class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
         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
@@ -1099,6 +1099,15 @@ class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
             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 = (':=', '$')