blob: 229577270d4eef0256cde8d22ae47bbb7e04e8c4 [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):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000010 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000011
12 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000013 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000014
Thomas Wouters89f507f2006-12-13 04:49:30 +000015 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000016 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000017
18 def iteritems(self): return iter(self.items())
19 def iterkeys(self): return iter(self.keys())
20 __iter__ = iterkeys
21 def itervalues(self): return iter(self.values())
Fred Drake3d5f7e82000-12-04 16:30:40 +000022
Fred Drakec6f28912002-10-25 19:40:49 +000023class TestCaseBase(unittest.TestCase):
24 def newconfig(self, defaults=None):
25 if defaults is None:
26 self.cf = self.config_class()
27 else:
28 self.cf = self.config_class(defaults)
29 return self.cf
Fred Drake8ef67672000-09-27 22:45:25 +000030
Fred Drakec6f28912002-10-25 19:40:49 +000031 def fromstring(self, string, defaults=None):
32 cf = self.newconfig(defaults)
33 sio = StringIO.StringIO(string)
34 cf.readfp(sio)
35 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000036
Fred Drakec6f28912002-10-25 19:40:49 +000037 def test_basic(self):
38 cf = self.fromstring(
39 "[Foo Bar]\n"
40 "foo=bar\n"
41 "[Spacey Bar]\n"
42 "foo = bar\n"
43 "[Commented Bar]\n"
44 "foo: bar ; comment\n"
45 "[Long Line]\n"
46 "foo: this line is much, much longer than my editor\n"
47 " likes it.\n"
48 "[Section\\with$weird%characters[\t]\n"
49 "[Internationalized Stuff]\n"
50 "foo[bg]: Bulgarian\n"
51 "foo=Default\n"
52 "foo[en]=English\n"
53 "foo[de]=Deutsch\n"
54 "[Spaces]\n"
55 "key with spaces : value\n"
56 "another with spaces = splat!\n"
57 )
58 L = cf.sections()
59 L.sort()
60 eq = self.assertEqual
61 eq(L, [r'Commented Bar',
62 r'Foo Bar',
63 r'Internationalized Stuff',
64 r'Long Line',
65 r'Section\with$weird%characters[' '\t',
66 r'Spaces',
67 r'Spacey Bar',
68 ])
Fred Drake8ef67672000-09-27 22:45:25 +000069
Fred Drakec6f28912002-10-25 19:40:49 +000070 # The use of spaces in the section names serves as a
71 # regression test for SourceForge bug #583248:
72 # http://www.python.org/sf/583248
73 eq(cf.get('Foo Bar', 'foo'), 'bar')
74 eq(cf.get('Spacey Bar', 'foo'), 'bar')
75 eq(cf.get('Commented Bar', 'foo'), 'bar')
76 eq(cf.get('Spaces', 'key with spaces'), 'value')
77 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drake3d5f7e82000-12-04 16:30:40 +000078
Fred Drakec6f28912002-10-25 19:40:49 +000079 self.failIf('__name__' in cf.options("Foo Bar"),
80 '__name__ "option" should not be exposed by the API!')
81
82 # Make sure the right things happen for remove_option();
83 # added to include check for SourceForge bug #123324:
84 self.failUnless(cf.remove_option('Foo Bar', 'foo'),
85 "remove_option() failed to report existance of option")
86 self.failIf(cf.has_option('Foo Bar', 'foo'),
87 "remove_option() failed to remove option")
88 self.failIf(cf.remove_option('Foo Bar', 'foo'),
89 "remove_option() failed to report non-existance of option"
90 " that was removed")
91
92 self.assertRaises(ConfigParser.NoSectionError,
93 cf.remove_option, 'No Such Section', 'foo')
94
95 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +000096 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +000097
Fred Drakec6f28912002-10-25 19:40:49 +000098 def test_case_sensitivity(self):
99 cf = self.newconfig()
100 cf.add_section("A")
101 cf.add_section("a")
102 L = cf.sections()
103 L.sort()
104 eq = self.assertEqual
105 eq(L, ["A", "a"])
106 cf.set("a", "B", "value")
107 eq(cf.options("a"), ["b"])
108 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000109 "could not locate option, expecting case-insensitive option names")
Fred Drakec6f28912002-10-25 19:40:49 +0000110 self.failUnless(cf.has_option("a", "b"))
111 cf.set("A", "A-B", "A-B value")
112 for opt in ("a-b", "A-b", "a-B", "A-B"):
113 self.failUnless(
114 cf.has_option("A", opt),
115 "has_option() returned false for option which should exist")
116 eq(cf.options("A"), ["a-b"])
117 eq(cf.options("a"), ["b"])
118 cf.remove_option("a", "B")
119 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000120
Fred Drakec6f28912002-10-25 19:40:49 +0000121 # SF bug #432369:
122 cf = self.fromstring(
123 "[MySection]\nOption: first line\n\tsecond line\n")
124 eq(cf.options("MySection"), ["option"])
125 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000126
Fred Drakec6f28912002-10-25 19:40:49 +0000127 # SF bug #561822:
128 cf = self.fromstring("[section]\nnekey=nevalue\n",
129 defaults={"key":"value"})
130 self.failUnless(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000131
Fred Drake3c823aa2001-02-26 21:55:34 +0000132
David Goodger68a1abd2004-10-03 15:40:25 +0000133 def test_default_case_sensitivity(self):
134 cf = self.newconfig({"foo": "Bar"})
135 self.assertEqual(
136 cf.get("DEFAULT", "Foo"), "Bar",
137 "could not locate option, expecting case-insensitive option names")
138 cf = self.newconfig({"Foo": "Bar"})
139 self.assertEqual(
140 cf.get("DEFAULT", "Foo"), "Bar",
141 "could not locate option, expecting case-insensitive defaults")
142
Fred Drakec6f28912002-10-25 19:40:49 +0000143 def test_parse_errors(self):
144 self.newconfig()
145 self.parse_error(ConfigParser.ParsingError,
146 "[Foo]\n extra-spaces: splat\n")
147 self.parse_error(ConfigParser.ParsingError,
148 "[Foo]\n extra-spaces= splat\n")
149 self.parse_error(ConfigParser.ParsingError,
150 "[Foo]\noption-without-value\n")
151 self.parse_error(ConfigParser.ParsingError,
152 "[Foo]\n:value-without-option-name\n")
153 self.parse_error(ConfigParser.ParsingError,
154 "[Foo]\n=value-without-option-name\n")
155 self.parse_error(ConfigParser.MissingSectionHeaderError,
156 "No Section!\n")
Fred Drake168bead2001-10-08 17:13:12 +0000157
Fred Drakec6f28912002-10-25 19:40:49 +0000158 def parse_error(self, exc, src):
159 sio = StringIO.StringIO(src)
160 self.assertRaises(exc, self.cf.readfp, sio)
Fred Drake168bead2001-10-08 17:13:12 +0000161
Fred Drakec6f28912002-10-25 19:40:49 +0000162 def test_query_errors(self):
163 cf = self.newconfig()
164 self.assertEqual(cf.sections(), [],
165 "new ConfigParser should have no defined sections")
166 self.failIf(cf.has_section("Foo"),
167 "new ConfigParser should have no acknowledged sections")
168 self.assertRaises(ConfigParser.NoSectionError,
169 cf.options, "Foo")
170 self.assertRaises(ConfigParser.NoSectionError,
171 cf.set, "foo", "bar", "value")
172 self.get_error(ConfigParser.NoSectionError, "foo", "bar")
173 cf.add_section("foo")
174 self.get_error(ConfigParser.NoOptionError, "foo", "bar")
Fred Drake8ef67672000-09-27 22:45:25 +0000175
Fred Drakec6f28912002-10-25 19:40:49 +0000176 def get_error(self, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000177 try:
178 self.cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000179 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000180 return e
181 else:
182 self.fail("expected exception type %s.%s"
183 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000184
Fred Drakec6f28912002-10-25 19:40:49 +0000185 def test_boolean(self):
186 cf = self.fromstring(
187 "[BOOLTEST]\n"
188 "T1=1\n"
189 "T2=TRUE\n"
190 "T3=True\n"
191 "T4=oN\n"
192 "T5=yes\n"
193 "F1=0\n"
194 "F2=FALSE\n"
195 "F3=False\n"
196 "F4=oFF\n"
197 "F5=nO\n"
198 "E1=2\n"
199 "E2=foo\n"
200 "E3=-1\n"
201 "E4=0.1\n"
202 "E5=FALSE AND MORE"
203 )
204 for x in range(1, 5):
205 self.failUnless(cf.getboolean('BOOLTEST', 't%d' % x))
206 self.failIf(cf.getboolean('BOOLTEST', 'f%d' % x))
207 self.assertRaises(ValueError,
208 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000209
Fred Drakec6f28912002-10-25 19:40:49 +0000210 def test_weird_errors(self):
211 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000212 cf.add_section("Foo")
Fred Drakec6f28912002-10-25 19:40:49 +0000213 self.assertRaises(ConfigParser.DuplicateSectionError,
214 cf.add_section, "Foo")
215
216 def test_write(self):
217 cf = self.fromstring(
218 "[Long Line]\n"
219 "foo: this line is much, much longer than my editor\n"
220 " likes it.\n"
221 "[DEFAULT]\n"
222 "foo: another very\n"
223 " long line"
224 )
225 output = StringIO.StringIO()
226 cf.write(output)
227 self.assertEqual(
228 output.getvalue(),
229 "[DEFAULT]\n"
230 "foo = another very\n"
231 "\tlong line\n"
232 "\n"
233 "[Long Line]\n"
234 "foo = this line is much, much longer than my editor\n"
235 "\tlikes it.\n"
236 "\n"
237 )
238
Fred Drakeabc086f2004-05-18 03:29:52 +0000239 def test_set_string_types(self):
240 cf = self.fromstring("[sect]\n"
241 "option1=foo\n")
242 # Check that we don't get an exception when setting values in
243 # an existing section using strings:
244 class mystr(str):
245 pass
246 cf.set("sect", "option1", "splat")
247 cf.set("sect", "option1", mystr("splat"))
248 cf.set("sect", "option2", "splat")
249 cf.set("sect", "option2", mystr("splat"))
250 try:
251 unicode
252 except NameError:
253 pass
254 else:
255 cf.set("sect", "option1", unicode("splat"))
256 cf.set("sect", "option2", unicode("splat"))
257
Fred Drake82903142004-05-18 04:24:02 +0000258 def test_read_returns_file_list(self):
259 file1 = test_support.findfile("cfgparser.1")
260 # check when we pass a mix of readable and non-readable files:
261 cf = self.newconfig()
262 parsed_files = cf.read([file1, "nonexistant-file"])
263 self.assertEqual(parsed_files, [file1])
264 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
265 # check when we pass only a filename:
266 cf = self.newconfig()
267 parsed_files = cf.read(file1)
268 self.assertEqual(parsed_files, [file1])
269 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
270 # check when we pass only missing files:
271 cf = self.newconfig()
272 parsed_files = cf.read(["nonexistant-file"])
273 self.assertEqual(parsed_files, [])
274 # check when we pass no files:
275 cf = self.newconfig()
276 parsed_files = cf.read([])
277 self.assertEqual(parsed_files, [])
278
Fred Drakec6f28912002-10-25 19:40:49 +0000279 # shared by subclasses
280 def get_interpolation_config(self):
281 return self.fromstring(
282 "[Foo]\n"
283 "bar=something %(with1)s interpolation (1 step)\n"
284 "bar9=something %(with9)s lots of interpolation (9 steps)\n"
285 "bar10=something %(with10)s lots of interpolation (10 steps)\n"
286 "bar11=something %(with11)s lots of interpolation (11 steps)\n"
287 "with11=%(with10)s\n"
288 "with10=%(with9)s\n"
289 "with9=%(with8)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000290 "with8=%(With7)s\n"
291 "with7=%(WITH6)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000292 "with6=%(with5)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000293 "With5=%(with4)s\n"
294 "WITH4=%(with3)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000295 "with3=%(with2)s\n"
296 "with2=%(with1)s\n"
297 "with1=with\n"
298 "\n"
299 "[Mutual Recursion]\n"
300 "foo=%(bar)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000301 "bar=%(foo)s\n"
302 "\n"
303 "[Interpolation Error]\n"
304 "name=%(reference)s\n",
305 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000306 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000307
Fred Drake98e3b292002-10-25 20:42:44 +0000308 def check_items_config(self, expected):
309 cf = self.fromstring(
310 "[section]\n"
311 "name = value\n"
312 "key: |%(name)s| \n"
313 "getdefault: |%(default)s|\n"
314 "getname: |%(__name__)s|",
315 defaults={"default": "<default>"})
316 L = list(cf.items("section"))
317 L.sort()
318 self.assertEqual(L, expected)
319
Fred Drake8ef67672000-09-27 22:45:25 +0000320
Fred Drakec6f28912002-10-25 19:40:49 +0000321class ConfigParserTestCase(TestCaseBase):
322 config_class = ConfigParser.ConfigParser
323
324 def test_interpolation(self):
325 cf = self.get_interpolation_config()
326 eq = self.assertEqual
327 eq(cf.get("Foo", "getname"), "Foo")
328 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
329 eq(cf.get("Foo", "bar9"),
330 "something with lots of interpolation (9 steps)")
331 eq(cf.get("Foo", "bar10"),
332 "something with lots of interpolation (10 steps)")
333 self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
Fred Drake95b96d32001-02-12 17:23:20 +0000334
Fred Drake54782192002-12-31 06:57:25 +0000335 def test_interpolation_missing_value(self):
336 cf = self.get_interpolation_config()
337 e = self.get_error(ConfigParser.InterpolationError,
338 "Interpolation Error", "name")
339 self.assertEqual(e.reference, "reference")
340 self.assertEqual(e.section, "Interpolation Error")
341 self.assertEqual(e.option, "name")
342
Fred Drake98e3b292002-10-25 20:42:44 +0000343 def test_items(self):
344 self.check_items_config([('default', '<default>'),
345 ('getdefault', '|<default>|'),
346 ('getname', '|section|'),
347 ('key', '|value|'),
348 ('name', 'value')])
349
David Goodger1cbf2062004-10-03 15:55:09 +0000350 def test_set_nonstring_types(self):
351 cf = self.newconfig()
352 cf.add_section('non-string')
353 cf.set('non-string', 'int', 1)
354 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
355 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
356 '%(list)': '%(list)'})
357 cf.set('non-string', 'string_with_interpolation', '%(list)s')
358 self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
359 self.assertRaises(TypeError, cf.get, 'non-string', 'int')
360 self.assertEqual(cf.get('non-string', 'list', raw=True),
361 [0, 1, 1, 2, 3, 5, 8, 13, '%('])
362 self.assertRaises(TypeError, cf.get, 'non-string', 'list')
363 self.assertEqual(cf.get('non-string', 'dict', raw=True),
364 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
365 self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
366 self.assertEqual(cf.get('non-string', 'string_with_interpolation',
367 raw=True), '%(list)s')
368 self.assertRaises(ValueError, cf.get, 'non-string',
369 'string_with_interpolation', raw=False)
370
Fred Drake8ef67672000-09-27 22:45:25 +0000371
Fred Drakec6f28912002-10-25 19:40:49 +0000372class RawConfigParserTestCase(TestCaseBase):
373 config_class = ConfigParser.RawConfigParser
374
375 def test_interpolation(self):
376 cf = self.get_interpolation_config()
377 eq = self.assertEqual
378 eq(cf.get("Foo", "getname"), "%(__name__)s")
379 eq(cf.get("Foo", "bar"),
380 "something %(with1)s interpolation (1 step)")
381 eq(cf.get("Foo", "bar9"),
382 "something %(with9)s lots of interpolation (9 steps)")
383 eq(cf.get("Foo", "bar10"),
384 "something %(with10)s lots of interpolation (10 steps)")
385 eq(cf.get("Foo", "bar11"),
386 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000387
Fred Drake98e3b292002-10-25 20:42:44 +0000388 def test_items(self):
389 self.check_items_config([('default', '<default>'),
390 ('getdefault', '|%(default)s|'),
391 ('getname', '|%(__name__)s|'),
392 ('key', '|%(name)s|'),
393 ('name', 'value')])
394
David Goodger1cbf2062004-10-03 15:55:09 +0000395 def test_set_nonstring_types(self):
396 cf = self.newconfig()
397 cf.add_section('non-string')
398 cf.set('non-string', 'int', 1)
399 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
400 cf.set('non-string', 'dict', {'pi': 3.14159})
401 self.assertEqual(cf.get('non-string', 'int'), 1)
402 self.assertEqual(cf.get('non-string', 'list'),
403 [0, 1, 1, 2, 3, 5, 8, 13])
404 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Tim Petersab9b32c2004-10-03 18:35:19 +0000405
Fred Drake8ef67672000-09-27 22:45:25 +0000406
Fred Drake0eebd5c2002-10-25 21:52:00 +0000407class SafeConfigParserTestCase(ConfigParserTestCase):
408 config_class = ConfigParser.SafeConfigParser
409
410 def test_safe_interpolation(self):
411 # See http://www.python.org/sf/511737
412 cf = self.fromstring("[section]\n"
413 "option1=xxx\n"
414 "option2=%(option1)s/xxx\n"
415 "ok=%(option1)s/%%s\n"
416 "not_ok=%(option2)s/%%s")
417 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
418 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
419
David Goodger1cbf2062004-10-03 15:55:09 +0000420 def test_set_nonstring_types(self):
421 cf = self.fromstring("[sect]\n"
422 "option1=foo\n")
423 # Check that we get a TypeError when setting non-string values
424 # in an existing section:
425 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
426 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
427 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
428 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
429 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
430 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
431
Thomas Wouters89f507f2006-12-13 04:49:30 +0000432class SortedTestCase(RawConfigParserTestCase):
433 def newconfig(self, defaults=None):
434 self.cf = self.config_class(defaults=defaults, dict_type=SortedDict)
435 return self.cf
436
437 def test_sorted(self):
438 self.fromstring("[b]\n"
439 "o4=1\n"
440 "o3=2\n"
441 "o2=3\n"
442 "o1=4\n"
443 "[a]\n"
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000444 "k=v\n")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000445 output = StringIO.StringIO()
446 self.cf.write(output)
447 self.assertEquals(output.getvalue(),
448 "[a]\n"
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000449 "k = v\n\n"
Thomas Wouters89f507f2006-12-13 04:49:30 +0000450 "[b]\n"
451 "o1 = 4\n"
452 "o2 = 3\n"
453 "o3 = 2\n"
454 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +0000455
Fred Drakec6f28912002-10-25 19:40:49 +0000456def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000457 test_support.run_unittest(
458 ConfigParserTestCase,
459 RawConfigParserTestCase,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000460 SafeConfigParserTestCase,
461 SortedTestCase
Walter Dörwald21d3a322003-05-01 17:45:56 +0000462 )
Fred Drake3af0eb82002-10-25 18:09:24 +0000463
Fred Drakec6f28912002-10-25 19:40:49 +0000464if __name__ == "__main__":
465 test_main()