mirror of https://github.com/python/cpython
Fix #7113. Patch by Łukasz Langa.
Changes include using a list of lines instead of patching together using string interpolation, and a multi-line value test cases.
This commit is contained in:
parent
53f94d0753
commit
e4334b4949
|
@ -398,12 +398,11 @@ class RawConfigParser:
|
|||
for section in self._sections:
|
||||
fp.write("[%s]\n" % section)
|
||||
for (key, value) in self._sections[section].items():
|
||||
if key != "__name__":
|
||||
if value is None:
|
||||
fp.write("%s\n" % (key))
|
||||
else:
|
||||
fp.write("%s = %s\n" %
|
||||
(key, str(value).replace('\n', '\n\t')))
|
||||
if key == "__name__":
|
||||
continue
|
||||
if value is not None:
|
||||
key = " = ".join((key, str(value).replace('\n', '\n\t')))
|
||||
fp.write("%s\n" % (key))
|
||||
fp.write("\n")
|
||||
|
||||
def remove_option(self, section, option):
|
||||
|
@ -464,10 +463,10 @@ class RawConfigParser:
|
|||
leading whitespace. Blank lines, lines beginning with a '#',
|
||||
and just about everything else are ignored.
|
||||
"""
|
||||
cursect = None # None, or a dictionary
|
||||
cursect = None # None, or a dictionary
|
||||
optname = None
|
||||
lineno = 0
|
||||
e = None # None, or an exception
|
||||
e = None # None, or an exception
|
||||
while True:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
|
@ -483,7 +482,7 @@ class RawConfigParser:
|
|||
if line[0].isspace() and cursect is not None and optname:
|
||||
value = line.strip()
|
||||
if value:
|
||||
cursect[optname] = "%s\n%s" % (cursect[optname], value)
|
||||
cursect[optname].append(value)
|
||||
# a section header or option header?
|
||||
else:
|
||||
# is it a section header?
|
||||
|
@ -508,6 +507,7 @@ class RawConfigParser:
|
|||
mo = self._optcre.match(line)
|
||||
if mo:
|
||||
optname, vi, optval = mo.group('option', 'vi', 'value')
|
||||
optname = self.optionxform(optname.rstrip())
|
||||
# This check is fine because the OPTCRE cannot
|
||||
# match if it would set optval to None
|
||||
if optval is not None:
|
||||
|
@ -518,11 +518,13 @@ class RawConfigParser:
|
|||
if pos != -1 and optval[pos-1].isspace():
|
||||
optval = optval[:pos]
|
||||
optval = optval.strip()
|
||||
# allow empty values
|
||||
if optval == '""':
|
||||
optval = ''
|
||||
optname = self.optionxform(optname.rstrip())
|
||||
cursect[optname] = optval
|
||||
# allow empty values
|
||||
if optval == '""':
|
||||
optval = ''
|
||||
cursect[optname] = [optval]
|
||||
else:
|
||||
# valueless option handling
|
||||
cursect[optname] = optval
|
||||
else:
|
||||
# a non-fatal parsing error occurred. set up the
|
||||
# exception but keep going. the exception will be
|
||||
|
@ -535,6 +537,13 @@ class RawConfigParser:
|
|||
if e:
|
||||
raise e
|
||||
|
||||
# join the multi-line values collected while reading
|
||||
all_sections = [self._defaults]
|
||||
all_sections.extend(self._sections.values())
|
||||
for options in all_sections:
|
||||
for name, val in options.items():
|
||||
if isinstance(val, list):
|
||||
options[name] = '\n'.join(val)
|
||||
|
||||
class ConfigParser(RawConfigParser):
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ConfigParser
|
||||
import StringIO
|
||||
import os
|
||||
import unittest
|
||||
import UserDict
|
||||
|
||||
|
@ -186,7 +187,8 @@ class TestCaseBase(unittest.TestCase):
|
|||
self.assertEqual(cf.sections(), [],
|
||||
"new ConfigParser should have no defined sections")
|
||||
self.assertFalse(cf.has_section("Foo"),
|
||||
"new ConfigParser should have no acknowledged sections")
|
||||
"new ConfigParser should have no acknowledged "
|
||||
"sections")
|
||||
self.assertRaises(ConfigParser.NoSectionError,
|
||||
cf.options, "Foo")
|
||||
self.assertRaises(ConfigParser.NoSectionError,
|
||||
|
@ -357,6 +359,11 @@ class ConfigParserTestCase(TestCaseBase):
|
|||
config_class = ConfigParser.ConfigParser
|
||||
|
||||
def test_interpolation(self):
|
||||
rawval = {
|
||||
ConfigParser.ConfigParser: ("something %(with11)s "
|
||||
"lots of interpolation (11 steps)"),
|
||||
ConfigParser.SafeConfigParser: "%(with1)s",
|
||||
}
|
||||
cf = self.get_interpolation_config()
|
||||
eq = self.assertEqual
|
||||
eq(cf.get("Foo", "getname"), "Foo")
|
||||
|
@ -403,6 +410,33 @@ class ConfigParserTestCase(TestCaseBase):
|
|||
self.assertRaises(ValueError, cf.get, 'non-string',
|
||||
'string_with_interpolation', raw=False)
|
||||
|
||||
class MultilineValuesTestCase(TestCaseBase):
|
||||
config_class = ConfigParser.ConfigParser
|
||||
wonderful_spam = ("I'm having spam spam spam spam "
|
||||
"spam spam spam beaked beans spam "
|
||||
"spam spam and spam!").replace(' ', '\t\n')
|
||||
|
||||
def setUp(self):
|
||||
cf = self.newconfig()
|
||||
for i in range(100):
|
||||
s = 'section{}'.format(i)
|
||||
cf.add_section(s)
|
||||
for j in range(10):
|
||||
cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
|
||||
with open(test_support.TESTFN, 'w') as f:
|
||||
cf.write(f)
|
||||
|
||||
def tearDown(self):
|
||||
os.unlink(test_support.TESTFN)
|
||||
|
||||
def test_dominating_multiline_values(self):
|
||||
# we're reading from file because this is where the code changed
|
||||
# during performance updates in Python 3.2
|
||||
cf_from_file = self.newconfig()
|
||||
with open(test_support.TESTFN) as f:
|
||||
cf_from_file.readfp(f)
|
||||
self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
|
||||
self.wonderful_spam.replace('\t\n', '\n'))
|
||||
|
||||
class RawConfigParserTestCase(TestCaseBase):
|
||||
config_class = ConfigParser.RawConfigParser
|
||||
|
@ -521,10 +555,11 @@ class SortedTestCase(RawConfigParserTestCase):
|
|||
def test_main():
|
||||
test_support.run_unittest(
|
||||
ConfigParserTestCase,
|
||||
MultilineValuesTestCase,
|
||||
RawConfigParserTestCase,
|
||||
SafeConfigParserTestCase,
|
||||
SortedTestCase,
|
||||
SafeConfigParserTestCaseNoValue,
|
||||
SortedTestCase,
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue