From c264c098d03fb48e42ca58cd9888f6e35fba7d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 20 Nov 2010 16:15:37 +0000 Subject: [PATCH] configparser: the name of the DEFAULT section is now customizable --- Lib/configparser.py | 36 +++++++++++++++++++----------------- Lib/test/test_cfgparser.py | 37 +++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/Lib/configparser.py b/Lib/configparser.py index 9cbff704929..03f98a6fe2c 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -406,12 +406,13 @@ class RawConfigParser(MutableMapping): def __init__(self, defaults=None, dict_type=_default_dict, allow_no_value=False, *, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, - empty_lines_in_values=True): + empty_lines_in_values=True, + default_section=DEFAULTSECT): self._dict = dict_type self._sections = self._dict() self._defaults = self._dict() self._proxies = self._dict() - self._proxies[DEFAULTSECT] = SectionProxy(self, DEFAULTSECT) + self._proxies[default_section] = SectionProxy(self, default_section) if defaults: for key, value in defaults.items(): self._defaults[self.optionxform(key)] = value @@ -435,6 +436,7 @@ class RawConfigParser(MutableMapping): self._strict = strict self._allow_no_value = allow_no_value self._empty_lines_in_values = empty_lines_in_values + self._default_section=default_section def defaults(self): return self._defaults @@ -448,10 +450,9 @@ class RawConfigParser(MutableMapping): """Create a new section in the configuration. Raise DuplicateSectionError if a section by the specified name - already exists. Raise ValueError if name is DEFAULT or any of it's - case-insensitive variants. + already exists. Raise ValueError if name is DEFAULT. """ - if section.upper() == DEFAULTSECT: + if section == self._default_section: raise ValueError('Invalid section name: %s' % section) if section in self._sections: @@ -587,7 +588,7 @@ class RawConfigParser(MutableMapping): try: d2 = self._sections[section] except KeyError: - if section != DEFAULTSECT: + if section != self._default_section: raise NoSectionError(section) d2 = self._dict() d = self._defaults.copy() @@ -632,7 +633,7 @@ class RawConfigParser(MutableMapping): def has_option(self, section, option): """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) return option in self._defaults elif section not in self._sections: @@ -644,7 +645,7 @@ class RawConfigParser(MutableMapping): def set(self, section, option, value=None): """Set an option.""" - if not section or section == DEFAULTSECT: + if not section or section == self._default_section: sectdict = self._defaults else: try: @@ -664,7 +665,8 @@ class RawConfigParser(MutableMapping): else: d = self._delimiters[0] 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: self._write_section(fp, section, self._sections[section].items(), d) @@ -684,7 +686,7 @@ class RawConfigParser(MutableMapping): def remove_option(self, section, option): """Remove an option.""" - if not section or section == DEFAULTSECT: + if not section or section == self._default_section: sectdict = self._defaults else: try: @@ -706,7 +708,7 @@ class RawConfigParser(MutableMapping): return existed 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) return self._proxies[key] @@ -720,21 +722,21 @@ class RawConfigParser(MutableMapping): self.read_dict({key: value}) def __delitem__(self, key): - if key == DEFAULTSECT: + if key == self._default_section: raise ValueError("Cannot remove the default section.") if not self.has_section(key): raise KeyError(key) self.remove_section(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): return len(self._sections) + 1 # the default section def __iter__(self): # 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): """Parse a sectioned configuration file. @@ -806,7 +808,7 @@ class RawConfigParser(MutableMapping): lineno) cursect = self._sections[sectname] elements_added.add(sectname) - elif sectname == DEFAULTSECT: + elif sectname == self._default_section: cursect = self._defaults else: cursect = self._dict() @@ -877,7 +879,7 @@ class RawConfigParser(MutableMapping): try: d.update(self._sections[section]) except KeyError: - if section != DEFAULTSECT: + if section != self._default_section: raise NoSectionError(section) # Update with the entry specific variables if vars: @@ -999,7 +1001,7 @@ class ConfigParser(RawConfigParser): try: d.update(self._sections[section]) except KeyError: - if section != DEFAULTSECT: + if section != self._default_section: raise NoSectionError(section) # Update with the entry specific variables if vars: diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py index 11aa2674e6f..efa12339123 100644 --- a/Lib/test/test_cfgparser.py +++ b/Lib/test/test_cfgparser.py @@ -31,6 +31,7 @@ class CfgParserTestCaseClass(unittest.TestCase): empty_lines_in_values = True dict_type = configparser._default_dict strict = False + default_section = configparser.DEFAULTSECT def newconfig(self, defaults=None): arguments = dict( @@ -41,6 +42,7 @@ class CfgParserTestCaseClass(unittest.TestCase): empty_lines_in_values=self.empty_lines_in_values, dict_type=self.dict_type, strict=self.strict, + default_section=self.default_section, ) return self.config_class(**arguments) @@ -76,7 +78,7 @@ class BasicTestCase(CfgParserTestCaseClass): # mapping access L = [section for section in cf] L.sort() - E.append(configparser.DEFAULTSECT) + E.append(self.default_section) E.sort() eq(L, E) @@ -365,7 +367,7 @@ boolean {0[0]} NO L.sort() eq = self.assertEqual 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"]["b"], "value", "could not locate option, expecting case-insensitive option names") @@ -399,11 +401,11 @@ boolean {0[0]} NO def test_default_case_sensitivity(self): cf = self.newconfig({"foo": "Bar"}) self.assertEqual( - cf.get("DEFAULT", "Foo"), "Bar", + cf.get(self.default_section, "Foo"), "Bar", "could not locate option, expecting case-insensitive option names") cf = self.newconfig({"Foo": "Bar"}) self.assertEqual( - cf.get("DEFAULT", "Foo"), "Bar", + cf.get(self.default_section, "Foo"), "Bar", "could not locate option, expecting case-insensitive defaults") def test_parse_errors(self): @@ -530,7 +532,7 @@ boolean {0[0]} NO "[Long Line]\n" "foo{0[0]} this line is much, much longer than my editor\n" " likes it.\n" - "[DEFAULT]\n" + "[{default_section}]\n" "foo{0[1]} another very\n" " long line\n" "[Long Line - With Comments!]\n" @@ -538,7 +540,8 @@ boolean {0[0]} NO " also {comment} place\n" " comments {comment} in\n" " 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: config_string += ( @@ -550,7 +553,7 @@ boolean {0[0]} NO output = io.StringIO() cf.write(output) expect_string = ( - "[DEFAULT]\n" + "[{default_section}]\n" "foo {equals} another very\n" "\tlong line\n" "\n" @@ -563,7 +566,8 @@ boolean {0[0]} NO "\talso\n" "\tcomments\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: expect_string += ( @@ -724,6 +728,9 @@ class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase): delimiters = (':=', '$') comment_prefixes = ('//', '"') +class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase): + default_section = 'general' + class MultilineValuesTestCase(BasicTestCase): config_class = configparser.ConfigParser wonderful_spam = ("I'm having spam spam spam spam " @@ -851,13 +858,9 @@ class SafeConfigParserTestCase(ConfigParserTestCase): self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0) 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() - self.assertRaises(ValueError, cf.add_section, "DEFAULT") + self.assertRaises(ValueError, cf.add_section, self.default_section) class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase): delimiters = (':=', '$') @@ -884,11 +887,12 @@ class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass): 'no values here', 'tricky interpolation', 'more interpolation']) - self.assertEqual(cf.getint('DEFAULT', 'go', + self.assertEqual(cf.getint(self.default_section, 'go', vars={'interpolate': '-1'}), -1) with self.assertRaises(ValueError): # 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('corruption', 'value').split('\n')), 10) longname = 'yeah, sections can be indented as well' @@ -997,6 +1001,7 @@ def test_main(): Issue7005TestCase, StrictTestCase, CompatibleTestCase, + ConfigParserTestCaseNonStandardDefaultSection, )