Issue #7051: Clarify behaviour of 'g' and 'G'-style formatting.
[python.git] / Lib / test / test_cfgparser.py
blobb99ddc12648f3419d2ad3f9a1cae2927cfe68ab4
1 import ConfigParser
2 import StringIO
3 import unittest
4 import UserDict
6 from test import test_support
8 class SortedDict(UserDict.UserDict):
9 def items(self):
10 result = self.data.items()
11 result.sort()
12 return result
14 def keys(self):
15 result = self.data.keys()
16 result.sort()
17 return result
19 def values(self):
20 result = self.items()
21 return [i[1] for i in values]
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())
28 class 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
36 def fromstring(self, string, defaults=None):
37 cf = self.newconfig(defaults)
38 sio = StringIO.StringIO(string)
39 cf.readfp(sio)
40 return cf
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"
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',
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!')
84 self.assertFalse('__name__' in cf.options("Foo Bar"),
85 '__name__ "option" should not be exposed by the API!')
87 # Make sure the right things happen for remove_option();
88 # added to include check for SourceForge bug #123324:
89 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
90 "remove_option() failed to report existence of option")
91 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
92 "remove_option() failed to remove option")
93 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
94 "remove_option() failed to report non-existence of option"
95 " that was removed")
97 self.assertRaises(ConfigParser.NoSectionError,
98 cf.remove_option, 'No Such Section', 'foo')
100 eq(cf.get('Long Line', 'foo'),
101 'this line is much, much longer than my editor\nlikes it.')
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.assertTrue(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.assertTrue(
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"), [])
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")
132 # SF bug #561822:
133 cf = self.fromstring("[section]\nnekey=nevalue\n",
134 defaults={"key":"value"})
135 self.assertTrue(cf.has_option("section", "Key"))
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")
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")
163 def parse_error(self, exc, src):
164 sio = StringIO.StringIO(src)
165 self.assertRaises(exc, self.cf.readfp, sio)
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.assertFalse(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")
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__))
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"
209 for x in range(1, 5):
210 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
211 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
212 self.assertRaises(ValueError,
213 cf.getboolean, 'BOOLTEST', 'e%d' % x)
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")
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"
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"
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"))
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, [])
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"})
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)
326 class ConfigParserTestCase(TestCaseBase):
327 config_class = ConfigParser.ConfigParser
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")
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")
348 def test_items(self):
349 self.check_items_config([('default', '<default>'),
350 ('getdefault', '|<default>|'),
351 ('getname', '|section|'),
352 ('key', '|value|'),
353 ('name', 'value')])
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)
377 class RawConfigParserTestCase(TestCaseBase):
378 config_class = ConfigParser.RawConfigParser
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)")
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')])
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})
412 class SafeConfigParserTestCase(ConfigParserTestCase):
413 config_class = ConfigParser.SafeConfigParser
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")
425 def test_set_malformatted_interpolation(self):
426 cf = self.fromstring("[sect]\n"
427 "option1=foo\n")
429 self.assertEqual(cf.get('sect', "option1"), "foo")
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")
435 self.assertEqual(cf.get('sect', "option1"), "foo")
437 # bug #5741: double percents are *not* malformed
438 cf.set("sect", "option2", "foo%%bar")
439 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
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())
453 def test_add_section_default_1(self):
454 cf = self.newconfig()
455 self.assertRaises(ValueError, cf.add_section, "default")
457 def test_add_section_default_2(self):
458 cf = self.newconfig()
459 self.assertRaises(ValueError, cf.add_section, "DEFAULT")
461 class SortedTestCase(RawConfigParserTestCase):
462 def newconfig(self, defaults=None):
463 self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
464 return self.cf
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")
485 def test_main():
486 test_support.run_unittest(
487 ConfigParserTestCase,
488 RawConfigParserTestCase,
489 SafeConfigParserTestCase,
490 SortedTestCase
493 if __name__ == "__main__":
494 test_main()