Home | History | Annotate | Download | only in test
      1 import ConfigParser
      2 import StringIO
      3 import os
      4 import unittest
      5 import UserDict
      6 
      7 from test import test_support
      8 
      9 
     10 class SortedDict(UserDict.UserDict):
     11     def items(self):
     12         result = self.data.items()
     13         result.sort()
     14         return result
     15 
     16     def keys(self):
     17         result = self.data.keys()
     18         result.sort()
     19         return result
     20 
     21     def values(self):
     22         # XXX never used?
     23         result = self.items()
     24         return [i[1] for i in result]
     25 
     26     def iteritems(self): return iter(self.items())
     27     def iterkeys(self): return iter(self.keys())
     28     __iter__ = iterkeys
     29     def itervalues(self): return iter(self.values())
     30 
     31 
     32 class TestCaseBase(unittest.TestCase):
     33     allow_no_value = False
     34 
     35     def newconfig(self, defaults=None):
     36         if defaults is None:
     37             self.cf = self.config_class(allow_no_value=self.allow_no_value)
     38         else:
     39             self.cf = self.config_class(defaults,
     40                                         allow_no_value=self.allow_no_value)
     41         return self.cf
     42 
     43     def fromstring(self, string, defaults=None):
     44         cf = self.newconfig(defaults)
     45         sio = StringIO.StringIO(string)
     46         cf.readfp(sio)
     47         return cf
     48 
     49     def test_basic(self):
     50         config_string = (
     51             "[Foo Bar]\n"
     52             "foo=bar\n"
     53             "[Spacey Bar]\n"
     54             "foo = bar\n"
     55             "[Commented Bar]\n"
     56             "foo: bar ; comment\n"
     57             "[Long Line]\n"
     58             "foo: this line is much, much longer than my editor\n"
     59             "   likes it.\n"
     60             "[Section\\with$weird%characters[\t]\n"
     61             "[Internationalized Stuff]\n"
     62             "foo[bg]: Bulgarian\n"
     63             "foo=Default\n"
     64             "foo[en]=English\n"
     65             "foo[de]=Deutsch\n"
     66             "[Spaces]\n"
     67             "key with spaces : value\n"
     68             "another with spaces = splat!\n"
     69             )
     70         if self.allow_no_value:
     71             config_string += (
     72                 "[NoValue]\n"
     73                 "option-without-value\n"
     74                 )
     75 
     76         cf = self.fromstring(config_string)
     77         L = cf.sections()
     78         L.sort()
     79         E = [r'Commented Bar',
     80              r'Foo Bar',
     81              r'Internationalized Stuff',
     82              r'Long Line',
     83              r'Section\with$weird%characters[' '\t',
     84              r'Spaces',
     85              r'Spacey Bar',
     86              ]
     87         if self.allow_no_value:
     88             E.append(r'NoValue')
     89         E.sort()
     90         eq = self.assertEqual
     91         eq(L, E)
     92 
     93         # The use of spaces in the section names serves as a
     94         # regression test for SourceForge bug #583248:
     95         # http://www.python.org/sf/583248
     96         eq(cf.get('Foo Bar', 'foo'), 'bar')
     97         eq(cf.get('Spacey Bar', 'foo'), 'bar')
     98         eq(cf.get('Commented Bar', 'foo'), 'bar')
     99         eq(cf.get('Spaces', 'key with spaces'), 'value')
    100         eq(cf.get('Spaces', 'another with spaces'), 'splat!')
    101         if self.allow_no_value:
    102             eq(cf.get('NoValue', 'option-without-value'), None)
    103 
    104         self.assertNotIn('__name__', cf.options("Foo Bar"),
    105                          '__name__ "option" should not be exposed by the API!')
    106 
    107         # Make sure the right things happen for remove_option();
    108         # added to include check for SourceForge bug #123324:
    109         self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
    110                         "remove_option() failed to report existence of option")
    111         self.assertFalse(cf.has_option('Foo Bar', 'foo'),
    112                     "remove_option() failed to remove option")
    113         self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
    114                     "remove_option() failed to report non-existence of option"
    115                     " that was removed")
    116 
    117         self.assertRaises(ConfigParser.NoSectionError,
    118                           cf.remove_option, 'No Such Section', 'foo')
    119 
    120         eq(cf.get('Long Line', 'foo'),
    121            'this line is much, much longer than my editor\nlikes it.')
    122 
    123     def test_case_sensitivity(self):
    124         cf = self.newconfig()
    125         cf.add_section("A")
    126         cf.add_section("a")
    127         L = cf.sections()
    128         L.sort()
    129         eq = self.assertEqual
    130         eq(L, ["A", "a"])
    131         cf.set("a", "B", "value")
    132         eq(cf.options("a"), ["b"])
    133         eq(cf.get("a", "b"), "value",
    134            "could not locate option, expecting case-insensitive option names")
    135         self.assertTrue(cf.has_option("a", "b"))
    136         cf.set("A", "A-B", "A-B value")
    137         for opt in ("a-b", "A-b", "a-B", "A-B"):
    138             self.assertTrue(
    139                 cf.has_option("A", opt),
    140                 "has_option() returned false for option which should exist")
    141         eq(cf.options("A"), ["a-b"])
    142         eq(cf.options("a"), ["b"])
    143         cf.remove_option("a", "B")
    144         eq(cf.options("a"), [])
    145 
    146         # SF bug #432369:
    147         cf = self.fromstring(
    148             "[MySection]\nOption: first line\n\tsecond line\n")
    149         eq(cf.options("MySection"), ["option"])
    150         eq(cf.get("MySection", "Option"), "first line\nsecond line")
    151 
    152         # SF bug #561822:
    153         cf = self.fromstring("[section]\nnekey=nevalue\n",
    154                              defaults={"key":"value"})
    155         self.assertTrue(cf.has_option("section", "Key"))
    156 
    157 
    158     def test_default_case_sensitivity(self):
    159         cf = self.newconfig({"foo": "Bar"})
    160         self.assertEqual(
    161             cf.get("DEFAULT", "Foo"), "Bar",
    162             "could not locate option, expecting case-insensitive option names")
    163         cf = self.newconfig({"Foo": "Bar"})
    164         self.assertEqual(
    165             cf.get("DEFAULT", "Foo"), "Bar",
    166             "could not locate option, expecting case-insensitive defaults")
    167 
    168     def test_parse_errors(self):
    169         self.newconfig()
    170         self.parse_error(ConfigParser.ParsingError,
    171                          "[Foo]\n  extra-spaces: splat\n")
    172         self.parse_error(ConfigParser.ParsingError,
    173                          "[Foo]\n  extra-spaces= splat\n")
    174         self.parse_error(ConfigParser.ParsingError,
    175                          "[Foo]\n:value-without-option-name\n")
    176         self.parse_error(ConfigParser.ParsingError,
    177                          "[Foo]\n=value-without-option-name\n")
    178         self.parse_error(ConfigParser.MissingSectionHeaderError,
    179                          "No Section!\n")
    180 
    181     def parse_error(self, exc, src):
    182         sio = StringIO.StringIO(src)
    183         self.assertRaises(exc, self.cf.readfp, sio)
    184 
    185     def test_query_errors(self):
    186         cf = self.newconfig()
    187         self.assertEqual(cf.sections(), [],
    188                          "new ConfigParser should have no defined sections")
    189         self.assertFalse(cf.has_section("Foo"),
    190                          "new ConfigParser should have no acknowledged "
    191                          "sections")
    192         self.assertRaises(ConfigParser.NoSectionError,
    193                           cf.options, "Foo")
    194         self.assertRaises(ConfigParser.NoSectionError,
    195                           cf.set, "foo", "bar", "value")
    196         self.get_error(ConfigParser.NoSectionError, "foo", "bar")
    197         cf.add_section("foo")
    198         self.get_error(ConfigParser.NoOptionError, "foo", "bar")
    199 
    200     def get_error(self, exc, section, option):
    201         try:
    202             self.cf.get(section, option)
    203         except exc, e:
    204             return e
    205         else:
    206             self.fail("expected exception type %s.%s"
    207                       % (exc.__module__, exc.__name__))
    208 
    209     def test_boolean(self):
    210         cf = self.fromstring(
    211             "[BOOLTEST]\n"
    212             "T1=1\n"
    213             "T2=TRUE\n"
    214             "T3=True\n"
    215             "T4=oN\n"
    216             "T5=yes\n"
    217             "F1=0\n"
    218             "F2=FALSE\n"
    219             "F3=False\n"
    220             "F4=oFF\n"
    221             "F5=nO\n"
    222             "E1=2\n"
    223             "E2=foo\n"
    224             "E3=-1\n"
    225             "E4=0.1\n"
    226             "E5=FALSE AND MORE"
    227             )
    228         for x in range(1, 5):
    229             self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
    230             self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
    231             self.assertRaises(ValueError,
    232                               cf.getboolean, 'BOOLTEST', 'e%d' % x)
    233 
    234     def test_weird_errors(self):
    235         cf = self.newconfig()
    236         cf.add_section("Foo")
    237         self.assertRaises(ConfigParser.DuplicateSectionError,
    238                           cf.add_section, "Foo")
    239 
    240     def test_write(self):
    241         config_string = (
    242             "[Long Line]\n"
    243             "foo: this line is much, much longer than my editor\n"
    244             "   likes it.\n"
    245             "[DEFAULT]\n"
    246             "foo: another very\n"
    247             " long line\n"
    248             )
    249         if self.allow_no_value:
    250             config_string += (
    251             "[Valueless]\n"
    252             "option-without-value\n"
    253             )
    254 
    255         cf = self.fromstring(config_string)
    256         output = StringIO.StringIO()
    257         cf.write(output)
    258         expect_string = (
    259             "[DEFAULT]\n"
    260             "foo = another very\n"
    261             "\tlong line\n"
    262             "\n"
    263             "[Long Line]\n"
    264             "foo = this line is much, much longer than my editor\n"
    265             "\tlikes it.\n"
    266             "\n"
    267             )
    268         if self.allow_no_value:
    269             expect_string += (
    270                 "[Valueless]\n"
    271                 "option-without-value\n"
    272                 "\n"
    273                 )
    274         self.assertEqual(output.getvalue(), expect_string)
    275 
    276     def test_set_string_types(self):
    277         cf = self.fromstring("[sect]\n"
    278                              "option1=foo\n")
    279         # Check that we don't get an exception when setting values in
    280         # an existing section using strings:
    281         class mystr(str):
    282             pass
    283         cf.set("sect", "option1", "splat")
    284         cf.set("sect", "option1", mystr("splat"))
    285         cf.set("sect", "option2", "splat")
    286         cf.set("sect", "option2", mystr("splat"))
    287 
    288     def test_set_unicode(self):
    289         try:
    290             unicode
    291         except NameError:
    292             self.skipTest('no unicode support')
    293 
    294         cf = self.fromstring("[sect]\n"
    295                              "option1=foo\n")
    296         cf.set("sect", "option1", unicode("splat"))
    297         cf.set("sect", "option2", unicode("splat"))
    298 
    299     def test_read_returns_file_list(self):
    300         file1 = test_support.findfile("cfgparser.1")
    301         # check when we pass a mix of readable and non-readable files:
    302         cf = self.newconfig()
    303         parsed_files = cf.read([file1, "nonexistent-file"])
    304         self.assertEqual(parsed_files, [file1])
    305         self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
    306         # check when we pass only a filename:
    307         cf = self.newconfig()
    308         parsed_files = cf.read(file1)
    309         self.assertEqual(parsed_files, [file1])
    310         self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
    311         # check when we pass only missing files:
    312         cf = self.newconfig()
    313         parsed_files = cf.read(["nonexistent-file"])
    314         self.assertEqual(parsed_files, [])
    315         # check when we pass no files:
    316         cf = self.newconfig()
    317         parsed_files = cf.read([])
    318         self.assertEqual(parsed_files, [])
    319 
    320     # shared by subclasses
    321     def get_interpolation_config(self):
    322         return self.fromstring(
    323             "[Foo]\n"
    324             "bar=something %(with1)s interpolation (1 step)\n"
    325             "bar9=something %(with9)s lots of interpolation (9 steps)\n"
    326             "bar10=something %(with10)s lots of interpolation (10 steps)\n"
    327             "bar11=something %(with11)s lots of interpolation (11 steps)\n"
    328             "with11=%(with10)s\n"
    329             "with10=%(with9)s\n"
    330             "with9=%(with8)s\n"
    331             "with8=%(With7)s\n"
    332             "with7=%(WITH6)s\n"
    333             "with6=%(with5)s\n"
    334             "With5=%(with4)s\n"
    335             "WITH4=%(with3)s\n"
    336             "with3=%(with2)s\n"
    337             "with2=%(with1)s\n"
    338             "with1=with\n"
    339             "\n"
    340             "[Mutual Recursion]\n"
    341             "foo=%(bar)s\n"
    342             "bar=%(foo)s\n"
    343             "\n"
    344             "[Interpolation Error]\n"
    345             "name=%(reference)s\n",
    346             # no definition for 'reference'
    347             defaults={"getname": "%(__name__)s"})
    348 
    349     def check_items_config(self, expected):
    350         cf = self.fromstring(
    351             "[section]\n"
    352             "name = value\n"
    353             "key: |%(name)s| \n"
    354             "getdefault: |%(default)s|\n"
    355             "getname: |%(__name__)s|",
    356             defaults={"default": "<default>"})
    357         L = list(cf.items("section"))
    358         L.sort()
    359         self.assertEqual(L, expected)
    360 
    361 
    362 class ConfigParserTestCase(TestCaseBase):
    363     config_class = ConfigParser.ConfigParser
    364     allow_no_value = True
    365 
    366     def test_interpolation(self):
    367         rawval = {
    368             ConfigParser.ConfigParser: ("something %(with11)s "
    369                                         "lots of interpolation (11 steps)"),
    370             ConfigParser.SafeConfigParser: "%(with1)s",
    371         }
    372         cf = self.get_interpolation_config()
    373         eq = self.assertEqual
    374         eq(cf.get("Foo", "getname"), "Foo")
    375         eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
    376         eq(cf.get("Foo", "bar9"),
    377            "something with lots of interpolation (9 steps)")
    378         eq(cf.get("Foo", "bar10"),
    379            "something with lots of interpolation (10 steps)")
    380         self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
    381 
    382     def test_interpolation_missing_value(self):
    383         self.get_interpolation_config()
    384         e = self.get_error(ConfigParser.InterpolationError,
    385                            "Interpolation Error", "name")
    386         self.assertEqual(e.reference, "reference")
    387         self.assertEqual(e.section, "Interpolation Error")
    388         self.assertEqual(e.option, "name")
    389 
    390     def test_items(self):
    391         self.check_items_config([('default', '<default>'),
    392                                  ('getdefault', '|<default>|'),
    393                                  ('getname', '|section|'),
    394                                  ('key', '|value|'),
    395                                  ('name', 'value')])
    396 
    397     def test_set_nonstring_types(self):
    398         cf = self.newconfig()
    399         cf.add_section('non-string')
    400         cf.set('non-string', 'int', 1)
    401         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
    402         cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
    403                                       '%(list)': '%(list)'})
    404         cf.set('non-string', 'string_with_interpolation', '%(list)s')
    405         cf.set('non-string', 'no-value')
    406         self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
    407         self.assertRaises(TypeError, cf.get, 'non-string', 'int')
    408         self.assertEqual(cf.get('non-string', 'list', raw=True),
    409                          [0, 1, 1, 2, 3, 5, 8, 13, '%('])
    410         self.assertRaises(TypeError, cf.get, 'non-string', 'list')
    411         self.assertEqual(cf.get('non-string', 'dict', raw=True),
    412                          {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
    413         self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
    414         self.assertEqual(cf.get('non-string', 'string_with_interpolation',
    415                                 raw=True), '%(list)s')
    416         self.assertRaises(ValueError, cf.get, 'non-string',
    417                           'string_with_interpolation', raw=False)
    418         self.assertEqual(cf.get('non-string', 'no-value'), None)
    419 
    420 class MultilineValuesTestCase(TestCaseBase):
    421     config_class = ConfigParser.ConfigParser
    422     wonderful_spam = ("I'm having spam spam spam spam "
    423                       "spam spam spam beaked beans spam "
    424                       "spam spam and spam!").replace(' ', '\t\n')
    425 
    426     def setUp(self):
    427         cf = self.newconfig()
    428         for i in range(100):
    429             s = 'section{}'.format(i)
    430             cf.add_section(s)
    431             for j in range(10):
    432                 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
    433         with open(test_support.TESTFN, 'w') as f:
    434             cf.write(f)
    435 
    436     def tearDown(self):
    437         os.unlink(test_support.TESTFN)
    438 
    439     def test_dominating_multiline_values(self):
    440         # we're reading from file because this is where the code changed
    441         # during performance updates in Python 3.2
    442         cf_from_file = self.newconfig()
    443         with open(test_support.TESTFN) as f:
    444             cf_from_file.readfp(f)
    445         self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
    446                          self.wonderful_spam.replace('\t\n', '\n'))
    447 
    448 class RawConfigParserTestCase(TestCaseBase):
    449     config_class = ConfigParser.RawConfigParser
    450 
    451     def test_interpolation(self):
    452         cf = self.get_interpolation_config()
    453         eq = self.assertEqual
    454         eq(cf.get("Foo", "getname"), "%(__name__)s")
    455         eq(cf.get("Foo", "bar"),
    456            "something %(with1)s interpolation (1 step)")
    457         eq(cf.get("Foo", "bar9"),
    458            "something %(with9)s lots of interpolation (9 steps)")
    459         eq(cf.get("Foo", "bar10"),
    460            "something %(with10)s lots of interpolation (10 steps)")
    461         eq(cf.get("Foo", "bar11"),
    462            "something %(with11)s lots of interpolation (11 steps)")
    463 
    464     def test_items(self):
    465         self.check_items_config([('default', '<default>'),
    466                                  ('getdefault', '|%(default)s|'),
    467                                  ('getname', '|%(__name__)s|'),
    468                                  ('key', '|%(name)s|'),
    469                                  ('name', 'value')])
    470 
    471     def test_set_nonstring_types(self):
    472         cf = self.newconfig()
    473         cf.add_section('non-string')
    474         cf.set('non-string', 'int', 1)
    475         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
    476         cf.set('non-string', 'dict', {'pi': 3.14159})
    477         self.assertEqual(cf.get('non-string', 'int'), 1)
    478         self.assertEqual(cf.get('non-string', 'list'),
    479                          [0, 1, 1, 2, 3, 5, 8, 13])
    480         self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
    481 
    482 
    483 class SafeConfigParserTestCase(ConfigParserTestCase):
    484     config_class = ConfigParser.SafeConfigParser
    485 
    486     def test_safe_interpolation(self):
    487         # See http://www.python.org/sf/511737
    488         cf = self.fromstring("[section]\n"
    489                              "option1=xxx\n"
    490                              "option2=%(option1)s/xxx\n"
    491                              "ok=%(option1)s/%%s\n"
    492                              "not_ok=%(option2)s/%%s")
    493         self.assertEqual(cf.get("section", "ok"), "xxx/%s")
    494         self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
    495 
    496     def test_set_malformatted_interpolation(self):
    497         cf = self.fromstring("[sect]\n"
    498                              "option1=foo\n")
    499 
    500         self.assertEqual(cf.get('sect', "option1"), "foo")
    501 
    502         self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
    503         self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
    504         self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
    505 
    506         self.assertEqual(cf.get('sect', "option1"), "foo")
    507 
    508         # bug #5741: double percents are *not* malformed
    509         cf.set("sect", "option2", "foo%%bar")
    510         self.assertEqual(cf.get("sect", "option2"), "foo%bar")
    511 
    512     def test_set_nonstring_types(self):
    513         cf = self.fromstring("[sect]\n"
    514                              "option1=foo\n")
    515         # Check that we get a TypeError when setting non-string values
    516         # in an existing section:
    517         self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
    518         self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
    519         self.assertRaises(TypeError, cf.set, "sect", "option1", object())
    520         self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
    521         self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
    522         self.assertRaises(TypeError, cf.set, "sect", "option2", object())
    523 
    524     def test_add_section_default_1(self):
    525         cf = self.newconfig()
    526         self.assertRaises(ValueError, cf.add_section, "default")
    527 
    528     def test_add_section_default_2(self):
    529         cf = self.newconfig()
    530         self.assertRaises(ValueError, cf.add_section, "DEFAULT")
    531 
    532 
    533 class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase):
    534     allow_no_value = True
    535 
    536 class TestChainMap(unittest.TestCase):
    537     def test_issue_12717(self):
    538         d1 = dict(red=1, green=2)
    539         d2 = dict(green=3, blue=4)
    540         dcomb = d2.copy()
    541         dcomb.update(d1)
    542         cm = ConfigParser._Chainmap(d1, d2)
    543         self.assertIsInstance(cm.keys(), list)
    544         self.assertEqual(set(cm.keys()), set(dcomb.keys()))      # keys()
    545         self.assertEqual(set(cm.values()), set(dcomb.values()))  # values()
    546         self.assertEqual(set(cm.items()), set(dcomb.items()))    # items()
    547         self.assertEqual(set(cm), set(dcomb))                    # __iter__ ()
    548         self.assertEqual(cm, dcomb)                              # __eq__()
    549         self.assertEqual([cm[k] for k in dcomb], dcomb.values()) # __getitem__()
    550         klist = 'red green blue black brown'.split()
    551         self.assertEqual([cm.get(k, 10) for k in klist],
    552                          [dcomb.get(k, 10) for k in klist])      # get()
    553         self.assertEqual([k in cm for k in klist],
    554                          [k in dcomb for k in klist])            # __contains__()
    555         with test_support.check_py3k_warnings():
    556             self.assertEqual([cm.has_key(k) for k in klist],
    557                              [dcomb.has_key(k) for k in klist])  # has_key()
    558 
    559 class Issue7005TestCase(unittest.TestCase):
    560     """Test output when None is set() as a value and allow_no_value == False.
    561 
    562     http://bugs.python.org/issue7005
    563 
    564     """
    565 
    566     expected_output = "[section]\noption = None\n\n"
    567 
    568     def prepare(self, config_class):
    569         # This is the default, but that's the point.
    570         cp = config_class(allow_no_value=False)
    571         cp.add_section("section")
    572         cp.set("section", "option", None)
    573         sio = StringIO.StringIO()
    574         cp.write(sio)
    575         return sio.getvalue()
    576 
    577     def test_none_as_value_stringified(self):
    578         output = self.prepare(ConfigParser.ConfigParser)
    579         self.assertEqual(output, self.expected_output)
    580 
    581     def test_none_as_value_stringified_raw(self):
    582         output = self.prepare(ConfigParser.RawConfigParser)
    583         self.assertEqual(output, self.expected_output)
    584 
    585 
    586 class SortedTestCase(RawConfigParserTestCase):
    587     def newconfig(self, defaults=None):
    588         self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
    589         return self.cf
    590 
    591     def test_sorted(self):
    592         self.fromstring("[b]\n"
    593                         "o4=1\n"
    594                         "o3=2\n"
    595                         "o2=3\n"
    596                         "o1=4\n"
    597                         "[a]\n"
    598                         "k=v\n")
    599         output = StringIO.StringIO()
    600         self.cf.write(output)
    601         self.assertEqual(output.getvalue(),
    602                          "[a]\n"
    603                          "k = v\n\n"
    604                          "[b]\n"
    605                          "o1 = 4\n"
    606                          "o2 = 3\n"
    607                          "o3 = 2\n"
    608                          "o4 = 1\n\n")
    609 
    610 
    611 class ExceptionPicklingTestCase(unittest.TestCase):
    612     """Tests for issue #13760: ConfigParser exceptions are not picklable."""
    613 
    614     def test_error(self):
    615         import pickle
    616         e1 = ConfigParser.Error('value')
    617         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    618             pickled = pickle.dumps(e1, proto)
    619             e2 = pickle.loads(pickled)
    620             self.assertEqual(e1.message, e2.message)
    621             self.assertEqual(repr(e1), repr(e2))
    622 
    623     def test_nosectionerror(self):
    624         import pickle
    625         e1 = ConfigParser.NoSectionError('section')
    626         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    627             pickled = pickle.dumps(e1, proto)
    628             e2 = pickle.loads(pickled)
    629             self.assertEqual(e1.message, e2.message)
    630             self.assertEqual(e1.args, e2.args)
    631             self.assertEqual(e1.section, e2.section)
    632             self.assertEqual(repr(e1), repr(e2))
    633 
    634     def test_nooptionerror(self):
    635         import pickle
    636         e1 = ConfigParser.NoOptionError('option', 'section')
    637         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    638             pickled = pickle.dumps(e1, proto)
    639             e2 = pickle.loads(pickled)
    640             self.assertEqual(e1.message, e2.message)
    641             self.assertEqual(e1.args, e2.args)
    642             self.assertEqual(e1.section, e2.section)
    643             self.assertEqual(e1.option, e2.option)
    644             self.assertEqual(repr(e1), repr(e2))
    645 
    646     def test_duplicatesectionerror(self):
    647         import pickle
    648         e1 = ConfigParser.DuplicateSectionError('section')
    649         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    650             pickled = pickle.dumps(e1, proto)
    651             e2 = pickle.loads(pickled)
    652             self.assertEqual(e1.message, e2.message)
    653             self.assertEqual(e1.args, e2.args)
    654             self.assertEqual(e1.section, e2.section)
    655             self.assertEqual(repr(e1), repr(e2))
    656 
    657     def test_interpolationerror(self):
    658         import pickle
    659         e1 = ConfigParser.InterpolationError('option', 'section', 'msg')
    660         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    661             pickled = pickle.dumps(e1, proto)
    662             e2 = pickle.loads(pickled)
    663             self.assertEqual(e1.message, e2.message)
    664             self.assertEqual(e1.args, e2.args)
    665             self.assertEqual(e1.section, e2.section)
    666             self.assertEqual(e1.option, e2.option)
    667             self.assertEqual(repr(e1), repr(e2))
    668 
    669     def test_interpolationmissingoptionerror(self):
    670         import pickle
    671         e1 = ConfigParser.InterpolationMissingOptionError('option', 'section',
    672             'rawval', 'reference')
    673         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    674             pickled = pickle.dumps(e1, proto)
    675             e2 = pickle.loads(pickled)
    676             self.assertEqual(e1.message, e2.message)
    677             self.assertEqual(e1.args, e2.args)
    678             self.assertEqual(e1.section, e2.section)
    679             self.assertEqual(e1.option, e2.option)
    680             self.assertEqual(e1.reference, e2.reference)
    681             self.assertEqual(repr(e1), repr(e2))
    682 
    683     def test_interpolationsyntaxerror(self):
    684         import pickle
    685         e1 = ConfigParser.InterpolationSyntaxError('option', 'section', 'msg')
    686         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    687             pickled = pickle.dumps(e1, proto)
    688             e2 = pickle.loads(pickled)
    689             self.assertEqual(e1.message, e2.message)
    690             self.assertEqual(e1.args, e2.args)
    691             self.assertEqual(e1.section, e2.section)
    692             self.assertEqual(e1.option, e2.option)
    693             self.assertEqual(repr(e1), repr(e2))
    694 
    695     def test_interpolationdeptherror(self):
    696         import pickle
    697         e1 = ConfigParser.InterpolationDepthError('option', 'section',
    698             'rawval')
    699         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    700             pickled = pickle.dumps(e1, proto)
    701             e2 = pickle.loads(pickled)
    702             self.assertEqual(e1.message, e2.message)
    703             self.assertEqual(e1.args, e2.args)
    704             self.assertEqual(e1.section, e2.section)
    705             self.assertEqual(e1.option, e2.option)
    706             self.assertEqual(repr(e1), repr(e2))
    707 
    708     def test_parsingerror(self):
    709         import pickle
    710         e1 = ConfigParser.ParsingError('source')
    711         e1.append(1, 'line1')
    712         e1.append(2, 'line2')
    713         e1.append(3, 'line3')
    714         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    715             pickled = pickle.dumps(e1, proto)
    716             e2 = pickle.loads(pickled)
    717             self.assertEqual(e1.message, e2.message)
    718             self.assertEqual(e1.args, e2.args)
    719             self.assertEqual(e1.filename, e2.filename)
    720             self.assertEqual(e1.errors, e2.errors)
    721             self.assertEqual(repr(e1), repr(e2))
    722 
    723     def test_missingsectionheadererror(self):
    724         import pickle
    725         e1 = ConfigParser.MissingSectionHeaderError('filename', 123, 'line')
    726         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    727             pickled = pickle.dumps(e1, proto)
    728             e2 = pickle.loads(pickled)
    729             self.assertEqual(e1.message, e2.message)
    730             self.assertEqual(e1.args, e2.args)
    731             self.assertEqual(e1.line, e2.line)
    732             self.assertEqual(e1.filename, e2.filename)
    733             self.assertEqual(e1.lineno, e2.lineno)
    734             self.assertEqual(repr(e1), repr(e2))
    735 
    736 
    737 def test_main():
    738     test_support.run_unittest(
    739         ConfigParserTestCase,
    740         MultilineValuesTestCase,
    741         RawConfigParserTestCase,
    742         SafeConfigParserTestCase,
    743         SafeConfigParserTestCaseNoValue,
    744         SortedTestCase,
    745         Issue7005TestCase,
    746         TestChainMap,
    747         ExceptionPicklingTestCase,
    748         )
    749 
    750 
    751 if __name__ == "__main__":
    752     test_main()
    753