PageRenderTime 168ms CodeModel.GetById 61ms app.highlight 73ms RepoModel.GetById 27ms app.codeStats 1ms

/Lib/test/test_cfgparser.py

http://unladen-swallow.googlecode.com/
Python | 494 lines | 420 code | 55 blank | 19 comment | 9 complexity | 090eb2697868b83fbf63f38a04a055aa MD5 | raw file
  1import ConfigParser
  2import StringIO
  3import unittest
  4import UserDict
  5
  6from test import test_support
  7
  8class SortedDict(UserDict.UserDict):
  9    def items(self):
 10        result = self.data.items()
 11        result.sort()
 12        return result
 13
 14    def keys(self):
 15        result = self.data.keys()
 16        result.sort()
 17        return result
 18
 19    def values(self):
 20        result = self.items()
 21        return [i[1] for i in values]
 22
 23    def iteritems(self): return iter(self.items())
 24    def iterkeys(self): return iter(self.keys())
 25    __iter__ = iterkeys
 26    def itervalues(self): return iter(self.values())
 27
 28class TestCaseBase(unittest.TestCase):
 29    def newconfig(self, defaults=None):
 30        if defaults is None:
 31            self.cf = self.config_class()
 32        else:
 33            self.cf = self.config_class(defaults)
 34        return self.cf
 35
 36    def fromstring(self, string, defaults=None):
 37        cf = self.newconfig(defaults)
 38        sio = StringIO.StringIO(string)
 39        cf.readfp(sio)
 40        return cf
 41
 42    def test_basic(self):
 43        cf = self.fromstring(
 44            "[Foo Bar]\n"
 45            "foo=bar\n"
 46            "[Spacey Bar]\n"
 47            "foo = bar\n"
 48            "[Commented Bar]\n"
 49            "foo: bar ; comment\n"
 50            "[Long Line]\n"
 51            "foo: this line is much, much longer than my editor\n"
 52            "   likes it.\n"
 53            "[Section\\with$weird%characters[\t]\n"
 54            "[Internationalized Stuff]\n"
 55            "foo[bg]: Bulgarian\n"
 56            "foo=Default\n"
 57            "foo[en]=English\n"
 58            "foo[de]=Deutsch\n"
 59            "[Spaces]\n"
 60            "key with spaces : value\n"
 61            "another with spaces = splat!\n"
 62            )
 63        L = cf.sections()
 64        L.sort()
 65        eq = self.assertEqual
 66        eq(L, [r'Commented Bar',
 67               r'Foo Bar',
 68               r'Internationalized Stuff',
 69               r'Long Line',
 70               r'Section\with$weird%characters[' '\t',
 71               r'Spaces',
 72               r'Spacey Bar',
 73               ])
 74
 75        # The use of spaces in the section names serves as a
 76        # regression test for SourceForge bug #583248:
 77        # http://www.python.org/sf/583248
 78        eq(cf.get('Foo Bar', 'foo'), 'bar')
 79        eq(cf.get('Spacey Bar', 'foo'), 'bar')
 80        eq(cf.get('Commented Bar', 'foo'), 'bar')
 81        eq(cf.get('Spaces', 'key with spaces'), 'value')
 82        eq(cf.get('Spaces', 'another with spaces'), 'splat!')
 83
 84        self.failIf('__name__' in cf.options("Foo Bar"),
 85                    '__name__ "option" should not be exposed by the API!')
 86
 87        # Make sure the right things happen for remove_option();
 88        # added to include check for SourceForge bug #123324:
 89        self.failUnless(cf.remove_option('Foo Bar', 'foo'),
 90                        "remove_option() failed to report existence of option")
 91        self.failIf(cf.has_option('Foo Bar', 'foo'),
 92                    "remove_option() failed to remove option")
 93        self.failIf(cf.remove_option('Foo Bar', 'foo'),
 94                    "remove_option() failed to report non-existence of option"
 95                    " that was removed")
 96
 97        self.assertRaises(ConfigParser.NoSectionError,
 98                          cf.remove_option, 'No Such Section', 'foo')
 99
