blob: 3979f1545589d594df99e3c1b6111d5288e7db1e [file] [log] [blame]
Fred Drake8ef67672000-09-27 22:45:25 +00001import ConfigParser
2import StringIO
Fred Drakec6f28912002-10-25 19:40:49 +00003import unittest
Thomas Wouters89f507f2006-12-13 04:49:30 +00004import UserDict
Fred Drake8ef67672000-09-27 22:45:25 +00005
Fred Drakec6f28912002-10-25 19:40:49 +00006from test import test_support
Fred Drake3d5f7e82000-12-04 16:30:40 +00007
Thomas Wouters89f507f2006-12-13 04:49:30 +00008class 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())
Fred Drake3d5f7e82000-12-04 16:30:40 +000027
Fred Drakec6f28912002-10-25 19:40:49 +000028class 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
Fred Drake8ef67672000-09-27 22:45:25 +000035
Fred Drakec6f28912002-10-25 19:40:49 +000036 def fromstring(self, string, defaults=None):
37 cf = self.newconfig(defaults)
38 sio = StringIO.StringIO(string)
39 cf.readfp(sio)
40 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000041
Fred Drakec6f28912002-10-25 19:40:49 +000042 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 ])
Fred Drake8ef67672000-09-27 22:45:25 +000074
Fred Drakec6f28912002-10-25 19:40:49 +000075 # 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!')
Fred Drake3d5f7e82000-12-04 16:30:40 +000083
Fred Drakec6f28912002-10-25 19:40:49 +000084 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 existance 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-existance 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'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000101 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000102
Fred Drakec6f28912002-10-25 19:40:49 +0000103 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",
Fred Drake3c823aa2001-02-26 21:55:34 +0000114 "could not locate option, expecting case-insensitive option names")
Fred Drakec6f28912002-10-25 19:40:49 +0000115 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"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000125
Fred Drakec6f28912002-10-25 19:40:49 +0000126 # 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")
Fred Drakebeb67132001-07-06 17:22:48 +0000131
Fred Drakec6f28912002-10-25 19:40:49 +0000132 # SF bug #561822:
133 cf = self.fromstring("[section]\nnekey=nevalue\n",
134 defaults={"key":"value"})
135 self.failUnless(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000136
Fred Drake3c823aa2001-02-26 21:55:34 +0000137
David Goodger68a1abd2004-10-03 15:40:25 +0000138 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
Fred Drakec6f28912002-10-25 19:40:49 +0000148 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")
Fred Drake168bead2001-10-08 17:13:12 +0000162
Fred Drakec6f28912002-10-25 19:40:49 +0000163 def parse_error(self, exc, src):
164 sio = StringIO.StringIO(src)
165 self.assertRaises(exc, self.cf.readfp, sio)
Fred Drake168bead2001-10-08 17:13:12 +0000166
Fred Drakec6f28912002-10-25 19:40:49 +0000167 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")
Fred Drake8ef67672000-09-27 22:45:25 +0000180
Fred Drakec6f28912002-10-25 19:40:49 +0000181 def get_error(self, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000182 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__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000189
Fred Drakec6f28912002-10-25 19:40:49 +0000190 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)
Fred Drake95b96d32001-02-12 17:23:20 +0000214
Fred Drakec6f28912002-10-25 19:40:49 +0000215 def test_weird_errors(self):
216 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000217 cf.add_section("Foo")
Fred Drakec6f28912002-10-25 19:40:49 +0000218 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
Fred Drakeabc086f2004-05-18 03:29:52 +0000244 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
Fred Drake82903142004-05-18 04:24:02 +0000263 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, "nonexistant-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(["nonexistant-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
Fred Drakec6f28912002-10-25 19:40:49 +0000284 # 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"
Fred Drakebc12b012004-05-18 02:25:51 +0000295 "with8=%(With7)s\n"
296 "with7=%(WITH6)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000297 "with6=%(with5)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000298 "With5=%(with4)s\n"
299 "WITH4=%(with3)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000300 "with3=%(with2)s\n"
301 "with2=%(with1)s\n"
302 "with1=with\n"
303 "\n"
304 "[Mutual Recursion]\n"
305 "foo=%(bar)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000306 "bar=%(foo)s\n"
307 "\n"
308 "[Interpolation Error]\n"
309 "name=%(reference)s\n",
310 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000311 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000312
Fred Drake98e3b292002-10-25 20:42:44 +0000313 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
Fred Drake8ef67672000-09-27 22:45:25 +0000325
Fred Drakec6f28912002-10-25 19:40:49 +0000326class 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")
Fred Drake95b96d32001-02-12 17:23:20 +0000339
Fred Drake54782192002-12-31 06:57:25 +0000340 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
Fred Drake98e3b292002-10-25 20:42:44 +0000348 def test_items(self):
349 self.check_items_config([('default', '<default>'),
350 ('getdefault', '|<default>|'),
351 ('getname', '|section|'),
352 ('key', '|value|'),
353 ('name', 'value')])
354
David Goodger1cbf2062004-10-03 15:55:09 +0000355 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
Fred Drake8ef67672000-09-27 22:45:25 +0000376
Fred Drakec6f28912002-10-25 19:40:49 +0000377class 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)")
Fred Drake95b96d32001-02-12 17:23:20 +0000392
Fred Drake98e3b292002-10-25 20:42:44 +0000393 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
David Goodger1cbf2062004-10-03 15:55:09 +0000400 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})
Tim Petersab9b32c2004-10-03 18:35:19 +0000410
Fred Drake8ef67672000-09-27 22:45:25 +0000411
Fred Drake0eebd5c2002-10-25 21:52:00 +0000412class 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
David Goodger1cbf2062004-10-03 15:55:09 +0000425 def test_set_nonstring_types(self):
426 cf = self.fromstring("[sect]\n"
427 "option1=foo\n")
428 # Check that we get a TypeError when setting non-string values
429 # in an existing section:
430 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
431 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
432 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
433 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
434 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
435 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
436
Thomas Wouters89f507f2006-12-13 04:49:30 +0000437class SortedTestCase(RawConfigParserTestCase):
438 def newconfig(self, defaults=None):
439 self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
440 return self.cf
441
442 def test_sorted(self):
443 self.fromstring("[b]\n"
444 "o4=1\n"
445 "o3=2\n"
446 "o2=3\n"
447 "o1=4\n"
448 "[a]\n"
449 "k=v\n")
450 output = StringIO.StringIO()
451 self.cf.write(output)
452 self.assertEquals(output.getvalue(),
453 "[a]\n"
454 "k = v\n\n"
455 "[b]\n"
456 "o1 = 4\n"
457 "o2 = 3\n"
458 "o3 = 2\n"
459 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +0000460
Fred Drakec6f28912002-10-25 19:40:49 +0000461def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000462 test_support.run_unittest(
463 ConfigParserTestCase,
464 RawConfigParserTestCase,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000465 SafeConfigParserTestCase,
466 SortedTestCase
Walter Dörwald21d3a322003-05-01 17:45:56 +0000467 )
Fred Drake3af0eb82002-10-25 18:09:24 +0000468
Fred Drakec6f28912002-10-25 19:40:49 +0000469if __name__ == "__main__":
470 test_main()