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
688b9e384e
commit
9a27b0cd19
@ -405,12 +405,11 @@ class RawConfigParser:
|
|||||||
for section in self._sections:
|
for section in self._sections:
|
||||||
fp.write("[%s]\n" % section)
|
fp.write("[%s]\n" % section)
|
||||||
for (key, value) in self._sections[section].items():
|
for (key, value) in self._sections[section].items():
|
||||||
if key != "__name__":
|
if key == "__name__":
|
||||||
if value is None:
|
continue
|
||||||
|
if value is not None:
|
||||||
|
key = " = ".join((key, str(value).replace('\n', '\n\t')))
|
||||||
fp.write("%s\n" % (key))
|
fp.write("%s\n" % (key))
|
||||||
else:
|
|
||||||
fp.write("%s = %s\n" %
|
|
||||||
(key, str(value).replace('\n', '\n\t')))
|
|
||||||
fp.write("\n")
|
fp.write("\n")
|
||||||
|
|
||||||
def remove_option(self, section, option):
|
def remove_option(self, section, option):
|
||||||
@ -490,7 +489,7 @@ class RawConfigParser:
|
|||||||
if line[0].isspace() and cursect is not None and optname:
|
if line[0].isspace() and cursect is not None and optname:
|
||||||
value = line.strip()
|
value = line.strip()
|
||||||
if value:
|
if value:
|
||||||
cursect[optname] = "%s\n%s" % (cursect[optname], value)
|
cursect[optname].append(value)
|
||||||
# a section header or option header?
|
# a section header or option header?
|
||||||
else:
|
else:
|
||||||
# is it a section header?
|
# is it a section header?
|
||||||
@ -515,6 +514,7 @@ class RawConfigParser:
|
|||||||
mo = self._optcre.match(line)
|
mo = self._optcre.match(line)
|
||||||
if mo:
|
if mo:
|
||||||
optname, vi, optval = mo.group('option', 'vi', 'value')
|
optname, vi, optval = mo.group('option', 'vi', 'value')
|
||||||
|
optname = self.optionxform(optname.rstrip())
|
||||||
# This check is fine because the OPTCRE cannot
|
# This check is fine because the OPTCRE cannot
|
||||||
# match if it would set optval to None
|
# match if it would set optval to None
|
||||||
if optval is not None:
|
if optval is not None:
|
||||||
@ -528,7 +528,9 @@ class RawConfigParser:
|
|||||||
# allow empty values
|
# allow empty values
|
||||||
if optval == '""':
|
if optval == '""':
|
||||||
optval = ''
|
optval = ''
|
||||||
optname = self.optionxform(optname.rstrip())
|
cursect[optname] = [optval]
|
||||||
|
else:
|
||||||
|
# valueless option handling
|
||||||
cursect[optname] = optval
|
cursect[optname] = optval
|
||||||
else:
|
else:
|
||||||
# a non-fatal parsing error occurred. set up the
|
# a non-fatal parsing error occurred. set up the
|
||||||
@ -542,6 +544,13 @@ class RawConfigParser:
|
|||||||
if e:
|
if e:
|
||||||
raise 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):
|
class ConfigParser(RawConfigParser):
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import collections
|
||||||
import configparser
|
import configparser
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import collections
|
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
@ -185,7 +186,8 @@ class TestCaseBase(unittest.TestCase):
|
|||||||
self.assertEqual(cf.sections(), [],
|
self.assertEqual(cf.sections(), [],
|
||||||
"new ConfigParser should have no defined sections")
|
"new ConfigParser should have no defined sections")
|
||||||
self.assertFalse(cf.has_section("Foo"),
|
self.assertFalse(cf.has_section("Foo"),
|
||||||
"new ConfigParser should have no acknowledged sections")
|
"new ConfigParser should have no acknowledged "
|
||||||
|
"sections")
|
||||||
with self.assertRaises(configparser.NoSectionError) as cm:
|
with self.assertRaises(configparser.NoSectionError) as cm:
|
||||||
cf.options("Foo")
|
cf.options("Foo")
|
||||||
with self.assertRaises(configparser.NoSectionError) as cm:
|
with self.assertRaises(configparser.NoSectionError) as cm:
|
||||||
@ -355,8 +357,8 @@ class ConfigParserTestCase(TestCaseBase):
|
|||||||
|
|
||||||
def test_interpolation(self):
|
def test_interpolation(self):
|
||||||
rawval = {
|
rawval = {
|
||||||
configparser.ConfigParser: "something %(with11)s "\
|
configparser.ConfigParser: ("something %(with11)s "
|
||||||
"lots of interpolation (11 steps)",
|
"lots of interpolation (11 steps)"),
|
||||||
configparser.SafeConfigParser: "%(with1)s",
|
configparser.SafeConfigParser: "%(with1)s",
|
||||||
}
|
}
|
||||||
cf = self.get_interpolation_config()
|
cf = self.get_interpolation_config()
|
||||||
@ -412,6 +414,33 @@ class ConfigParserTestCase(TestCaseBase):
|
|||||||
self.assertRaises(ValueError, cf.get, 'non-string',
|
self.assertRaises(ValueError, cf.get, 'non-string',
|
||||||
'string_with_interpolation', raw=False)
|
'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(support.TESTFN, 'w') as f:
|
||||||
|
cf.write(f)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
os.unlink(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(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):
|
class RawConfigParserTestCase(TestCaseBase):
|
||||||
config_class = configparser.RawConfigParser
|
config_class = configparser.RawConfigParser
|
||||||
@ -530,10 +559,11 @@ class SortedTestCase(RawConfigParserTestCase):
|
|||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(
|
support.run_unittest(
|
||||||
ConfigParserTestCase,
|
ConfigParserTestCase,
|
||||||
|
MultilineValuesTestCase,
|
||||||
RawConfigParserTestCase,
|
RawConfigParserTestCase,
|
||||||
SafeConfigParserTestCase,
|
SafeConfigParserTestCase,
|
||||||
SortedTestCase,
|
|
||||||
SafeConfigParserTestCaseNoValue,
|
SafeConfigParserTestCaseNoValue,
|
||||||
|
SortedTestCase,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user