blob: b40cedf4ab17b50556f9f4ea73895d33be33e44e [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
Fred Drakec6f28912002-10-25 19:40:49 +0000118 def test_parse_errors(self):
119 self.newconfig()
120 self.parse_error(ConfigParser.ParsingError,
121 "[Foo]\n extra-spaces: splat\n")
122 self.parse_error(ConfigParser.ParsingError,
123 "[Foo]\n extra-spaces= splat\n")
124 self.parse_error(ConfigParser.ParsingError,
125 "[Foo]\noption-without-value\n")
126 self.parse_error(ConfigParser.ParsingError,
127 "[Foo]\n:value-without-option-name\n")
128 self.parse_error(ConfigParser.ParsingError,
129 "[Foo]\n=value-without-option-name\n")
130 self.parse_error(ConfigParser.MissingSectionHeaderError,
131 "No Section!\n")
Fred Drake168bead2001-10-08 17:13:12 +0000132
Fred Drakec6f28912002-10-25 19:40:49 +0000133 def parse_error(self, exc, src):
134 sio = StringIO.StringIO(src)
135 self.assertRaises(exc, self.cf.readfp, sio)
Fred Drake168bead2001-10-08 17:13:12 +0000136
Fred Drakec6f28912002-10-25 19:40:49 +0000137 def test_query_errors(self):
138 cf = self.newconfig()
139 self.assertEqual(cf.sections(), [],
140 "new ConfigParser should have no defined sections")
141 self.failIf(cf.has_section("Foo"),
142 "new ConfigParser should have no acknowledged sections")
143 self.assertRaises(ConfigParser.NoSectionError,
144 cf.options, "Foo")
145 self.assertRaises(ConfigParser.NoSectionError,
146 cf.set, "foo", "bar", "value")
147 self.get_error(ConfigParser.NoSectionError, "foo", "bar")
148 cf.add_section("foo")
149 self.get_error(ConfigParser.NoOptionError, "foo", "bar")
Fred Drake8ef67672000-09-27 22:45:25 +0000150
Fred Drakec6f28912002-10-25 19:40:49 +0000151 def get_error(self, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000152 try:
153 self.cf.get(section, option)
154 except exc, e:
155 return e
156 else:
157 self.fail("expected exception type %s.%s"
158 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000159
Fred Drakec6f28912002-10-25 19:40:49 +0000160 def test_boolean(self):
161 cf = self.fromstring(
162 "[BOOLTEST]\n"
163 "T1=1\n"
164 "T2=TRUE\n"
165 "T3=True\n"
166 "T4=oN\n"
167 "T5=yes\n"
168 "F1=0\n"
169 "F2=FALSE\n"
170 "F3=False\n"
171 "F4=oFF\n"
172 "F5=nO\n"
173 "E1=2\n"
174 "E2=foo\n"
175 "E3=-1\n"
176 "E4=0.1\n"
177 "E5=FALSE AND MORE"
178 )
179 for x in range(1, 5):
180 self.failUnless(cf.getboolean('BOOLTEST', 't%d' % x))
181 self.failIf(cf.getboolean('BOOLTEST', 'f%d' % x))
182 self.assertRaises(ValueError,
183 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000184
Fred Drakec6f28912002-10-25 19:40:49 +0000185 def test_weird_errors(self):
186 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000187 cf.add_section("Foo")
Fred Drakec6f28912002-10-25 19:40:49 +0000188 self.assertRaises(ConfigParser.DuplicateSectionError,
189 cf.add_section, "Foo")
190
191 def test_write(self):
192 cf = self.fromstring(
193 "[Long Line]\n"
194 "foo: this line is much, much longer than my editor\n"
195 " likes it.\n"
196 "[DEFAULT]\n"
197 "foo: another very\n"
198 " long line"
199 )
200 output = StringIO.StringIO()
201 cf.write(output)
202 self.assertEqual(
203 output.getvalue(),
204 "[DEFAULT]\n"
205 "foo = another very\n"
206 "\tlong line\n"
207 "\n"
208 "[Long Line]\n"
209 "foo = this line is much, much longer than my editor\n"
210 "\tlikes it.\n"
211 "\n"
212 )
213
Fred Drakeabc086f2004-05-18 03:29:52 +0000214 def test_set_string_types(self):
215 cf = self.fromstring("[sect]\n"
216 "option1=foo\n")
217 # Check that we don't get an exception when setting values in
218 # an existing section using strings:
219 class mystr(str):
220 pass
221 cf.set("sect", "option1", "splat")
222 cf.set("sect", "option1", mystr("splat"))
223 cf.set("sect", "option2", "splat")
224 cf.set("sect", "option2", mystr("splat"))
225 try:
226 unicode
227 except NameError:
228 pass
229 else:
230 cf.set("sect", "option1", unicode("splat"))
231 cf.set("sect", "option2", unicode("splat"))
232
233 def test_set_nonstring_types(self):
234 cf = self.fromstring("[sect]\n"
235 "option1=foo\n")
236 # Check that we get a TypeError when setting non-string values
237 # in an existing section:
238 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
239 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
240 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
241 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
242 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
243 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
244
Fred Drakec6f28912002-10-25 19:40:49 +0000245 # shared by subclasses
246 def get_interpolation_config(self):
247 return self.fromstring(
248 "[Foo]\n"
249 "bar=something %(with1)s interpolation (1 step)\n"
250 "bar9=something %(with9)s lots of interpolation (9 steps)\n"
251 "bar10=something %(with10)s lots of interpolation (10 steps)\n"
252 "bar11=something %(with11)s lots of interpolation (11 steps)\n"
253 "with11=%(with10)s\n"
254 "with10=%(with9)s\n"
255 "with9=%(with8)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000256 "with8=%(With7)s\n"
257 "with7=%(WITH6)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000258 "with6=%(with5)s\n"
Fred Drakebc12b012004-05-18 02:25:51 +0000259 "With5=%(with4)s\n"
260 "WITH4=%(with3)s\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000261 "with3=%(with2)s\n"
262 "with2=%(with1)s\n"
263 "with1=with\n"
264 "\n"
265 "[Mutual Recursion]\n"
266 "foo=%(bar)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000267 "bar=%(foo)s\n"
268 "\n"
269 "[Interpolation Error]\n"
270 "name=%(reference)s\n",
271 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000272 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000273
Fred Drake98e3b292002-10-25 20:42:44 +0000274 def check_items_config(self, expected):
275 cf = self.fromstring(
276 "[section]\n"
277 "name = value\n"
278 "key: |%(name)s| \n"
279 "getdefault: |%(default)s|\n"
280 "getname: |%(__name__)s|",
281 defaults={"default": "<default>"})
282 L = list(cf.items("section"))
283 L.sort()
284 self.assertEqual(L, expected)
285
Fred Drake8ef67672000-09-27 22:45:25 +0000286
Fred Drakec6f28912002-10-25 19:40:49 +0000287class ConfigParserTestCase(TestCaseBase):
288 config_class = ConfigParser.ConfigParser
289
290 def test_interpolation(self):
291 cf = self.get_interpolation_config()
292 eq = self.assertEqual
293 eq(cf.get("Foo", "getname"), "Foo")
294 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
295 eq(cf.get("Foo", "bar9"),
296 "something with lots of interpolation (9 steps)")
297 eq(cf.get("Foo", "bar10"),
298 "something with lots of interpolation (10 steps)")
299 self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11")
Fred Drake95b96d32001-02-12 17:23:20 +0000300
Fred Drake54782192002-12-31 06:57:25 +0000301 def test_interpolation_missing_value(self):
302 cf = self.get_interpolation_config()
303 e = self.get_error(ConfigParser.InterpolationError,
304 "Interpolation Error", "name")
305 self.assertEqual(e.reference, "reference")
306 self.assertEqual(e.section, "Interpolation Error")
307 self.assertEqual(e.option, "name")
308
Fred Drake98e3b292002-10-25 20:42:44 +0000309 def test_items(self):
310 self.check_items_config([('default', '<default>'),
311 ('getdefault', '|<default>|'),
312 ('getname', '|section|'),
313 ('key', '|value|'),
314 ('name', 'value')])
315
Fred Drake8ef67672000-09-27 22:45:25 +0000316
Fred Drakec6f28912002-10-25 19:40:49 +0000317class RawConfigParserTestCase(TestCaseBase):
318 config_class = ConfigParser.RawConfigParser
319
320 def test_interpolation(self):
321 cf = self.get_interpolation_config()
322 eq = self.assertEqual
323 eq(cf.get("Foo", "getname"), "%(__name__)s")
324 eq(cf.get("Foo", "bar"),
325 "something %(with1)s interpolation (1 step)")
326 eq(cf.get("Foo", "bar9"),
327 "something %(with9)s lots of interpolation (9 steps)")
328 eq(cf.get("Foo", "bar10"),
329 "something %(with10)s lots of interpolation (10 steps)")
330 eq(cf.get("Foo", "bar11"),
331 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000332
Fred Drake98e3b292002-10-25 20:42:44 +0000333 def test_items(self):
334 self.check_items_config([('default', '<default>'),
335 ('getdefault', '|%(default)s|'),
336 ('getname', '|%(__name__)s|'),
337 ('key', '|%(name)s|'),
338 ('name', 'value')])
339
Fred Drake8ef67672000-09-27 22:45:25 +0000340
Fred Drake0eebd5c2002-10-25 21:52:00 +0000341class SafeConfigParserTestCase(ConfigParserTestCase):
342 config_class = ConfigParser.SafeConfigParser
343
344 def test_safe_interpolation(self):
345 # See http://www.python.org/sf/511737
346 cf = self.fromstring("[section]\n"
347 "option1=xxx\n"
348 "option2=%(option1)s/xxx\n"
349 "ok=%(option1)s/%%s\n"
350 "not_ok=%(option2)s/%%s")
351 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
352 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
353
354
Fred Drakec6f28912002-10-25 19:40:49 +0000355def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000356 test_support.run_unittest(
357 ConfigParserTestCase,
358 RawConfigParserTestCase,
359 SafeConfigParserTestCase
360 )
Fred Drake3af0eb82002-10-25 18:09:24 +0000361
Fred Drakec6f28912002-10-25 19:40:49 +0000362if __name__ == "__main__":
363 test_main()