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 
    533 class Issue7005TestCase(unittest.TestCase):
    534     """Test output when None is set() as a value and allow_no_value == False.
    535 
    536     http://bugs.python.org/issue7005
    537 
    538     """
    539 
    540     expected_output = "[section]\noption = None\n\n"
    541 
    542     def prepare(self, config_class):
    543         # This is the default, but that's the point.

    544         cp = config_class(allow_no_value=False)
    545         cp.add_section("section")
    546         cp.set("section", "option", None)
    547         sio = StringIO.StringIO()
    548         cp.write(sio)
    549         return sio.getvalue()
    550 
    551     def test_none_as_value_stringified(self):
    552         output = self.prepare(ConfigParser.ConfigParser)
    553         self.assertEqual(output, self.expected_output)
    554 
    555     def test_none_as_value_stringified_raw(self):
    556         output = self.prepare(ConfigParser.RawConfigParser)
    557         self.assertEqual(output, self.expected_output)
    558 
    559 
    560 class SortedTestCase(RawConfigParserTestCase):
    561     def newconfig(self, defaults=None):
    562         self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
    563         return self.cf
    564 
    565     def test_sorted(self):
    566         self.fromstring("[b]\n"
    567                         "o4=1\n"
    568                         "o3=2\n"
    569                         "o2=3\n"
    570                         "o1=4\n"
    571                         "[a]\n"
    572                         "k=v\n")
    573         output = StringIO.StringIO()
    574         self.cf.write(output)
    575         self.assertEqual(output.getvalue(),
    576                          "[a]\n"
    577                          "k = v\n\n"
    578                          "[b]\n"
    579                          "o1 = 4\n"
    580                          "o2 = 3\n"
    581                          "o3 = 2\n"
    582                          "o4 = 1\n\n")
    583 
    584 
    585 def test_main():
    586     test_support.run_unittest(
    587         ConfigParserTestCase,
    588         MultilineValuesTestCase,
    589         RawConfigParserTestCase,
    590         SafeConfigParserTestCase,
    591         SafeConfigParserTestCaseNoValue,
    592         SortedTestCase,
    593         Issue7005TestCase,
    594         )
    595 
    596 
    597 if __name__ == "__main__":
    598     test_main()
    599