summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorŁukasz Langa <lukasz@langa.pl>2010-12-04 12:46:01 (GMT)
committerŁukasz Langa <lukasz@langa.pl>2010-12-04 12:46:01 (GMT)
commit2cf9ddb390564046242d7b0c5667c62843e74714 (patch)
treeda6760e3fc78c4c8ffd0ca914c173359e8793c5d
parentd2a9b20efaa15b0152856a545b0206af21d06c5e (diff)
downloadcpython-2cf9ddb390564046242d7b0c5667c62843e74714.zip
cpython-2cf9ddb390564046242d7b0c5667c62843e74714.tar.gz
cpython-2cf9ddb390564046242d7b0c5667c62843e74714.tar.bz2
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.
-rw-r--r--Doc/library/configparser.rst22
-rw-r--r--Lib/configparser.py23
-rw-r--r--Lib/test/test_cfgparser.py23
3 files changed, 60 insertions, 8 deletions
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 @@ SafeConfigParser Objects
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 @@ SafeConfigParser Objects
.. 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 @@ RawConfigParser Objects
.. 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 @@ RawConfigParser Objects
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 @@ class RawConfigParser(MutableMapping):
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 @@ class RawConfigParser(MutableMapping):
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 @@ class RawConfigParser(MutableMapping):
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 @@ class RawConfigParser(MutableMapping):
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 @@ class SafeConfigParser(ConfigParser):
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 @@ class SectionProxy(MutableMapping):
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 @@ class BasicTestCase(CfgParserTestCaseClass):
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 @@ another with spaces {0[0]} splat!
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 @@ boolean {0[0]} NO
"int": 42,
"float": 0.44,
"boolean": False,
+ 123: "strange but acceptable",
},
}
if self.allow_no_value:
@@ -716,6 +719,15 @@ class ConfigParserTestCase(BasicTestCase):
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 @@ class RawConfigParserTestCase(BasicTestCase):
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 @@ class SafeConfigParserTestCase(ConfigParserTestCase):
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()