blob: 66fecf1aceb38ae4602a2bf695cd1d3c636fcc0f [file] [log] [blame]
Fred Drake8ef67672000-09-27 22:45:25 +00001import ConfigParser
2import StringIO
Fred Drakec6f28912002-10-25 19:40:49 +00003import unittest
Fred Drake8ef67672000-09-27 22:45:25 +00004
Fred Drakec6f28912002-10-25 19:40:49 +00005from test import test_support
Fred Drake3d5f7e82000-12-04 16:30:40 +00006
7
Fred Drakec6f28912002-10-25 19:40:49 +00008class TestCaseBase(unittest.TestCase):
9 def newconfig(self, defaults=None):
10 if defaults is None:
11 self.cf = self.config_class()
12 else:
13 self.cf = self.config_class(defaults)
14 return self.cf
Fred Drake8ef67672000-09-27 22:45:25 +000015
Fred Drakec6f28912002-10-25 19:40:49 +000016 def fromstring(self, string, defaults=None):
17 cf = self.newconfig(defaults)
18 sio = StringIO.StringIO(string)
19 cf.readfp(sio)
20 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000021
Fred Drakec6f28912002-10-25 19:40:49 +000022 def test_basic(self):
23 cf = self.fromstring(
24 "[Foo Bar]\n"
25 "foo=bar\n"
26 "[Spacey Bar]\n"
27 "foo = bar\n"
28 "[Commented Bar]\n"
29 "foo: bar ; comment\n"
30 "[Long Line]\n"
31 "foo: this line is much, much longer than my editor\n"
32 " likes it.\n"
33 "[Section\\with$weird%characters[\t]\n"
34 "[Internationalized Stuff]\n"
35 "foo[bg]: Bulgarian\n"
36 "foo=Default\n"
37 "foo[en]=English\n"
38 "foo[de]=Deutsch\n"
39 "[Spaces]\n"
40 "key with spaces : value\n"
41 "another with spaces = splat!\n"
42 )
43 L = cf.sections()
44 L.sort()
45 eq = self.assertEqual
46 eq(L, [r'Commented Bar',
47 r'Foo Bar',
48 r'Internationalized Stuff',
49 r'Long Line',
50 r'Section\with$weird%characters[' '\t',
51 r'Spaces',
52 r'Spacey Bar',
53 ])
Fred Drake8ef67672000-09-27 22:45:25 +000054
Fred Drakec6f28912002-10-25 19:40:49 +000055 # The use of spaces in the section names serves as a
56 # regression test for SourceForge bug #583248:
57 # http://www.python.org/sf/583248
58 eq(cf.get('Foo Bar', 'foo'), 'bar')
59 eq(cf.get('Spacey Bar', 'foo'), 'bar')
60 eq(cf.get('Commented Bar', 'foo'), 'bar')
61 eq(cf.get('Spaces', 'key with spaces'), 'value')
62 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drake3d5f7e82000-12-04 16:30:40 +000063
Fred Drakec6f28912002-10-25 19:40:49 +000064 self.failIf('__name__' in cf.options("Foo Bar"),
65 '__name__ "option" should not be exposed by the API!')
66
67 # Make sure the right things happen for remove_option();
68 # added to include check for SourceForge bug #123324:
69 self.failUnless(cf.remove_option('Foo Bar', 'foo'),
70 "remove_option() failed to report existance of option")
71 self.failIf(cf.has_option('Foo Bar', 'foo'),
72 "remove_option() failed to remove option")
73 self.failIf(cf.remove_option('Foo Bar', 'foo'),
74 "remove_option() failed to report non-existance of option"
75 " that was removed")
76
77 self.assertRaises(ConfigParser.NoSectionError,
78 cf.remove_option, 'No Such Section', 'foo')
79
80 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +000081 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +000082
Fred Drakec6f28912002-10-25 19:40:49 +000083 def test_case_sensitivity(self):
84 cf = self.newconfig()
85 cf.add_section("A")
86 cf.add_section("a")
87 L = cf.sections()
88 L.sort()
89 eq = self.assertEqual
90 eq(L, ["A", "a"])
91 cf.set("a", "B", "value")
92 eq(cf.options("a"), ["b"])
93 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +000094 "could not locate option, expecting case-insensitive option names")
Fred Drakec6f28912002-10-25 19:40:49 +000095 self.failUnless(cf.has_option("a", "b"))
96 cf.set("A", "A-B", "A-B value")
97 for opt in ("a-b", "A-b", "a-B", "A-B"):
98 self.failUnless(
99 cf.has_option("A", opt),
100 "has_option() returned false for option which should exist")
101 eq(cf.options("A"), ["a-b"])
102 eq(cf.options("a"), ["b"])
103 cf.remove_option("a", "B")
104 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000105
Fred Drakec6f28912002-10-25 19:40:49 +0000106 # SF bug #432369:
107 cf = self.fromstring(
108 "[MySection]\nOption: first line\n\tsecond line\n")
109 eq(cf.options("MySection"), ["option"])
110 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000111
Fred Drakec6f28912002-10-25 19:40:49 +0000112 # SF bug #561822:
113 cf = self.fromstring("[section]\nnekey=nevalue\n",
114 defaults={"key":"value"})
115 self.failUnless(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000116
Fred Drake3c823aa2001-02-26 21:55:34 +0000117
David Goodger68a1abd2004-10-03 15:40:25 +0000118 def test_default_case_sensitivity(self):
119 cf = self.newconfig({"foo": "Bar"})
120 self.assertEqual(
121 cf.get("DEFAULT", "Foo"), "Bar",
122 "could not locate option, expecting case-insensitive option names")
123 cf = self.newconfig({"Foo": "Bar"})
124 self.assertEqual(
125 cf.get("DEFAULT", "Foo"), "Bar",
126 "could not locate option, expecting case-insensitive defaults")
127
Fred Drakec6f28912002-10-25 19:40:49 +0000128 def test_parse_errors(self):
129 self.newconfig()
130 self.parse_error(ConfigParser.ParsingError,
131 "[Foo]\n extra-spaces: splat\n")
132 self.parse_error(ConfigParser.ParsingError,
133 "[Foo]\n extra-spaces= splat\n")
134 self.parse_error(ConfigParser.ParsingError,
135 "[Foo]\noption-without-value\n")
136 self.parse_error(ConfigParser.ParsingError,
137 "[Foo]\n:value-without-option-name\n")
138 self.parse_error(ConfigParser.ParsingError,
139 "[Foo]\n=value-without-option-name\n")
140 self.parse_error(ConfigParser.MissingSectionHeaderError,
141 "No Section!\n")
Fred Drake168bead2001-10-08 17:13:12 +0000142
Fred Drakec6f28912002-10-25 19:40:49 +0000143 def parse_error(self, exc, src):
144 sio = StringIO.StringIO(src)
145 self.assertRaises(exc, self.cf.readfp, sio)
Fred Drake168bead2001-10-08 17:13:12 +0000146
Fred Drakec6f28912002-10-25 19:40:49 +0000147 def test_query_errors(self):
148 cf = self.newconfig()
149 self.assertEqual(cf.sections(), [],
150 "new ConfigParser should have no defined sections")
151 self.failIf(cf.has_section("Foo"),
152 "new ConfigParser should have no acknowledged sections")
153 self.assertRaises(ConfigParser.NoSectionError,
154 cf.options, "Foo")
155 self.assertRaises(ConfigParser.NoSectionError,
156 cf.set, "foo", "bar", "value")
157 self.get_error(ConfigParser.NoSectionError, "foo", "bar")
158 cf.add_section("foo")
159 self.get_error(ConfigParser.NoOptionError, "foo", "bar")
Fred Drake8ef67672000-09-27 22:45:25 +0000160
Fred Drakec6f28912002-10-25 19:40:49 +0000161 def get_error(self, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000162 try:
163 self.cf.get(section, option)
164 except exc, e:
165 return e
166 else:
167 self.fail("expected exception type %s.%s"
168 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000169
Fred Drakec6f28912002-10-25 19:40:49 +0000170 def test_boolean(self):
171 cf = self.fromstring(
172 "[BOOLTEST]\n"
173 "T1=1\n"
174 "T2=TRUE\n"
175 "T3=True\n"
176 "T4=oN\n"
177 "T5=yes\n"
178 "F1=0\n"
179 "F2=FALSE\n"
180 "F3=False\n"
181 "F4=oFF\n"
182 "F5=nO\n"
183 "E1=2\n"
184 "E2=foo\n"
185 "E3=-1\n"
186 "E4=0.1\n"
187 "E5=FALSE AND MORE"
188 )
189 for x in range(1, 5):
190 self.failUnless(cf.getboolean('BOOLTEST', 't%d' % x))
191 self.failIf(cf.getboolean('BOOLTEST', 'f%d' % x))
192 self.assertRaises(ValueError,
193 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000194
Fred Drakec6f28912002-10-25 19:40:49 +0000195 def test_weird_errors(self):
196 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000197 cf.add_section("Foo")
Fred Drakec6f28912002-10-25 19:40:49 +0000198 self.assertRaises(ConfigParser.DuplicateSectionError,
199 cf.add_section, "Foo")
200
201 def test_write(self):
202 cf = self.fromstring(
203 "[Long Line]\n"
204 "foo: this line is much, much longer than my editor\n"
205 " likes it.\n"
206 "[DEFAULT]\n"
207 "foo: another very\n"
208 " long line"
209 )
210 output = StringIO.StringIO()
211 cf.write(output)
212 self.assertEqual(
213 output.getvalue(),
214 "[DEFAULT]\n"
215 "foo = another very\n"
216 "\tlong line\n"
217 "\n"
218 "[Long Line]\n"
219 "foo = this line is much, much longer than my editor\n"
220 "\tlikes it.\n"
221 "\n"
222 )
223
Fred Drakeabc086f2004-05-18 03:29:52 +0000224 def test_set_string_types(self):
225 cf = self.fromstring("[sect]\n"
226 "option1=foo\n")
227 # Check that we don't get an exception when setting values in
228 # an existing section using strings:
229 class mystr(str):
230 pass
231 cf.set("sect", "option1", "splat")
232 cf.set("sect", "option1", mystr("splat"))
233 cf.set("sect", "option2", "splat")
234 cf.set("sect", "option2", mystr("splat"))
235 try:
236 unicode
237 except NameError:
238 pass
239 else:
240 cf.set("sect", "option1", unicode("splat"))
241 cf.set("sect", "option2", unicode("splat"))
242
Fred Drake82903142004-05-18 04:24:02 +0000243 def test_read_returns_file_list(self):
244 file1 = test_support.findfile("cfgparser.1")
245 # check when we pass a mix of readable and non-readable files:
246 cf = self.newconfig()
247 parsed_files = cf.read([file1, "nonexistant-file"])
248 self.assertEqual(parsed_files, [file1])
249 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
250 # check when we pass only a filename:
251 cf = self.newconfig()
252 parsed_files = cf.read(file1)
253 self.assertEqual(parsed_files, [file1])
254 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
255 # check when we pass only missing files:
256 cf = self.newconfig()
257 parsed_files = cf.read(["nonexistant-file"])
258 self.assertEqual(parsed_files, [])
259 # check when we pass no files:
260 cf = self.newconfig()
261 parsed_files = cf.read([])
262 self.assertEqual(parsed_files, [])
263
Fred Drakec6f28912002-10-25 19:40:49 +0000264 # shared by subclasses
265 def get_interpolation_config(self):
266 return self.fromstring(
267 "[Foo]\n"
268 "bar=something %(with1)s interpolation (1 step)\n"
269 "bar9=something %(with9)s lots of interpolation (9 steps)\n"
270 "bar10=something %(with10)s lots of interpolation (10 steps)\n"
271 "bar11=something %(with11)s lots of interpolation (11 steps)\n"
272 "with11=%(with10)s\n"
273 "with10=%(with9)s\n"
274 "with9=%(with8)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000275 "with8=%(With7)s\n"
276 "with7=%(WITH6)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000277 "with6=%(with5)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000278 "With5=%(with4)s\n"
279 "WITH4=%(with3)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000280 "with3=%(with2)s\n"
281 "with2=%(with1)s\n"
282 "with1=with\n"
283 "\n"
284 "[Mutual Recursion]\n"
285 "foo=%(bar)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000286 "bar=%(foo)s\n"
287 "\n"
288 "[Interpolation Error]\n"
289 "name=%(reference)s\n",
290 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000291 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000292
Fred Drake98e3b292002-10-25 20:42:44 +0000293 def check_items_config(self, expected):
294 cf = self.fromstring(
295 "[section]\n"
296 "name = value\n"
297 "key: |%(name)s| \n"
298 "getdefault: |%(default)s|\n"
299 "getname: |%(__name__)s|",
300 defaults={"default": "<default>"})
301 L = list(cf.items("section"))
302 L.sort()
303 self.assertEqual(L, expected)
304
Fred Drake8ef67672000-09-27 22:45:25 +0000305
Fred Drakec6f28912002-10-25 19:40:49 +0000306class ConfigParserTestCase(TestCaseBase):
307 config_class = ConfigParser.ConfigParser
308
309 def test_interpolation(self):
310 cf = self.get_interpolation_config()
311 eq = self.assertEqual
312 eq(cf.get("Foo", "getname"), "Foo")
313 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
314 eq(cf.get("Foo", "bar9"),
315 "something with lots of interpolation (9 steps)")
316 eq(cf.get("Foo", "bar10"),
317 "something with lots of interpolation (10 steps)")
318 self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
Fred Drake95b96d32001-02-12 17:23:20 +0000319
Fred Drake54782192002-12-31 06:57:25 +0000320 def test_interpolation_missing_value(self):
321 cf = self.get_interpolation_config()
322 e = self.get_error(ConfigParser.InterpolationError,
323 "Interpolation Error", "name")
324 self.assertEqual(e.reference, "reference")
325 self.assertEqual(e.section, "Interpolation Error")
326 self.assertEqual(e.option, "name")
327
Fred Drake98e3b292002-10-25 20:42:44 +0000328 def test_items(self):
329 self.check_items_config([('default', '<default>'),
330 ('getdefault', '|<default>|'),
331 ('getname', '|section|'),
332 ('key', '|value|'),
333 ('name', 'value')])
334
David Goodger1cbf2062004-10-03 15:55:09 +0000335 def test_set_nonstring_types(self):
336 cf = self.newconfig()
337 cf.add_section('non-string')
338 cf.set('non-string', 'int', 1)
339 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
340 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
341 '%(list)': '%(list)'})
342 cf.set('non-string', 'string_with_interpolation', '%(list)s')
343 self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
344 self.assertRaises(TypeError, cf.get, 'non-string', 'int')
345 self.assertEqual(cf.get('non-string', 'list', raw=True),
346 [0, 1, 1, 2, 3, 5, 8, 13, '%('])
347 self.assertRaises(TypeError, cf.get, 'non-string', 'list')
348 self.assertEqual(cf.get('non-string', 'dict', raw=True),
349 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
350 self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
351 self.assertEqual(cf.get('non-string', 'string_with_interpolation',
352 raw=True), '%(list)s')
353 self.assertRaises(ValueError, cf.get, 'non-string',
354 'string_with_interpolation', raw=False)
355
Fred Drake8ef67672000-09-27 22:45:25 +0000356
Fred Drakec6f28912002-10-25 19:40:49 +0000357class RawConfigParserTestCase(TestCaseBase):
358 config_class = ConfigParser.RawConfigParser
359
360 def test_interpolation(self):
361 cf = self.get_interpolation_config()
362 eq = self.assertEqual
363 eq(cf.get("Foo", "getname"), "%(__name__)s")
364 eq(cf.get("Foo", "bar"),
365 "something %(with1)s interpolation (1 step)")
366 eq(cf.get("Foo", "bar9"),
367 "something %(with9)s lots of interpolation (9 steps)")
368 eq(cf.get("Foo", "bar10"),
369 "something %(with10)s lots of interpolation (10 steps)")
370 eq(cf.get("Foo", "bar11"),
371 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000372
Fred Drake98e3b292002-10-25 20:42:44 +0000373 def test_items(self):
374 self.check_items_config([('default', '<default>'),
375 ('getdefault', '|%(default)s|'),
376 ('getname', '|%(__name__)s|'),
377 ('key', '|%(name)s|'),
378 ('name', 'value')])
379
David Goodger1cbf2062004-10-03 15:55:09 +0000380 def test_set_nonstring_types(self):
381 cf = self.newconfig()
382 cf.add_section('non-string')
383 cf.set('non-string', 'int', 1)
384 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
385 cf.set('non-string', 'dict', {'pi': 3.14159})
386 self.assertEqual(cf.get('non-string', 'int'), 1)
387 self.assertEqual(cf.get('non-string', 'list'),
388 [0, 1, 1, 2, 3, 5, 8, 13])
389 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Tim Petersab9b32c2004-10-03 18:35:19 +0000390
Fred Drake8ef67672000-09-27 22:45:25 +0000391
Fred Drake0eebd5c2002-10-25 21:52:00 +0000392class SafeConfigParserTestCase(ConfigParserTestCase):
393 config_class = ConfigParser.SafeConfigParser
394
395 def test_safe_interpolation(self):
396 # See http://www.python.org/sf/511737
397 cf = self.fromstring("[section]\n"
398 "option1=xxx\n"
399 "option2=%(option1)s/xxx\n"
400 "ok=%(option1)s/%%s\n"
401 "not_ok=%(option2)s/%%s")
402 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
403 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
404
David Goodger1cbf2062004-10-03 15:55:09 +0000405 def test_set_nonstring_types(self):
406 cf = self.fromstring("[sect]\n"
407 "option1=foo\n")
408 # Check that we get a TypeError when setting non-string values
409 # in an existing section:
410 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
411 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
412 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
413 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
414 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
415 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
416
Fred Drake0eebd5c2002-10-25 21:52:00 +0000417
Fred Drakec6f28912002-10-25 19:40:49 +0000418def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000419 test_support.run_unittest(
420 ConfigParserTestCase,
421 RawConfigParserTestCase,
422 SafeConfigParserTestCase
423 )
Fred Drake3af0eb82002-10-25 18:09:24 +0000424
Fred Drakec6f28912002-10-25 19:40:49 +0000425if __name__ == "__main__":
426 test_main()