blob: 6b3e68a6d16430f50e8f5c1f77d62fb6916b06aa [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
243 def test_set_nonstring_types(self):
244 cf = self.fromstring("[sect]\n"
245 "option1=foo\n")
246 # Check that we get a TypeError when setting non-string values
247 # in an existing section:
248 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
249 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
250 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
251 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
252 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
253 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
254
Fred Drake82903142004-05-18 04:24:02 +0000255 def test_read_returns_file_list(self):
256 file1 = test_support.findfile("cfgparser.1")
257 # check when we pass a mix of readable and non-readable files:
258 cf = self.newconfig()
259 parsed_files = cf.read([file1, "nonexistant-file"])
260 self.assertEqual(parsed_files, [file1])
261 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
262 # check when we pass only a filename:
263 cf = self.newconfig()
264 parsed_files = cf.read(file1)
265 self.assertEqual(parsed_files, [file1])
266 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
267 # check when we pass only missing files:
268 cf = self.newconfig()
269 parsed_files = cf.read(["nonexistant-file"])
270 self.assertEqual(parsed_files, [])
271 # check when we pass no files:
272 cf = self.newconfig()
273 parsed_files = cf.read([])
274 self.assertEqual(parsed_files, [])
275
Fred Drakec6f28912002-10-25 19:40:49 +0000276 # shared by subclasses
277 def get_interpolation_config(self):
278 return self.fromstring(
279 "[Foo]\n"
280 "bar=something %(with1)s interpolation (1 step)\n"
281 "bar9=something %(with9)s lots of interpolation (9 steps)\n"
282 "bar10=something %(with10)s lots of interpolation (10 steps)\n"
283 "bar11=something %(with11)s lots of interpolation (11 steps)\n"
284 "with11=%(with10)s\n"
285 "with10=%(with9)s\n"
286 "with9=%(with8)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000287 "with8=%(With7)s\n"
288 "with7=%(WITH6)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000289 "with6=%(with5)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000290 "With5=%(with4)s\n"
291 "WITH4=%(with3)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000292 "with3=%(with2)s\n"
293 "with2=%(with1)s\n"
294 "with1=with\n"
295 "\n"
296 "[Mutual Recursion]\n"
297 "foo=%(bar)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000298 "bar=%(foo)s\n"
299 "\n"
300 "[Interpolation Error]\n"
301 "name=%(reference)s\n",
302 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000303 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000304
Fred Drake98e3b292002-10-25 20:42:44 +0000305 def check_items_config(self, expected):
306 cf = self.fromstring(
307 "[section]\n"
308 "name = value\n"
309 "key: |%(name)s| \n"
310 "getdefault: |%(default)s|\n"
311 "getname: |%(__name__)s|",
312 defaults={"default": "<default>"})
313 L = list(cf.items("section"))
314 L.sort()
315 self.assertEqual(L, expected)
316
Fred Drake8ef67672000-09-27 22:45:25 +0000317
Fred Drakec6f28912002-10-25 19:40:49 +0000318class ConfigParserTestCase(TestCaseBase):
319 config_class = ConfigParser.ConfigParser
320
321 def test_interpolation(self):
322 cf = self.get_interpolation_config()
323 eq = self.assertEqual
324 eq(cf.get("Foo", "getname"), "Foo")
325 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
326 eq(cf.get("Foo", "bar9"),
327 "something with lots of interpolation (9 steps)")
328 eq(cf.get("Foo", "bar10"),
329 "something with lots of interpolation (10 steps)")
330 self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
Fred Drake95b96d32001-02-12 17:23:20 +0000331
Fred Drake54782192002-12-31 06:57:25 +0000332 def test_interpolation_missing_value(self):
333 cf = self.get_interpolation_config()
334 e = self.get_error(ConfigParser.InterpolationError,
335 "Interpolation Error", "name")
336 self.assertEqual(e.reference, "reference")
337 self.assertEqual(e.section, "Interpolation Error")
338 self.assertEqual(e.option, "name")
339
Fred Drake98e3b292002-10-25 20:42:44 +0000340 def test_items(self):
341 self.check_items_config([('default', '<default>'),
342 ('getdefault', '|<default>|'),
343 ('getname', '|section|'),
344 ('key', '|value|'),
345 ('name', 'value')])
346
Fred Drake8ef67672000-09-27 22:45:25 +0000347
Fred Drakec6f28912002-10-25 19:40:49 +0000348class RawConfigParserTestCase(TestCaseBase):
349 config_class = ConfigParser.RawConfigParser
350
351 def test_interpolation(self):
352 cf = self.get_interpolation_config()
353 eq = self.assertEqual
354 eq(cf.get("Foo", "getname"), "%(__name__)s")
355 eq(cf.get("Foo", "bar"),
356 "something %(with1)s interpolation (1 step)")
357 eq(cf.get("Foo", "bar9"),
358 "something %(with9)s lots of interpolation (9 steps)")
359 eq(cf.get("Foo", "bar10"),
360 "something %(with10)s lots of interpolation (10 steps)")
361 eq(cf.get("Foo", "bar11"),
362 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000363
Fred Drake98e3b292002-10-25 20:42:44 +0000364 def test_items(self):
365 self.check_items_config([('default', '<default>'),
366 ('getdefault', '|%(default)s|'),
367 ('getname', '|%(__name__)s|'),
368 ('key', '|%(name)s|'),
369 ('name', 'value')])
370
Fred Drake8ef67672000-09-27 22:45:25 +0000371
Fred Drake0eebd5c2002-10-25 21:52:00 +0000372class SafeConfigParserTestCase(ConfigParserTestCase):
373 config_class = ConfigParser.SafeConfigParser
374
375 def test_safe_interpolation(self):
376 # See http://www.python.org/sf/511737
377 cf = self.fromstring("[section]\n"
378 "option1=xxx\n"
379 "option2=%(option1)s/xxx\n"
380 "ok=%(option1)s/%%s\n"
381 "not_ok=%(option2)s/%%s")
382 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
383 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
384
385
Fred Drakec6f28912002-10-25 19:40:49 +0000386def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000387 test_support.run_unittest(
388 ConfigParserTestCase,
389 RawConfigParserTestCase,
390 SafeConfigParserTestCase
391 )
Fred Drake3af0eb82002-10-25 18:09:24 +0000392
Fred Drakec6f28912002-10-25 19:40:49 +0000393if __name__ == "__main__":
394 test_main()