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         try:
    288             unicode
    289         except NameError:
    290             pass
    291         else:
    292             cf.set("sect", "option1", unicode("splat"))
    293             cf.set("sect", "option2", unicode("splat"))
    294 
    295     def test_read_returns_file_list(self):
    296         file1 = test_support.findfile("cfgparser.1")
    297         # check when we pass a mix of readable and non-readable files:
    298         cf = self.newconfig()
    299         parsed_files = cf.read([file1, "nonexistent-file"])
    300         self.assertEqual(parsed_files, [file1])
    301         self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
    302         # check when we pass only a filename:
    303         cf = self.newconfig()
    304         parsed_files = cf.read(file1)
    305         self.assertEqual(parsed_files, [file1])
    306         self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
    307         # check when we pass only missing files:
    308         cf = self.newconfig()
    309         parsed_files = cf.read(["nonexistent-file"])
    310         self.assertEqual(parsed_files, [])
    311         # check when we pass no files:
    312         cf = self.newconfig()
    313         parsed_files = cf.read([])
    314         self.assertEqual(parsed_files, [])
    315 
    316     # shared by subclasses
    317     def get_interpolation_config(self):
    318         return self.fromstring(
    319             "[Foo]\n"
    320             "bar=something %(with1)s interpolation (1 step)\n"
    321             "bar9=something %(with9)s lots of interpolation (9 steps)\n"
    322             "bar10=something %(with10)s lots of interpolation (10 steps)\n"
    323             "bar11=something %(with11)s lots of interpolation (11 steps)\n"
    324             "with11=%(with10)s\n"
    325             "with10=%(with9)s\n"
    326             "with9=%(with8)s\n"
    327             "with8=%(With7)s\n"
    328             "with7=%(WITH6)s\n"
    329             "with6=%(with5)s\n"
    330             "With5=%(with4)s\n"
    331             "WITH4=%(with3)s\n"
    332             "with3=%(with2)s\n"
    333             "with2=%(with1)s\n"
    334             "with1=with\n"
    335             "\n"
    336             "[Mutual Recursion]\n"
    337             "foo=%(bar)s\n"
    338             "bar=%(foo)s\n"
    339             "\n"
    340             "[Interpolation Error]\n"
    341             "name=%(reference)s\n",
    342             # no definition for 'reference'
    343             defaults={"getname": "%(__name__)s"})
    344 
    345     def check_items_config(self, expected):
    346         cf = self.fromstring(
    347             "[section]\n"
    348             "name = value\n"
    349             "key: |%(name)s| \n"
    350             "getdefault: |%(default)s|\n"
    351             "getname: |%(__name__)s|",
    352             defaults={"default": "<default>"})
    353         L = list(cf.items("section"))
    354         L.sort()
    355         self.assertEqual(L, expected)
    356 
    357 
    358 class ConfigParserTestCase(TestCaseBase):
    359     config_class = ConfigParser.ConfigParser
    360     allow_no_value = True
    361 
    362     def test_interpolation(self):
    363         rawval = {
    364             ConfigParser.ConfigParser: ("something %(with11)s "
    365                                         "lots of interpolation (11 steps)"),
    366             ConfigParser.SafeConfigParser: "%(with1)s",
    367         }
    368         cf = self.get_interpolation_config()
    369         eq = self.assertEqual
    370         eq(cf.get("Foo", "getname"), "Foo")
    371         eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
    372         eq(cf.get("Foo", "bar9"),
    373            "something with lots of interpolation (9 steps)")
    374         eq(cf.get("Foo", "bar10"),
    375            "something with lots of interpolation (10 steps)")
    376         self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
    377 
    378     def test_interpolation_missing_value(self):
    379         self.get_interpolation_config()
    380         e = self.get_error(ConfigParser.InterpolationError,
    381                            "Interpolation Error", "name")
    382         self.assertEqual(e.reference, "reference")
    383         self.assertEqual(e.section, "Interpolation Error")
    384         self.assertEqual(e.option, "name")
    385 
    386     def test_items(self):
    387         self.check_items_config([('default', '<default>'),
    388                                  ('getdefault', '|<default>|'),
    389                                  ('getname', '|section|'),
    390                                  ('key', '|value|'),
    391                                  ('name', 'value')])
    392 
    393     def test_set_nonstring_types(self):
    394         cf = self.newconfig()
    395         cf.add_section('non-string')
    396         cf.set('non-string', 'int', 1)
    397         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
    398         cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
    399                                       '%(list)': '%(list)'})
    400         cf.set('non-string', 'string_with_interpolation', '%(list)s')
    401         cf.set('non-string', 'no-value')
    402         self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
    403         self.assertRaises(TypeError, cf.get, 'non-string', 'int')
    404         self.assertEqual(cf.get('non-string', 'list', raw=True),
    405                          [0, 1, 1, 2, 3, 5, 8, 13, '%('])
    406         self.assertRaises(TypeError, cf.get, 'non-string', 'list')
    407         self.assertEqual(cf.get('non-string', 'dict', raw=True),
    408                          {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
    409         self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
    410         self.assertEqual(cf.get('non-string', 'string_with_interpolation',
    411                                 raw=True), '%(list)s')
    412         self.assertRaises(ValueError, cf.get, 'non-string',
    413                           'string_with_interpolation', raw=False)
    414         self.assertEqual(cf.get('non-string', 'no-value'), None)
    415 
    416 class MultilineValuesTestCase(TestCaseBase):
    417     config_class = ConfigParser.ConfigParser
    418     wonderful_spam = ("I'm having spam spam spam spam "
    419                       "spam spam spam beaked beans spam "
    420                       "spam spam and spam!").replace(' ', '\t\n')
    421 
    422     def setUp(self):
    423         cf = self.newconfig()
    424         for i in range(100):
    425             s = 'section{}'.format(i)
    426             cf.add_section(s)
    427             for j in range(10):
    428                 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
    429         with open(test_support.TESTFN, 'w') as f:
    430             cf.write(f)
    431 
    432     def tearDown(self):
    433         os.unlink(test_support.TESTFN)
    434 
    435     def test_dominating_multiline_values(self):
    436         # we're reading from file because this is where the code changed
    437         # during performance updates in Python 3.2
    438         cf_from_file = self.newconfig()
    439         with open(test_support.TESTFN) as f:
    440             cf_from_file.readfp(f)
    441         self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
    442                          self.wonderful_spam.replace('\t\n', '\n'))
    443 
    444 class RawConfigParserTestCase(TestCaseBase):
    445     config_class = ConfigParser.RawConfigParser
    446 
    447     def test_interpolation(self):
    448         cf = self.get_interpolation_config()
    449         eq = self.assertEqual
    450         eq(cf.get("Foo", "getname"), "%(__name__)s")
    451         eq(cf.get("Foo", "bar"),
    452            "something %(with1)s interpolation (1 step)")
    453         eq(cf.get("Foo", "bar9"),
    454            "something %(with9)s lots of interpolation (9 steps)")
    455         eq(cf.get("Foo", "bar10"),
    456            "something %(with10)s lots of interpolation (10 steps)")
    457         eq(cf.get("Foo", "bar11"),
    458            "something %(with11)s lots of interpolation (11 steps)")
    459 
    460     def test_items(self):
    461         self.check_items_config([('default', '<default>'),
    462                                  ('getdefault', '|%(default)s|'),
    463                                  ('getname', '|%(__name__)s|'),
    464                                  ('key', '|%(name)s|'),
    465                                  ('name', 'value')])
    466 
    467     def test_set_nonstring_types(self):
    468         cf = self.newconfig()
    469         cf.add_section('non-string')
    470         cf.set('non-string', 'int', 1)
    471         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
    472         cf.set('non-string', 'dict', {'pi': 3.14159})
    473         self.assertEqual(cf.get('non-string', 'int'), 1)
    474         self.assertEqual(cf.get('non-string', 'list'),
    475                          [0, 1, 1, 2, 3, 5, 8, 13])
    476         self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
    477 
    478 
    479 class SafeConfigParserTestCase(ConfigParserTestCase):
    480     config_class = ConfigParser.SafeConfigParser
    481 
    482     def test_safe_interpolation(self):
    483         # See http://www.python.org/sf/511737
    484         cf = self.fromstring("[section]\n"
    485                              "option1=xxx\n"
    486                              "option2=%(option1)s/xxx\n"
    487                              "ok=%(option1)s/%%s\n"
    488                              "not_ok=%(option2)s/%%s")
    489         self.assertEqual(cf.get("section", "ok"), "xxx/%s")
    490         self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
    491 
    492     def test_set_malformatted_interpolation(self):
    493         cf = self.fromstring("[sect]\n"
    494                              "option1=foo\n")
    495 
    496         self.assertEqual(cf.get('sect', "option1"), "foo")
    497 
    498         self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
    499         self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
    500         self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
    501 
    502         self.assertEqual(cf.get('sect', "option1"), "foo")
    503 
    504         # bug #5741: double percents are *not* malformed
    505         cf.set("sect", "option2", "foo%%bar")
    506         self.assertEqual(cf.get("sect", "option2"), "foo%bar")
    507 
    508     def test_set_nonstring_types(self):
    509         cf = self.fromstring("[sect]\n"
    510                              "option1=foo\n")
    511         # Check that we get a TypeError when setting non-string values
    512         # in an existing section:
    513         self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
    514         self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
    515         self.assertRaises(TypeError, cf.set, "sect", "option1", object())
    516         self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
    517         self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
    518         self.assertRaises(TypeError, cf.set, "sect", "option2", object())
    519 
    520     def test_add_section_default_1(self):
    521         cf = self.newconfig()
    522         self.assertRaises(ValueError, cf.add_section, "default")
    523 
    524     def test_add_section_default_2(self):
    525         cf = self.newconfig()
    526         self.assertRaises(ValueError, cf.add_section, "DEFAULT")
    527 
    528 
    529 class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase):
    530     allow_no_value = True
    531 
    532 class TestChainMap(unittest.TestCase):
    533     def test_issue_12717(self):
    534         d1 = dict(red=1, green=2)
    535         d2 = dict(green=3, blue=4)
    536         dcomb = d2.copy()
    537         dcomb.update(d1)
    538         cm = ConfigParser._Chainmap(d1, d2)
    539         self.assertIsInstance(cm.keys(), list)
    540         self.assertEqual(set(cm.keys()), set(dcomb.keys()))      # keys()
    541         self.assertEqual(set(cm.values()), set(dcomb.values()))  # values()
    542         self.assertEqual(set(cm.items()), set(dcomb.items()))    # items()
    543         self.assertEqual(set(cm), set(dcomb))                    # __iter__ ()
    544         self.assertEqual(cm, dcomb)                              # __eq__()
    545         self.assertEqual([cm[k] for k in dcomb], dcomb.values()) # __getitem__()
    546         klist = 'red green blue black brown'.split()
    547         self.assertEqual([cm.get(k, 10) for k in klist],
    548                          [dcomb.get(k, 10) for k in klist])      # get()
    549         self.assertEqual([k in cm for k in klist],
    550                          [k in dcomb for k in klist])            # __contains__()
    551         with test_support.check_py3k_warnings():
    552             self.assertEqual([cm.has_key(k) for k in klist],
    553                              [dcomb.has_key(k) for k in klist])  # has_key()
    554 
    555 class Issue7005TestCase(unittest.TestCase):
    556     """Test output when None is set() as a value and allow_no_value == False.
    557 
    558     http://bugs.python.org/issue7005
    559 
    560     """
    561 
    562     expected_output = "[section]\noption = None\n\n"
    563 
    564     def prepare(self, config_class):
    565         # This is the default, but that's the point.
    566         cp = config_class(allow_no_value=False)
    567         cp.add_section("section")
    568         cp.set("section", "option", None)
    569         sio = StringIO.StringIO()
    570         cp.write(sio)
    571         return sio.getvalue()
    572 
    573     def test_none_as_value_stringified(self):
    574         output = self.prepare(ConfigParser.ConfigParser)
    575         self.assertEqual(output, self.expected_output)
    576 
    577     def test_none_as_value_stringified_raw(self):
    578         output = self.prepare(ConfigParser.RawConfigParser)
    579         self.assertEqual(output, self.expected_output)
    580 
    581 
    582 class SortedTestCase(RawConfigParserTestCase):
    583     def newconfig(self, defaults=None):
    584         self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
    585         return self.cf
    586 
    587     def test_sorted(self):
    588         self.fromstring("[b]\n"
    589                         "o4=1\n"
    590                         "o3=2\n"
    591                         "o2=3\n"
    592                         "o1=4\n"
    593                         "[a]\n"
    594                         "k=v\n")
    595         output = StringIO.StringIO()
    596         self.cf.write(output)
    597         self.assertEqual(output.getvalue(),
    598                          "[a]\n"
    599                          "k = v\n\n"
    600                          "[b]\n"
    601                          "o1 = 4\n"
    602                          "o2 = 3\n"
    603                          "o3 = 2\n"
    604                          "o4 = 1\n\n")
    605 
    606 
    607 class ExceptionPicklingTestCase(unittest.TestCase):
    608     """Tests for issue #13760: ConfigParser exceptions are not picklable."""
    609 
    610     def test_error(self):
    611         import pickle
    612         e1 = ConfigParser.Error('value')
    613         pickled = pickle.dumps(e1)
    614         e2 = pickle.loads(pickled)
    615         self.assertEqual(e1.message, e2.message)
    616         self.assertEqual(repr(e1), repr(e2))
    617 
    618     def test_nosectionerror(self):
    619         import pickle
    620         e1 = ConfigParser.NoSectionError('section')
    621         pickled = pickle.dumps(e1)
    622         e2 = pickle.loads(pickled)
    623         self.assertEqual(e1.message, e2.message)
    624         self.assertEqual(e1.args, e2.args)
    625         self.assertEqual(e1.section, e2.section)
    626         self.assertEqual(repr(e1), repr(e2))
    627 
    628     def test_nooptionerror(self):
    629         import pickle
    630         e1 = ConfigParser.NoOptionError('option', 'section')
    631         pickled = pickle.dumps(e1)
    632         e2 = pickle.loads(pickled)
    633         self.assertEqual(e1.message, e2.message)
    634         self.assertEqual(e1.args, e2.args)
    635         self.assertEqual(e1.section, e2.section)
    636         self.assertEqual(e1.option, e2.option)
    637         self.assertEqual(repr(e1), repr(e2))
    638 
    639     def test_duplicatesectionerror(self):
    640         import pickle
    641         e1 = ConfigParser.DuplicateSectionError('section')
    642         pickled = pickle.dumps(e1)
    643         e2 = pickle.loads(pickled)
    644         self.assertEqual(e1.message, e2.message)
    645         self.assertEqual(e1.args, e2.args)
    646         self.assertEqual(e1.section, e2.section)
    647         self.assertEqual(repr(e1), repr(e2))
    648 
    649     def test_interpolationerror(self):
    650         import pickle
    651         e1 = ConfigParser.InterpolationError('option', 'section', 'msg')
    652         pickled = pickle.dumps(e1)
    653         e2 = pickle.loads(pickled)
    654         self.assertEqual(e1.message, e2.message)
    655         self.assertEqual(e1.args, e2.args)
    656         self.assertEqual(e1.section, e2.section)
    657         self.assertEqual(e1.option, e2.option)
    658         self.assertEqual(repr(e1), repr(e2))
    659 
    660     def test_interpolationmissingoptionerror(self):
    661         import pickle
    662         e1 = ConfigParser.InterpolationMissingOptionError('option', 'section',
    663             'rawval', 'reference')
    664         pickled = pickle.dumps(e1)
    665         e2 = pickle.loads(pickled)
    666         self.assertEqual(e1.message, e2.message)
    667         self.assertEqual(e1.args, e2.args)
    668         self.assertEqual(e1.section, e2.section)
    669         self.assertEqual(e1.option, e2.option)
    670         self.assertEqual(e1.reference, e2.reference)
    671         self.assertEqual(repr(e1), repr(e2))
    672 
    673     def test_interpolationsyntaxerror(self):
    674         import pickle
    675         e1 = ConfigParser.InterpolationSyntaxError('option', 'section', 'msg')
    676         pickled = pickle.dumps(e1)
    677         e2 = pickle.loads(pickled)
    678         self.assertEqual(e1.message, e2.message)
    679         self.assertEqual(e1.args, e2.args)
    680         self.assertEqual(e1.section, e2.section)
    681         self.assertEqual(e1.option, e2.option)
    682         self.assertEqual(repr(e1), repr(e2))
    683 
    684     def test_interpolationdeptherror(self):
    685         import pickle
    686         e1 = ConfigParser.InterpolationDepthError('option', 'section',
    687             'rawval')
    688         pickled = pickle.dumps(e1)
    689         e2 = pickle.loads(pickled)
    690         self.assertEqual(e1.message, e2.message)
    691         self.assertEqual(e1.args, e2.args)
    692         self.assertEqual(e1.section, e2.section)
    693         self.assertEqual(e1.option, e2.option)
    694         self.assertEqual(repr(e1), repr(e2))
    695 
    696     def test_parsingerror(self):
    697         import pickle
    698         e1 = ConfigParser.ParsingError('source')
    699         e1.append(1, 'line1')
    700         e1.append(2, 'line2')
    701         e1.append(3, 'line3')
    702         pickled = pickle.dumps(e1)
    703         e2 = pickle.loads(pickled)
    704         self.assertEqual(e1.message, e2.message)
    705         self.assertEqual(e1.args, e2.args)
    706         self.assertEqual(e1.filename, e2.filename)
    707         self.assertEqual(e1.errors, e2.errors)
    708         self.assertEqual(repr(e1), repr(e2))
    709 
    710     def test_missingsectionheadererror(self):
    711         import pickle
    712         e1 = ConfigParser.MissingSectionHeaderError('filename', 123, 'line')
    713         pickled = pickle.dumps(e1)
    714         e2 = pickle.loads(pickled)
    715         self.assertEqual(e1.message, e2.message)
    716         self.assertEqual(e1.args, e2.args)
    717         self.assertEqual(e1.line, e2.line)
    718         self.assertEqual(e1.filename, e2.filename)
    719         self.assertEqual(e1.lineno, e2.lineno)
    720         self.assertEqual(repr(e1), repr(e2))
    721 
    722 
    723 def test_main():
    724     test_support.run_unittest(
    725         ConfigParserTestCase,
    726         MultilineValuesTestCase,
    727         RawConfigParserTestCase,
    728         SafeConfigParserTestCase,
    729         SafeConfigParserTestCaseNoValue,
    730         SortedTestCase,
    731         Issue7005TestCase,
    732         TestChainMap,
    733         ExceptionPicklingTestCase,
    734         )
    735 
    736 
    737 if __name__ == "__main__":
    738     test_main()
    739