100        eq(cf.get('Long Line', 'foo'),
101           'this line is much, much longer than my editor\nlikes it.')
102
103    def test_case_sensitivity(self):
104        cf = self.newconfig()
105        cf.add_section("A")
106        cf.add_section("a")
107        L = cf.sections()
108        L.sort()
109        eq = self.assertEqual
110        eq(L, ["A", "a"])
111        cf.set("a", "B", "value")
112        eq(cf.options("a"), ["b"])
113        eq(cf.get("a", "b"), "value",
114           "could not locate option, expecting case-insensitive option names")
115        self.failUnless(cf.has_option("a", "b"))
116        cf.set("A", "A-B", "A-B value")
117        for opt in ("a-b", "A-b", "a-B", "A-B"):
118            self.failUnless(
119                cf.has_option("A", opt),
120                "has_option() returned false for option which should exist")
121        eq(cf.options("A"), ["a-b"])
122        eq(cf.options("a"), ["b"])
123        cf.remove_option("a", "B")
124        eq(cf.options("a"), [])
125
126        # SF bug #432369:
127        cf = self.fromstring(
128            "[MySection]\nOption: first line\n\tsecond line\n")
129        eq(cf.options("MySection"), ["option"])
130        eq(cf.get("MySection", "Option"), "first line\nsecond line")
131
132        # SF bug #561822:
133        cf = self.fromstring("[section]\nnekey=nevalue\n",
134                             defaults={"key":"value"})
135        self.failUnless(cf.has_option("section", "Key"))
136
137
138    def test_default_case_sensitivity(self):
139        cf = self.newconfig({"foo": "Bar"})
140        self.assertEqual(
141            cf.get("DEFAULT", "Foo"), "Bar",
142            "could not locate option, expecting case-insensitive option names")
143        cf = self.newconfig({"Foo": "Bar"})
144        self.assertEqual(
145            cf.get("DEFAULT", "Foo"), "Bar",
146            "could not locate option, expecting case-insensitive defaults")
147
148    def test_parse_errors(self):
149        self.newconfig()
150        self.parse_error(ConfigParser.ParsingError,
151                         "[Foo]\n  extra-spaces: splat\n")
152        self.parse_error(ConfigParser.ParsingError,
153                         "[Foo]\n  extra-spaces= splat\n")
154        self.parse_error(ConfigParser.ParsingError,
155                         "[Foo]\noption-without-value\n")
156        self.parse_error(ConfigParser.ParsingError,
157                         "[Foo]\n:value-without-option-name\n")
158        self.parse_error(ConfigParser.ParsingError,
159                         "[Foo]\n=value-without-option-name\n")
160        self.parse_error(ConfigParser.MissingSectionHeaderError,
161                         "No Section!\n")
162
163    def parse_error(self, exc, src):
164        sio = StringIO.StringIO(src)
165        self.assertRaises(exc, self.cf.readfp, sio)
166
167    def test_query_errors(self):
168        cf = self.newconfig()
169        self.assertEqual(cf.sections(), [],
170                         "new ConfigParser should have no defined sections")
171        self.failIf(cf.has_section("Foo"),
172                    "new ConfigParser should have no acknowledged sections")
173        self.assertRaises(ConfigParser.NoSectionError,
174                          cf.options, "Foo")
175        self.assertRaises(ConfigParser.NoSectionError,
176                          cf.set, "foo", "bar", "value")
177        self.get_error(ConfigParser.NoSectionError, "foo", "bar")
178        cf.add_section("foo")
179        self.get_error(ConfigParser.NoOptionError, "foo", "bar")
180
181    def get_error(self, exc, section, option):
182        try:
183            self.cf.get(section, option)
184        except exc, e:
185            return e
186        else:
187            self.fail("expected exception type %s.%s"
188                      % (exc.__module__, exc.__name__))
189
190    def test_boolean(self):
191        cf = self.fromstring(
192            "[BOOLTEST]\n"
193            "T1=1\n"
194            "T2=TRUE\n"
195            "T3=True\n"
196            "T4=oN\n"
197            "T5=yes\n"
198            "F1=0\n"
199            "F2=FALSE\n"
200            "F3=False\n"
201            "F4=oFF\n"
202            "F5=nO\n"
203            "E1=2\n"
204            "E2=foo\n"
205            "E3=-1\n"
206            "E4=0.1\n"
207            "E5=FALSE AND MORE"
208            )
209        for x in range(1, 5):
210            self.failUnless(cf.getboolean('BOOLTEST', 't%d' % x))
211            self.failIf(cf.getboolean('BOOLTEST', 'f%d' % x))
212            self.assertRaises(ValueError,
213                              cf.getboolean, 'BOOLTEST', 'e%d' % x)
214
215    def test_weird_errors(self):
216        cf = self.newconfig()
217        cf.add_section("Foo")
218        self.assertRaises(ConfigParser.DuplicateSectionError,
219                          cf.add_section, "Foo")
220
221    def test_write(self):
222        cf = self.fromstring(
223            "[Long Line]\n"
224            "foo: this line is much, much longer than my editor\n"
225            "   likes it.\n"
226            "[DEFAULT]\n"
227            "foo: another very\n"
228            " long line"
229            )
230        output = StringIO.StringIO()
231        cf.write(output)
232        self.assertEqual(
233            output.getvalue(),
234            "[DEFAULT]\n"
235            "foo = another very\n"
236            "\tlong line\n"
237            "\n"
238            "[Long Line]\n"
239            "foo = this line is much, much longer than my editor\n"
240            "\tlikes it.\n"
241            "\n"
242            )
243
244    def test_set_string_types(self):
245        cf = self.fromstring("[sect]\n"
246                             "option1=foo\n")
247        # Check that we don't get an exception when setting values in
248        # an existing section using strings:
249        class mystr(str):
250            pass
251        cf.set("sect", "option1", "splat")
252        cf.set("sect", "option1", mystr("splat"))
253        cf.set("sect", "option2", "splat")
254        cf.set("sect", "option2", mystr("splat"))
255        try:
256            unicode
257        except NameError:
258            pass
259        else:
260            cf.set("sect", "option1", unicode("splat"))
261            cf.set("sect", "option2", unicode("splat"))
262
263    def test_read_returns_file_list(self):
264        file1 = test_support.findfile("cfgparser.1")
265        # check when we pass a mix of readable and non-readable files:
266        cf = self.newconfig()
267        parsed_files = cf.read([file1, "nonexistent-file"])
268        self.assertEqual(parsed_files, [file1])
269        self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
270        # check when we pass only a filename:
271        cf = self.newconfig()
272        parsed_files = cf.read(file1)
273        self.assertEqual(parsed_files, [file1])
274        self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
275        # check when we pass only missing files:
276        cf = self.newconfig()
277        parsed_files = cf.read(["nonexistent-file"])
278        self.assertEqual(parsed_files, [])
279        # check when we pass no files:
280        cf = self.newconfig()
281        parsed_files = cf.read([])
282        self.assertEqual(parsed_files, [])
283
284    # shared by subclasses
285    def get_interpolation_config(self):
286        return self.fromstring(
287            "[Foo]\n"
288            "bar=something %(with1)s interpolation (1 step)\n"
289            "bar9=something %(with9)s lots of interpolation (9 steps)\n"
290            "bar10=something %(with10)s lots of interpolation (10 steps)\n"
291            "bar11=something %(with11)s lots of interpolation (11 steps)\n"
292            "with11=%(with10)s\n"
293            "with10=%(with9)s\n"
294            "with9=%(with8)s\n"
295            "with8=%(With7)s\n"
296            "with7=%(WITH6)s\n"
297            "with6=%(with5)s\n"
298            "With5=%(with4)s\n"
299            "WITH4=%(with3)s\n"
300            "with3=%(with2)s\n"
301            "with2=%(with1)s\n"
302            "with1=with\n"
303            "\n"
304            "[Mutual Recursion]\n"
305            "foo=%(bar)s\n"
306            "bar=%(foo)s\n"
307            "\n"
308            "[Interpolation Error]\n"
309            "name=%(reference)s\n",
310            # no definition for 'reference'
311            defaults={"getname": "%(__name__)s"})
312
313    def check_items_config(self, expected):
314        cf = self.fromstring(
315            "[section]\n"
316            "name = value\n"
317            "key: |%(name)s| \n"
318            "getdefault: |%(default)s|\n"
319            "getname: |%(__name__)s|",
320            defaults={"default": "<default>"})
321        L = list(cf.items("section"))
322        L.sort()
323        self.assertEqual(L, expected)
324
325
326class ConfigParserTestCase(TestCaseBase):
327    config_class = ConfigParser.ConfigParser
328
329    def test_interpolation(self):
330        cf = self.get_interpolation_config()
331        eq = self.assertEqual
332        eq(cf.get("Foo", "getname"), "Foo")
333        eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
334        eq(cf.get("Foo", "bar9"),
335           "something with lots of interpolation (9 steps)")
336        eq(cf.get("Foo", "bar10"),
337           "something with lots of interpolation (10 steps)")
338        self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
339
340    def test_interpolation_missing_value(self):
341        cf = self.get_interpolation_config()
342        e = self.get_error(ConfigParser.InterpolationError,
343                           "Interpolation Error", "name")
344        self.assertEqual(e.reference, "reference")
345        self.assertEqual(e.section, "Interpolation Error")
346        self.assertEqual(e.option, "name")
347
348    def test_items(self):
349        self.check_items_config([('default', '<default>'),
350                                 ('getdefault', '|<default>|'),
351                                 ('getname', '|section|'),
352                                 ('key', '|value|'),
353                                 ('name', 'value')])
354
355    def test_set_nonstring_types(self):
356        cf = self.newconfig()
357        cf.add_section('non-string')
358        cf.set('non-string', 'int', 1)
359        cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
360        cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
361                                      '%(list)': '%(list)'})
362        cf.set('non-string', 'string_with_interpolation', '%(list)s')
363        self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
364        self.assertRaises(TypeError, cf.get, 'non-string', 'int')
365        self.assertEqual(cf.get('non-string', 'list', raw=True),
366                         [0, 1, 1, 2, 3, 5, 8, 13, '%('])
367        self.assertRaises(TypeError, cf.get, 'non-string', 'list')
368        self.assertEqual(cf.get('non-string', 'dict', raw=True),
369                         {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
370        self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
371        self.assertEqual(cf.get('non-string', 'string_with_interpolation',
372                                raw=True), '%(list)s')
373        self.assertRaises(ValueError, cf.get, 'non-string',
374                          'string_with_interpolation', raw=False)
375
376
377class RawConfigParserTestCase(TestCaseBase):
378    config_class = ConfigParser.RawConfigParser
379
380    def test_interpolation(self):
381        cf = self.get_interpolation_config()
382        eq = self.assertEqual
383        eq(cf.get("Foo", "getname"), "%(__name__)s")
384        eq(cf.get("Foo", "bar"),
385           "something %(with1)s interpolation (1 step)")
386        eq(cf.get("Foo", "bar9"),
387           "something %(with9)s lots of interpolation (9 steps)")
388        eq(cf.get("Foo", "bar10"),
389           "something %(with10)s lots of interpolation (10 steps)")
390        eq(cf.get("Foo", "bar11"),
391           "something %(with11)s lots of interpolation (11 steps)")
392
393    def test_items(self):
394        self.check_items_config([('default', '<default>'),
395                                 ('getdefault', '|%(default)s|'),
396                                 ('getname', '|%(__name__)s|'),
397                                 ('key', '|%(name)s|'),
398                                 ('name', 'value')])
399
400    def test_set_nonstring_types(self):
401        cf = self.newconfig()
402        cf.add_section('non-string')
403        cf.set('non-string', 'int', 1)
404        cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
405        cf.set('non-string', 'dict', {'pi': 3.14159})
406        self.assertEqual(cf.get('non-string', 'int'), 1)
407        self.assertEqual(cf.get('non-string', 'list'),
408                         [0, 1, 1, 2, 3, 5, 8, 13])
409        self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
410
411
412class SafeConfigParserTestCase(ConfigParserTestCase):
413    config_class = ConfigParser.SafeConfigParser
414
415    def test_safe_interpolation(self):
416        # See http://www.python.org/sf/511737
417        cf = self.fromstring("[section]\n"
418                             "option1=xxx\n"
419                             "option2=%(option1)s/xxx\n"
420                             "ok=%(option1)s/%%s\n"
421                             "not_ok=%(option2)s/%%s")
422        self.assertEqual(cf.get("section", "ok"), "xxx/%s")
423        self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
424
425    def test_set_malformatted_interpolation(self):
426        cf = self.fromstring("[sect]\n"
427                             "option1=foo\n")
428
429        self.assertEqual(cf.get('sect', "option1"), "foo")
430
431        self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
432        self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
433        self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
434
435        self.assertEqual(cf.get('sect', "option1"), "foo")
436
437        # bug #5741: double percents are *not* malformed
438        cf.set("sect", "option2", "foo%%bar")
439        self.assertEqual(cf.get("sect", "option2"), "foo%bar")
440
441    def test_set_nonstring_types(self):
442        cf = self.fromstring("[sect]\n"
443                             "option1=foo\n")
444        # Check that we get a TypeError when setting non-string values
445        # in an existing section:
446        self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
447        self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
448        self.assertRaises(TypeError, cf.set, "sect", "option1", object())
449        self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
450        self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
451        self.assertRaises(TypeError, cf.set, "sect", "option2", object())
452
453    def test_add_section_default_1(self):
454        cf = self.newconfig()
455        self.assertRaises(ValueError, cf.add_section, "default")
456
457    def test_add_section_default_2(self):
458        cf = self.newconfig()
459        self.assertRaises(ValueError, cf.add_section, "DEFAULT")
460
461class SortedTestCase(RawConfigParserTestCase):
462    def newconfig(self, defaults=None):
463        self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
464        return self.cf
465
466    def test_sorted(self):
467        self.fromstring("[b]\n"
468                        "o4=1\n"
469                        "o3=2\n"
470                        "o2=3\n"
471                        "o1=4\n"
472                        "[a]\n"
473                        "k=v\n")
474        output = StringIO.StringIO()
475        self.cf.write(output)
476        self.assertEquals(output.getvalue(),
477                          "[a]\n"
478                          "k = v\n\n"
479                          "[b]\n"
480                          "o1 = 4\n"
481                          "o2 = 3\n"
482                          "o3 = 2\n"
483                          "o4 = 1\n\n")
484
485def test_main():
486    test_support.run_unittest(
487        ConfigParserTestCase,
488        RawConfigParserTestCase,
489        SafeConfigParserTestCase,
490        SortedTestCase
491    )
492
493if __name__ == "__main__":
494    test_main()