configparser: fixed inconsistency where in SafeConfigParser option values
 were ensured to be strings but section names and option keys were not.
 Behaviour unchanged for RawConfigParser and ConfigParser.
diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst
index 6822176..154d062 100644
--- a/Doc/library/configparser.rst
+++ b/Doc/library/configparser.rst
@@ -836,7 +836,11 @@
 
       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.
+      *default section* name is passed, :exc:`ValueError` is raised.  The name
+      of the section must be a string; if not, :exc:`TypeError` is raised.
+
+      .. versionchanged:: 3.2
+         Non-string section names raise :exc:`TypeError`.
 
 
    .. method:: has_section(section)
@@ -976,8 +980,8 @@
    .. method:: set(section, option, value)
 
       If the given section exists, set the given option to the specified value;
-      otherwise raise :exc:`NoSectionError`.  *value* must be a string; if not,
-      :exc:`TypeError` is raised.
+      otherwise raise :exc:`NoSectionError`.  *option* and *value* must be
+      strings; if not, :exc:`TypeError` is raised.
 
 
    .. method:: write(fileobject, space_around_delimiters=True)
@@ -1044,7 +1048,7 @@
 .. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True, default_section=configaparser.DEFAULTSECT, interpolation=None)
 
    Legacy variant of the :class:`SafeConfigParser` with interpolation disabled
-   by default and an unsafe ``set`` method.
+   by default and unsafe ``add_section`` and ``set`` methods.
 
    .. note::
       Consider using :class:`SafeConfigParser` instead which checks types of
@@ -1052,6 +1056,16 @@
       can use ``SafeConfigParser(interpolation=None)``.
 
 
+   .. 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.
+
+      Type of *section* is not checked which lets users create non-string named
+      sections. This behaviour is unsupported and may cause internal errors.
+
+
    .. method:: set(section, option, value)
 
       If the given section exists, set the given option to the specified value;
diff --git a/Lib/configparser.py b/Lib/configparser.py
index eafcea3..e92d7fa 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -727,11 +727,15 @@
         that should be present in the section. If the used dictionary type
         preserves order, sections and their keys will be added in order.
 
+        All types held in the dictionary are converted to strings during
+        reading, including section names, option names and keys.
+
         Optional second argument is the `source' specifying the name of the
         dictionary being read.
         """
         elements_added = set()
         for section, keys in dictionary.items():
+            section = str(section)
             try:
                 self.add_section(section)
             except (DuplicateSectionError, ValueError):
@@ -739,7 +743,7 @@
                     raise
                 elements_added.add(section)
             for key, value in keys.items():
-                key = self.optionxform(key)
+                key = self.optionxform(str(key))
                 if value is not None:
                     value = str(value)
                 if self._strict and (section, key) in elements_added:
@@ -1128,7 +1132,7 @@
             raise ValueError('Not a boolean: %s' % value)
         return self.BOOLEAN_STATES[value.lower()]
 
-    def _validate_value_type(self, value):
+    def _validate_value_types(self, *, section="", option="", value=""):
         """Raises a TypeError for non-string values.
 
         The only legal non-string value if we allow valueless
@@ -1141,6 +1145,10 @@
         for RawConfigParsers and ConfigParsers. It is invoked in every
         case for mapping protocol access and in SafeConfigParser.set().
         """
+        if not isinstance(section, str):
+            raise TypeError("section names must be strings")
+        if not isinstance(option, str):
+            raise TypeError("option keys must be strings")
         if not self._allow_no_value or value:
             if not isinstance(value, str):
                 raise TypeError("option values must be strings")
@@ -1169,9 +1177,16 @@
     def set(self, section, option, value=None):
         """Set an option.  Extends RawConfigParser.set by validating type and
         interpolation syntax on the value."""
-        self._validate_value_type(value)
+        self._validate_value_types(option=option, value=value)
         super().set(section, option, value)
 
+    def add_section(self, section):
+        """Create a new section in the configuration.  Extends
+        RawConfigParser.add_section by validating if the section name is
+        a string."""
+        self._validate_value_types(section=section)
+        super().add_section(section)
+
 
 class SectionProxy(MutableMapping):
     """A proxy for a single section from a parser."""
@@ -1196,7 +1211,7 @@
         return self._parser.get(self._name, key)
 
     def __setitem__(self, key, value):
-        self._parser._validate_value_type(value)
+        self._parser._validate_value_types(option=key, value=value)
         return self._parser.set(self._name, key, value)
 
     def __delitem__(self, key):
diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py
index 1ae720e..24158ad 100644
--- a/Lib/test/test_cfgparser.py
+++ b/Lib/test/test_cfgparser.py
@@ -106,6 +106,7 @@
         self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
         eq(cf.get('Types', 'float'), "0.44")
         eq(cf.getboolean('Types', 'boolean'), False)
+        eq(cf.get('Types', '123'), 'strange but acceptable')
         if self.allow_no_value:
             eq(cf.get('NoValue', 'option-without-value'), None)
 
@@ -214,6 +215,7 @@
 int {0[1]} 42
 float {0[0]} 0.44
 boolean {0[0]} NO
+123 {0[1]} strange but acceptable
 """.format(self.delimiters, self.comment_prefixes)
         if self.allow_no_value:
             config_string += (
@@ -286,6 +288,7 @@
                 "int": 42,
                 "float": 0.44,
                 "boolean": False,
+                123: "strange but acceptable",
             },
         }
         if self.allow_no_value:
@@ -716,6 +719,15 @@
                                 raw=True), '%(list)s')
         self.assertRaises(ValueError, cf.get, 'non-string',
                           'string_with_interpolation', raw=False)
+        cf.add_section(123)
+        cf.set(123, 'this is sick', True)
+        self.assertEqual(cf.get(123, 'this is sick', raw=True), True)
+        with self.assertRaises(TypeError):
+            cf.get(123, 'this is sick')
+        cf.optionxform = lambda x: x
+        cf.set('non-string', 1, 1)
+        self.assertRaises(TypeError, cf.get, 'non-string', 1, 1)
+        self.assertEqual(cf.get('non-string', 1, raw=True), 1)
 
 class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
     delimiters = (':=', '$')
@@ -783,6 +795,15 @@
         self.assertEqual(cf.get('non-string', 'list'),
                          [0, 1, 1, 2, 3, 5, 8, 13])
         self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
+        cf.add_section(123)
+        cf.set(123, 'this is sick', True)
+        self.assertEqual(cf.get(123, 'this is sick'), True)
+        if cf._dict.__class__ is configparser._default_dict:
+            # would not work for SortedDict; only checking for the most common
+            # default dictionary (OrderedDict)
+            cf.optionxform = lambda x: x
+            cf.set('non-string', 1, 1)
+            self.assertEqual(cf.get('non-string', 1), 1)
 
 class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
     delimiters = (':=', '$')
@@ -848,6 +869,8 @@
         self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
         self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
         self.assertRaises(TypeError, cf.set, "sect", "option2", object())
+        self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
+        self.assertRaises(TypeError, cf.add_section, 123)
 
     def test_add_section_default(self):
         cf = self.newconfig()