Commit c264c098 authored by Łukasz Langa's avatar Łukasz Langa

configparser: the name of the DEFAULT section is now customizable

parent b357fb7b
...@@ -406,12 +406,13 @@ class RawConfigParser(MutableMapping): ...@@ -406,12 +406,13 @@ class RawConfigParser(MutableMapping):
def __init__(self, defaults=None, dict_type=_default_dict, def __init__(self, defaults=None, dict_type=_default_dict,
allow_no_value=False, *, delimiters=('=', ':'), allow_no_value=False, *, delimiters=('=', ':'),
comment_prefixes=_COMPATIBLE, strict=False, comment_prefixes=_COMPATIBLE, strict=False,
empty_lines_in_values=True): empty_lines_in_values=True,
default_section=DEFAULTSECT):
self._dict = dict_type self._dict = dict_type
self._sections = self._dict() self._sections = self._dict()
self._defaults = self._dict() self._defaults = self._dict()
self._proxies = self._dict() self._proxies = self._dict()
self._proxies[DEFAULTSECT] = SectionProxy(self, DEFAULTSECT) self._proxies[default_section] = SectionProxy(self, default_section)
if defaults: if defaults:
for key, value in defaults.items(): for key, value in defaults.items():
self._defaults[self.optionxform(key)] = value self._defaults[self.optionxform(key)] = value
...@@ -435,6 +436,7 @@ class RawConfigParser(MutableMapping): ...@@ -435,6 +436,7 @@ class RawConfigParser(MutableMapping):
self._strict = strict self._strict = strict
self._allow_no_value = allow_no_value self._allow_no_value = allow_no_value
self._empty_lines_in_values = empty_lines_in_values self._empty_lines_in_values = empty_lines_in_values
self._default_section=default_section
def defaults(self): def defaults(self):
return self._defaults return self._defaults
...@@ -448,10 +450,9 @@ class RawConfigParser(MutableMapping): ...@@ -448,10 +450,9 @@ class RawConfigParser(MutableMapping):
"""Create a new section in the configuration. """Create a new section in the configuration.
Raise DuplicateSectionError if a section by the specified name Raise DuplicateSectionError if a section by the specified name
already exists. Raise ValueError if name is DEFAULT or any of it's already exists. Raise ValueError if name is DEFAULT.
case-insensitive variants.
""" """
if section.upper() == DEFAULTSECT: if section == self._default_section:
raise ValueError('Invalid section name: %s' % section) raise ValueError('Invalid section name: %s' % section)
if section in self._sections: if section in self._sections:
...@@ -587,7 +588,7 @@ class RawConfigParser(MutableMapping): ...@@ -587,7 +588,7 @@ class RawConfigParser(MutableMapping):
try: try:
d2 = self._sections[section] d2 = self._sections[section]
except KeyError: except KeyError:
if section != DEFAULTSECT: if section != self._default_section:
raise NoSectionError(section) raise NoSectionError(section)
d2 = self._dict() d2 = self._dict()
d = self._defaults.copy() d = self._defaults.copy()
...@@ -632,7 +633,7 @@ class RawConfigParser(MutableMapping): ...@@ -632,7 +633,7 @@ class RawConfigParser(MutableMapping):
def has_option(self, section, option): def has_option(self, section, option):
"""Check for the existence of a given option in a given section.""" """Check for the existence of a given option in a given section."""
if not section or section == DEFAULTSECT: if not section or section == self._default_section:
option = self.optionxform(option) option = self.optionxform(option)
return option in self._defaults return option in self._defaults
elif section not in self._sections: elif section not in self._sections:
...@@ -644,7 +645,7 @@ class RawConfigParser(MutableMapping): ...@@ -644,7 +645,7 @@ class RawConfigParser(MutableMapping):
def set(self, section, option, value=None): def set(self, section, option, value=None):
"""Set an option.""" """Set an option."""
if not section or section == DEFAULTSECT: if not section or section == self._default_section:
sectdict = self._defaults sectdict = self._defaults
else: else:
try: try:
...@@ -664,7 +665,8 @@ class RawConfigParser(MutableMapping): ...@@ -664,7 +665,8 @@ class RawConfigParser(MutableMapping):
else: else:
d = self._delimiters[0] d = self._delimiters[0]
if self._defaults: if self._defaults:
self._write_section(fp, DEFAULTSECT, self._defaults.items(), d) self._write_section(fp, self._default_section,
self._defaults.items(), d)
for section in self._sections: for section in self._sections:
self._write_section(fp, section, self._write_section(fp, section,
self._sections[section].items(), d) self._sections[section].items(), d)
...@@ -684,7 +686,7 @@ class RawConfigParser(MutableMapping): ...@@ -684,7 +686,7 @@ class RawConfigParser(MutableMapping):
def remove_option(self, section, option): def remove_option(self, section, option):
"""Remove an option.""" """Remove an option."""
if not section or section == DEFAULTSECT: if not section or section == self._default_section:
sectdict = self._defaults sectdict = self._defaults
else: else:
try: try:
...@@ -706,7 +708,7 @@ class RawConfigParser(MutableMapping): ...@@ -706,7 +708,7 @@ class RawConfigParser(MutableMapping):
return existed return existed
def __getitem__(self, key): def __getitem__(self, key):
if key != DEFAULTSECT and not self.has_section(key): if key != self._default_section and not self.has_section(key):
raise KeyError(key) raise KeyError(key)
return self._proxies[key] return self._proxies[key]
...@@ -720,21 +722,21 @@ class RawConfigParser(MutableMapping): ...@@ -720,21 +722,21 @@ class RawConfigParser(MutableMapping):
self.read_dict({key: value}) self.read_dict({key: value})
def __delitem__(self, key): def __delitem__(self, key):
if key == DEFAULTSECT: if key == self._default_section:
raise ValueError("Cannot remove the default section.") raise ValueError("Cannot remove the default section.")
if not self.has_section(key): if not self.has_section(key):
raise KeyError(key) raise KeyError(key)
self.remove_section(key) self.remove_section(key)
def __contains__(self, key): def __contains__(self, key):
return key == DEFAULTSECT or self.has_section(key) return key == self._default_section or self.has_section(key)
def __len__(self): def __len__(self):
return len(self._sections) + 1 # the default section return len(self._sections) + 1 # the default section
def __iter__(self): def __iter__(self):
# XXX does it break when underlying container state changed? # XXX does it break when underlying container state changed?
return itertools.chain((DEFAULTSECT,), self._sections.keys()) return itertools.chain((self._default_section,), self._sections.keys())
def _read(self, fp, fpname): def _read(self, fp, fpname):
"""Parse a sectioned configuration file. """Parse a sectioned configuration file.
...@@ -806,7 +808,7 @@ class RawConfigParser(MutableMapping): ...@@ -806,7 +808,7 @@ class RawConfigParser(MutableMapping):
lineno) lineno)
cursect = self._sections[sectname] cursect = self._sections[sectname]
elements_added.add(sectname) elements_added.add(sectname)
elif sectname == DEFAULTSECT: elif sectname == self._default_section:
cursect = self._defaults cursect = self._defaults
else: else:
cursect = self._dict() cursect = self._dict()
...@@ -877,7 +879,7 @@ class RawConfigParser(MutableMapping): ...@@ -877,7 +879,7 @@ class RawConfigParser(MutableMapping):
try: try:
d.update(self._sections[section]) d.update(self._sections[section])
except KeyError: except KeyError:
if section != DEFAULTSECT: if section != self._default_section:
raise NoSectionError(section) raise NoSectionError(section)
# Update with the entry specific variables # Update with the entry specific variables
if vars: if vars:
...@@ -999,7 +1001,7 @@ class ConfigParser(RawConfigParser): ...@@ -999,7 +1001,7 @@ class ConfigParser(RawConfigParser):
try: try:
d.update(self._sections[section]) d.update(self._sections[section])
except KeyError: except KeyError:
if section != DEFAULTSECT: if section != self._default_section:
raise NoSectionError(section) raise NoSectionError(section)
# Update with the entry specific variables # Update with the entry specific variables
if vars: if vars:
......
...@@ -31,6 +31,7 @@ class CfgParserTestCaseClass(unittest.TestCase): ...@@ -31,6 +31,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
empty_lines_in_values = True empty_lines_in_values = True
dict_type = configparser._default_dict dict_type = configparser._default_dict
strict = False strict = False
default_section = configparser.DEFAULTSECT
def newconfig(self, defaults=None): def newconfig(self, defaults=None):
arguments = dict( arguments = dict(
...@@ -41,6 +42,7 @@ class CfgParserTestCaseClass(unittest.TestCase): ...@@ -41,6 +42,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
empty_lines_in_values=self.empty_lines_in_values, empty_lines_in_values=self.empty_lines_in_values,
dict_type=self.dict_type, dict_type=self.dict_type,
strict=self.strict, strict=self.strict,
default_section=self.default_section,
) )
return self.config_class(**arguments) return self.config_class(**arguments)
...@@ -76,7 +78,7 @@ class BasicTestCase(CfgParserTestCaseClass): ...@@ -76,7 +78,7 @@ class BasicTestCase(CfgParserTestCaseClass):
# mapping access # mapping access
L = [section for section in cf] L = [section for section in cf]
L.sort() L.sort()
E.append(configparser.DEFAULTSECT) E.append(self.default_section)
E.sort() E.sort()
eq(L, E) eq(L, E)
...@@ -365,7 +367,7 @@ boolean {0[0]} NO ...@@ -365,7 +367,7 @@ boolean {0[0]} NO
L.sort() L.sort()
eq = self.assertEqual eq = self.assertEqual
elem_eq = self.assertItemsEqual elem_eq = self.assertItemsEqual
eq(L, ["A", "B", configparser.DEFAULTSECT, "a"]) eq(L, sorted(["A", "B", self.default_section, "a"]))
eq(cf["a"].keys(), {"b"}) eq(cf["a"].keys(), {"b"})
eq(cf["a"]["b"], "value", eq(cf["a"]["b"], "value",
"could not locate option, expecting case-insensitive option names") "could not locate option, expecting case-insensitive option names")
...@@ -399,11 +401,11 @@ boolean {0[0]} NO ...@@ -399,11 +401,11 @@ boolean {0[0]} NO
def test_default_case_sensitivity(self): def test_default_case_sensitivity(self):
cf = self.newconfig({"foo": "Bar"}) cf = self.newconfig({"foo": "Bar"})
self.assertEqual( self.assertEqual(
cf.get("DEFAULT", "Foo"), "Bar", cf.get(self.default_section, "Foo"), "Bar",
"could not locate option, expecting case-insensitive option names") "could not locate option, expecting case-insensitive option names")
cf = self.newconfig({"Foo": "Bar"}) cf = self.newconfig({"Foo": "Bar"})
self.assertEqual( self.assertEqual(
cf.get("DEFAULT", "Foo"), "Bar", cf.get(self.default_section, "Foo"), "Bar",
"could not locate option, expecting case-insensitive defaults") "could not locate option, expecting case-insensitive defaults")
def test_parse_errors(self): def test_parse_errors(self):
...@@ -530,7 +532,7 @@ boolean {0[0]} NO ...@@ -530,7 +532,7 @@ boolean {0[0]} NO
"[Long Line]\n" "[Long Line]\n"
"foo{0[0]} this line is much, much longer than my editor\n" "foo{0[0]} this line is much, much longer than my editor\n"
" likes it.\n" " likes it.\n"
"[DEFAULT]\n" "[{default_section}]\n"
"foo{0[1]} another very\n" "foo{0[1]} another very\n"
" long line\n" " long line\n"
"[Long Line - With Comments!]\n" "[Long Line - With Comments!]\n"
...@@ -538,7 +540,8 @@ boolean {0[0]} NO ...@@ -538,7 +540,8 @@ boolean {0[0]} NO
" also {comment} place\n" " also {comment} place\n"
" comments {comment} in\n" " comments {comment} in\n"
" multiline {comment} values" " multiline {comment} values"
"\n".format(self.delimiters, comment=self.comment_prefixes[0]) "\n".format(self.delimiters, comment=self.comment_prefixes[0],
default_section=self.default_section)
) )
if self.allow_no_value: if self.allow_no_value:
config_string += ( config_string += (
...@@ -550,7 +553,7 @@ boolean {0[0]} NO ...@@ -550,7 +553,7 @@ boolean {0[0]} NO
output = io.StringIO() output = io.StringIO()
cf.write(output) cf.write(output)
expect_string = ( expect_string = (
"[DEFAULT]\n" "[{default_section}]\n"
"foo {equals} another very\n" "foo {equals} another very\n"
"\tlong line\n" "\tlong line\n"
"\n" "\n"
...@@ -563,7 +566,8 @@ boolean {0[0]} NO ...@@ -563,7 +566,8 @@ boolean {0[0]} NO
"\talso\n" "\talso\n"
"\tcomments\n" "\tcomments\n"
"\tmultiline\n" "\tmultiline\n"
"\n".format(equals=self.delimiters[0]) "\n".format(equals=self.delimiters[0],
default_section=self.default_section)
) )
if self.allow_no_value: if self.allow_no_value:
expect_string += ( expect_string += (
...@@ -724,6 +728,9 @@ class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase): ...@@ -724,6 +728,9 @@ class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
delimiters = (':=', '$') delimiters = (':=', '$')
comment_prefixes = ('//', '"') comment_prefixes = ('//', '"')
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
default_section = 'general'
class MultilineValuesTestCase(BasicTestCase): class MultilineValuesTestCase(BasicTestCase):
config_class = configparser.ConfigParser config_class = configparser.ConfigParser
wonderful_spam = ("I'm having spam spam spam spam " wonderful_spam = ("I'm having spam spam spam spam "
...@@ -851,13 +858,9 @@ class SafeConfigParserTestCase(ConfigParserTestCase): ...@@ -851,13 +858,9 @@ class SafeConfigParserTestCase(ConfigParserTestCase):
self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0) self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
self.assertRaises(TypeError, cf.set, "sect", "option2", object()) self.assertRaises(TypeError, cf.set, "sect", "option2", object())
def test_add_section_default_1(self): def test_add_section_default(self):
cf = self.newconfig()
self.assertRaises(ValueError, cf.add_section, "default")
def test_add_section_default_2(self):
cf = self.newconfig() cf = self.newconfig()
self.assertRaises(ValueError, cf.add_section, "DEFAULT") self.assertRaises(ValueError, cf.add_section, self.default_section)
class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase): class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
delimiters = (':=', '$') delimiters = (':=', '$')
...@@ -884,11 +887,12 @@ class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass): ...@@ -884,11 +887,12 @@ class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
'no values here', 'no values here',
'tricky interpolation', 'tricky interpolation',
'more interpolation']) 'more interpolation'])
self.assertEqual(cf.getint('DEFAULT', 'go', self.assertEqual(cf.getint(self.default_section, 'go',
vars={'interpolate': '-1'}), -1) vars={'interpolate': '-1'}), -1)
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
# no interpolation will happen # no interpolation will happen
cf.getint('DEFAULT', 'go', raw=True, vars={'interpolate': '-1'}) cf.getint(self.default_section, 'go', raw=True,
vars={'interpolate': '-1'})
self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4) self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10) self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
longname = 'yeah, sections can be indented as well' longname = 'yeah, sections can be indented as well'
...@@ -997,6 +1001,7 @@ def test_main(): ...@@ -997,6 +1001,7 @@ def test_main():
Issue7005TestCase, Issue7005TestCase,
StrictTestCase, StrictTestCase,
CompatibleTestCase, CompatibleTestCase,
ConfigParserTestCaseNonStandardDefaultSection,
) )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment