blob: f43d1d7faf340ee702a1c3fe067f5a53106b90a1 [file] [log] [blame]
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001import collections
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00002import configparser
Guido van Rossum34d19282007-08-09 01:03:29 +00003import io
Brian Curtin9a27b0c2010-07-26 00:27:10 +00004import os
Fred Drakec6f28912002-10-25 19:40:49 +00005import unittest
Georg Brandl96a60ae2010-07-28 13:13:46 +00006import textwrap
Fred Drake8ef67672000-09-27 22:45:25 +00007
Benjamin Petersonee8712c2008-05-20 21:35:26 +00008from test import support
Fred Drake3d5f7e82000-12-04 16:30:40 +00009
Raymond Hettingerf80680d2008-02-06 00:07:11 +000010class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000011
Thomas Wouters89f507f2006-12-13 04:49:30 +000012 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000013 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000014
15 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000016 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000017
Thomas Wouters89f507f2006-12-13 04:49:30 +000018 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000019 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000020
21 def iteritems(self): return iter(self.items())
22 def iterkeys(self): return iter(self.keys())
23 __iter__ = iterkeys
24 def itervalues(self): return iter(self.values())
Fred Drake3d5f7e82000-12-04 16:30:40 +000025
Fred Drake03c44a32010-02-19 06:08:41 +000026
Georg Brandl96a60ae2010-07-28 13:13:46 +000027class CfgParserTestCaseClass(unittest.TestCase):
Fred Drake03c44a32010-02-19 06:08:41 +000028 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000029 delimiters = ('=', ':')
30 comment_prefixes = (';', '#')
31 empty_lines_in_values = True
32 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000033 strict = False
Fred Drake03c44a32010-02-19 06:08:41 +000034
Fred Drakec6f28912002-10-25 19:40:49 +000035 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000036 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000037 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000038 allow_no_value=self.allow_no_value,
39 delimiters=self.delimiters,
40 comment_prefixes=self.comment_prefixes,
41 empty_lines_in_values=self.empty_lines_in_values,
42 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000043 strict=self.strict,
Georg Brandl96a60ae2010-07-28 13:13:46 +000044 )
Fred Drakea4923622010-08-09 12:52:45 +000045 return self.config_class(**arguments)
Fred Drake8ef67672000-09-27 22:45:25 +000046
Fred Drakec6f28912002-10-25 19:40:49 +000047 def fromstring(self, string, defaults=None):
48 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000049 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000050 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000051
Georg Brandl96a60ae2010-07-28 13:13:46 +000052class BasicTestCase(CfgParserTestCaseClass):
53
Fred Drakea4923622010-08-09 12:52:45 +000054 def basic_test(self, cf):
Fred Drakec6f28912002-10-25 19:40:49 +000055 L = cf.sections()
56 L.sort()
Georg Brandl96a60ae2010-07-28 13:13:46 +000057 E = ['Commented Bar',
58 'Foo Bar',
59 'Internationalized Stuff',
60 'Long Line',
61 'Section\\with$weird%characters[\t',
62 'Spaces',
63 'Spacey Bar',
64 'Spacey Bar From The Beginning',
Fred Drake03c44a32010-02-19 06:08:41 +000065 ]
66 if self.allow_no_value:
67 E.append(r'NoValue')
68 E.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000069 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000070 eq(L, E)
Fred Drake8ef67672000-09-27 22:45:25 +000071
Fred Drakec6f28912002-10-25 19:40:49 +000072 # The use of spaces in the section names serves as a
73 # regression test for SourceForge bug #583248:
74 # http://www.python.org/sf/583248
75 eq(cf.get('Foo Bar', 'foo'), 'bar')
76 eq(cf.get('Spacey Bar', 'foo'), 'bar')
Georg Brandl96a60ae2010-07-28 13:13:46 +000077 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar')
78 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +000079 eq(cf.get('Commented Bar', 'foo'), 'bar')
Georg Brandl96a60ae2010-07-28 13:13:46 +000080 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +000081 eq(cf.get('Spaces', 'key with spaces'), 'value')
82 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drake03c44a32010-02-19 06:08:41 +000083 if self.allow_no_value:
84 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +000085
Ezio Melottib58e0bd2010-01-23 15:40:09 +000086 self.assertNotIn('__name__', cf.options("Foo Bar"),
87 '__name__ "option" should not be exposed by the API!')
Fred Drakec6f28912002-10-25 19:40:49 +000088
89 # Make sure the right things happen for remove_option();
90 # added to include check for SourceForge bug #123324:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000091 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +000092 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000093 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +000094 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000095 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +000096 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +000097 " that was removed")
98
Michael Foordbd6c0792010-07-25 23:09:25 +000099 with self.assertRaises(configparser.NoSectionError) as cm:
100 cf.remove_option('No Such Section', 'foo')
101 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000102
103 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000104 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000105
Fred Drakea4923622010-08-09 12:52:45 +0000106 def test_basic(self):
107 config_string = """\
108[Foo Bar]
109foo{0[0]}bar
110[Spacey Bar]
111foo {0[0]} bar
112[Spacey Bar From The Beginning]
113 foo {0[0]} bar
114 baz {0[0]} qwe
115[Commented Bar]
116foo{0[1]} bar {1[1]} comment
117baz{0[0]}qwe {1[0]}another one
118[Long Line]
119foo{0[1]} this line is much, much longer than my editor
120 likes it.
121[Section\\with$weird%characters[\t]
122[Internationalized Stuff]
123foo[bg]{0[1]} Bulgarian
124foo{0[0]}Default
125foo[en]{0[0]}English
126foo[de]{0[0]}Deutsch
127[Spaces]
128key with spaces {0[1]} value
129another with spaces {0[0]} splat!
130""".format(self.delimiters, self.comment_prefixes)
131 if self.allow_no_value:
132 config_string += (
133 "[NoValue]\n"
134 "option-without-value\n"
135 )
136 cf = self.fromstring(config_string)
137 self.basic_test(cf)
138 if self.strict:
139 with self.assertRaises(configparser.DuplicateOptionError):
140 cf.read_string(textwrap.dedent("""\
141 [Duplicate Options Here]
142 option {0[0]} with a value
143 option {0[1]} with another value
144 """.format(self.delimiters)))
145 with self.assertRaises(configparser.DuplicateSectionError):
146 cf.read_string(textwrap.dedent("""\
147 [And Now For Something]
148 completely different {0[0]} True
149 [And Now For Something]
150 the larch {0[1]} 1
151 """.format(self.delimiters)))
152 else:
153 cf.read_string(textwrap.dedent("""\
154 [Duplicate Options Here]
155 option {0[0]} with a value
156 option {0[1]} with another value
157 """.format(self.delimiters)))
158
159 cf.read_string(textwrap.dedent("""\
160 [And Now For Something]
161 completely different {0[0]} True
162 [And Now For Something]
163 the larch {0[1]} 1
164 """.format(self.delimiters)))
165
166 def test_basic_from_dict(self):
167 config = {
168 "Foo Bar": {
169 "foo": "bar",
170 },
171 "Spacey Bar": {
172 "foo": "bar",
173 },
174 "Spacey Bar From The Beginning": {
175 "foo": "bar",
176 "baz": "qwe",
177 },
178 "Commented Bar": {
179 "foo": "bar",
180 "baz": "qwe",
181 },
182 "Long Line": {
183 "foo": "this line is much, much longer than my editor\nlikes "
184 "it.",
185 },
186 "Section\\with$weird%characters[\t": {
187 },
188 "Internationalized Stuff": {
189 "foo[bg]": "Bulgarian",
190 "foo": "Default",
191 "foo[en]": "English",
192 "foo[de]": "Deutsch",
193 },
194 "Spaces": {
195 "key with spaces": "value",
196 "another with spaces": "splat!",
197 }
198 }
199 if self.allow_no_value:
200 config.update({
201 "NoValue": {
202 "option-without-value": None,
203 }
204 })
205 cf = self.newconfig()
206 cf.read_dict(config)
207 self.basic_test(cf)
208 if self.strict:
209 with self.assertRaises(configparser.DuplicateOptionError):
210 cf.read_dict({
211 "Duplicate Options Here": {
212 'option': 'with a value',
213 'OPTION': 'with another value',
214 },
215 })
216 else:
217 cf.read_dict({
218 "Duplicate Options Here": {
219 'option': 'with a value',
220 'OPTION': 'with another value',
221 },
222 })
223
224
Fred Drakec6f28912002-10-25 19:40:49 +0000225 def test_case_sensitivity(self):
226 cf = self.newconfig()
227 cf.add_section("A")
228 cf.add_section("a")
229 L = cf.sections()
230 L.sort()
231 eq = self.assertEqual
232 eq(L, ["A", "a"])
233 cf.set("a", "B", "value")
234 eq(cf.options("a"), ["b"])
235 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000236 "could not locate option, expecting case-insensitive option names")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000237 self.assertTrue(cf.has_option("a", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000238 cf.set("A", "A-B", "A-B value")
239 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000240 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000241 cf.has_option("A", opt),
242 "has_option() returned false for option which should exist")
243 eq(cf.options("A"), ["a-b"])
244 eq(cf.options("a"), ["b"])
245 cf.remove_option("a", "B")
246 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000247
Fred Drakec6f28912002-10-25 19:40:49 +0000248 # SF bug #432369:
249 cf = self.fromstring(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000250 "[MySection]\nOption{} first line\n\tsecond line\n".format(
251 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000252 eq(cf.options("MySection"), ["option"])
253 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000254
Fred Drakec6f28912002-10-25 19:40:49 +0000255 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000256 cf = self.fromstring("[section]\n"
257 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000258 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000259 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000260
Fred Drake3c823aa2001-02-26 21:55:34 +0000261
David Goodger68a1abd2004-10-03 15:40:25 +0000262 def test_default_case_sensitivity(self):
263 cf = self.newconfig({"foo": "Bar"})
264 self.assertEqual(
265 cf.get("DEFAULT", "Foo"), "Bar",
266 "could not locate option, expecting case-insensitive option names")
267 cf = self.newconfig({"Foo": "Bar"})
268 self.assertEqual(
269 cf.get("DEFAULT", "Foo"), "Bar",
270 "could not locate option, expecting case-insensitive defaults")
271
Fred Drakec6f28912002-10-25 19:40:49 +0000272 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000273 cf = self.newconfig()
274 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000275 "[Foo]\n"
276 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000277 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000278 "[Foo]\n"
279 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000280 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000281 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000282 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000283 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000284 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000285 "[Foo]\n wrong-indent\n")
286 self.assertEqual(e.args, ('<???>',))
Fred Drake168bead2001-10-08 17:13:12 +0000287
Fred Drakea4923622010-08-09 12:52:45 +0000288 def parse_error(self, cf, exc, src):
Guido van Rossum34d19282007-08-09 01:03:29 +0000289 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000290 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000291 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000292 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000293
Fred Drakec6f28912002-10-25 19:40:49 +0000294 def test_query_errors(self):
295 cf = self.newconfig()
296 self.assertEqual(cf.sections(), [],
297 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000298 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000299 "new ConfigParser should have no acknowledged "
300 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000301 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000302 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000303 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000304 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000305 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000306 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000307 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000308 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000309 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000310
Fred Drakea4923622010-08-09 12:52:45 +0000311 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000312 try:
Fred Drakea4923622010-08-09 12:52:45 +0000313 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000314 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000315 return e
316 else:
317 self.fail("expected exception type %s.%s"
318 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000319
Fred Drakec6f28912002-10-25 19:40:49 +0000320 def test_boolean(self):
321 cf = self.fromstring(
322 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000323 "T1{equals}1\n"
324 "T2{equals}TRUE\n"
325 "T3{equals}True\n"
326 "T4{equals}oN\n"
327 "T5{equals}yes\n"
328 "F1{equals}0\n"
329 "F2{equals}FALSE\n"
330 "F3{equals}False\n"
331 "F4{equals}oFF\n"
332 "F5{equals}nO\n"
333 "E1{equals}2\n"
334 "E2{equals}foo\n"
335 "E3{equals}-1\n"
336 "E4{equals}0.1\n"
337 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000338 )
339 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000340 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
341 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000342 self.assertRaises(ValueError,
343 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000344
Fred Drakec6f28912002-10-25 19:40:49 +0000345 def test_weird_errors(self):
346 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000347 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000348 with self.assertRaises(configparser.DuplicateSectionError) as cm:
349 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000350 e = cm.exception
351 self.assertEqual(str(e), "Section 'Foo' already exists")
352 self.assertEqual(e.args, ("Foo", None, None))
353
354 if self.strict:
355 with self.assertRaises(configparser.DuplicateSectionError) as cm:
356 cf.read_string(textwrap.dedent("""\
357 [Foo]
358 will this be added{equals}True
359 [Bar]
360 what about this{equals}True
361 [Foo]
362 oops{equals}this won't
363 """.format(equals=self.delimiters[0])), source='<foo-bar>')
364 e = cm.exception
365 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
366 "section 'Foo' already exists")
367 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
368
369 with self.assertRaises(configparser.DuplicateOptionError) as cm:
370 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
371 e = cm.exception
372 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
373 "in section 'Bar' already exists")
374 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000375
376 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000377 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000378 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000379 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000380 " likes it.\n"
381 "[DEFAULT]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000382 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000383 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000384 "[Long Line - With Comments!]\n"
385 "test {0[1]} we {comment} can\n"
386 " also {comment} place\n"
387 " comments {comment} in\n"
388 " multiline {comment} values"
389 "\n".format(self.delimiters, comment=self.comment_prefixes[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000390 )
Fred Drake03c44a32010-02-19 06:08:41 +0000391 if self.allow_no_value:
392 config_string += (
393 "[Valueless]\n"
394 "option-without-value\n"
395 )
396
397 cf = self.fromstring(config_string)
Guido van Rossum34d19282007-08-09 01:03:29 +0000398 output = io.StringIO()
Fred Drakec6f28912002-10-25 19:40:49 +0000399 cf.write(output)
Fred Drake03c44a32010-02-19 06:08:41 +0000400 expect_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000401 "[DEFAULT]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000402 "foo {equals} another very\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000403 "\tlong line\n"
404 "\n"
405 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000406 "foo {equals} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000407 "\tlikes it.\n"
408 "\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000409 "[Long Line - With Comments!]\n"
410 "test {equals} we\n"
411 "\talso\n"
412 "\tcomments\n"
413 "\tmultiline\n"
414 "\n".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000415 )
Fred Drake03c44a32010-02-19 06:08:41 +0000416 if self.allow_no_value:
417 expect_string += (
418 "[Valueless]\n"
419 "option-without-value\n"
420 "\n"
421 )
422 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000423
Fred Drakeabc086f2004-05-18 03:29:52 +0000424 def test_set_string_types(self):
425 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000426 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000427 # Check that we don't get an exception when setting values in
428 # an existing section using strings:
429 class mystr(str):
430 pass
431 cf.set("sect", "option1", "splat")
432 cf.set("sect", "option1", mystr("splat"))
433 cf.set("sect", "option2", "splat")
434 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000435 cf.set("sect", "option1", "splat")
436 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000437
Fred Drake82903142004-05-18 04:24:02 +0000438 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000439 if self.delimiters[0] != '=':
440 # skip reading the file if we're using an incompatible format
441 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000442 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000443 # check when we pass a mix of readable and non-readable files:
444 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000445 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000446 self.assertEqual(parsed_files, [file1])
447 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
448 # check when we pass only a filename:
449 cf = self.newconfig()
450 parsed_files = cf.read(file1)
451 self.assertEqual(parsed_files, [file1])
452 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
453 # check when we pass only missing files:
454 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000455 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000456 self.assertEqual(parsed_files, [])
457 # check when we pass no files:
458 cf = self.newconfig()
459 parsed_files = cf.read([])
460 self.assertEqual(parsed_files, [])
461
Fred Drakec6f28912002-10-25 19:40:49 +0000462 # shared by subclasses
463 def get_interpolation_config(self):
464 return self.fromstring(
465 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000466 "bar{equals}something %(with1)s interpolation (1 step)\n"
467 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
468 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
469 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
470 "with11{equals}%(with10)s\n"
471 "with10{equals}%(with9)s\n"
472 "with9{equals}%(with8)s\n"
473 "with8{equals}%(With7)s\n"
474 "with7{equals}%(WITH6)s\n"
475 "with6{equals}%(with5)s\n"
476 "With5{equals}%(with4)s\n"
477 "WITH4{equals}%(with3)s\n"
478 "with3{equals}%(with2)s\n"
479 "with2{equals}%(with1)s\n"
480 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000481 "\n"
482 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000483 "foo{equals}%(bar)s\n"
484 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000485 "\n"
486 "[Interpolation Error]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000487 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]),
Fred Drake54782192002-12-31 06:57:25 +0000488 # no definition for 'reference'
Fred Drakec6f28912002-10-25 19:40:49 +0000489 defaults={"getname": "%(__name__)s"})
Fred Drake95b96d32001-02-12 17:23:20 +0000490
Fred Drake98e3b292002-10-25 20:42:44 +0000491 def check_items_config(self, expected):
492 cf = self.fromstring(
493 "[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000494 "name {0[0]} value\n"
495 "key{0[1]} |%(name)s| \n"
496 "getdefault{0[1]} |%(default)s|\n"
497 "getname{0[1]} |%(__name__)s|".format(self.delimiters),
Fred Drake98e3b292002-10-25 20:42:44 +0000498 defaults={"default": "<default>"})
499 L = list(cf.items("section"))
500 L.sort()
501 self.assertEqual(L, expected)
502
Fred Drake8ef67672000-09-27 22:45:25 +0000503
Fred Drakea4923622010-08-09 12:52:45 +0000504class StrictTestCase(BasicTestCase):
505 config_class = configparser.RawConfigParser
506 strict = True
507
508
Georg Brandl96a60ae2010-07-28 13:13:46 +0000509class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000510 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000511
512 def test_interpolation(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000513 rawval = {
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000514 configparser.ConfigParser: ("something %(with11)s "
515 "lots of interpolation (11 steps)"),
Michael Foordbd6c0792010-07-25 23:09:25 +0000516 configparser.SafeConfigParser: "%(with1)s",
517 }
Fred Drakec6f28912002-10-25 19:40:49 +0000518 cf = self.get_interpolation_config()
519 eq = self.assertEqual
520 eq(cf.get("Foo", "getname"), "Foo")
521 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
522 eq(cf.get("Foo", "bar9"),
523 "something with lots of interpolation (9 steps)")
524 eq(cf.get("Foo", "bar10"),
525 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000526 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Michael Foordbd6c0792010-07-25 23:09:25 +0000527 self.assertEqual(e.args, ("bar11", "Foo", rawval[self.config_class]))
Fred Drake95b96d32001-02-12 17:23:20 +0000528
Fred Drake54782192002-12-31 06:57:25 +0000529 def test_interpolation_missing_value(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000530 rawval = {
531 configparser.ConfigParser: '%(reference)s',
532 configparser.SafeConfigParser: '',
533 }
Fred Drakea4923622010-08-09 12:52:45 +0000534 cf = self.get_interpolation_config()
535 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000536 "Interpolation Error", "name")
537 self.assertEqual(e.reference, "reference")
538 self.assertEqual(e.section, "Interpolation Error")
539 self.assertEqual(e.option, "name")
Michael Foordbd6c0792010-07-25 23:09:25 +0000540 self.assertEqual(e.args, ('name', 'Interpolation Error',
541 rawval[self.config_class], 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000542
Fred Drake98e3b292002-10-25 20:42:44 +0000543 def test_items(self):
544 self.check_items_config([('default', '<default>'),
545 ('getdefault', '|<default>|'),
546 ('getname', '|section|'),
547 ('key', '|value|'),
548 ('name', 'value')])
549
David Goodger1cbf2062004-10-03 15:55:09 +0000550 def test_set_nonstring_types(self):
551 cf = self.newconfig()
552 cf.add_section('non-string')
553 cf.set('non-string', 'int', 1)
554 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
555 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
556 '%(list)': '%(list)'})
557 cf.set('non-string', 'string_with_interpolation', '%(list)s')
558 self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
559 self.assertRaises(TypeError, cf.get, 'non-string', 'int')
560 self.assertEqual(cf.get('non-string', 'list', raw=True),
561 [0, 1, 1, 2, 3, 5, 8, 13, '%('])
562 self.assertRaises(TypeError, cf.get, 'non-string', 'list')
563 self.assertEqual(cf.get('non-string', 'dict', raw=True),
564 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
565 self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
566 self.assertEqual(cf.get('non-string', 'string_with_interpolation',
567 raw=True), '%(list)s')
568 self.assertRaises(ValueError, cf.get, 'non-string',
569 'string_with_interpolation', raw=False)
570
Georg Brandl96a60ae2010-07-28 13:13:46 +0000571class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
572 delimiters = (':=', '$')
573 comment_prefixes = ('//', '"')
574
575class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000576 config_class = configparser.ConfigParser
577 wonderful_spam = ("I'm having spam spam spam spam "
578 "spam spam spam beaked beans spam "
579 "spam spam and spam!").replace(' ', '\t\n')
580
581 def setUp(self):
582 cf = self.newconfig()
583 for i in range(100):
584 s = 'section{}'.format(i)
585 cf.add_section(s)
586 for j in range(10):
587 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
588 with open(support.TESTFN, 'w') as f:
589 cf.write(f)
590
591 def tearDown(self):
592 os.unlink(support.TESTFN)
593
594 def test_dominating_multiline_values(self):
595 # We're reading from file because this is where the code changed
596 # during performance updates in Python 3.2
597 cf_from_file = self.newconfig()
598 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000599 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000600 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
601 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000602
Georg Brandl96a60ae2010-07-28 13:13:46 +0000603class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000604 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000605
606 def test_interpolation(self):
607 cf = self.get_interpolation_config()
608 eq = self.assertEqual
609 eq(cf.get("Foo", "getname"), "%(__name__)s")
610 eq(cf.get("Foo", "bar"),
611 "something %(with1)s interpolation (1 step)")
612 eq(cf.get("Foo", "bar9"),
613 "something %(with9)s lots of interpolation (9 steps)")
614 eq(cf.get("Foo", "bar10"),
615 "something %(with10)s lots of interpolation (10 steps)")
616 eq(cf.get("Foo", "bar11"),
617 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000618
Fred Drake98e3b292002-10-25 20:42:44 +0000619 def test_items(self):
620 self.check_items_config([('default', '<default>'),
621 ('getdefault', '|%(default)s|'),
622 ('getname', '|%(__name__)s|'),
623 ('key', '|%(name)s|'),
624 ('name', 'value')])
625
David Goodger1cbf2062004-10-03 15:55:09 +0000626 def test_set_nonstring_types(self):
627 cf = self.newconfig()
628 cf.add_section('non-string')
629 cf.set('non-string', 'int', 1)
630 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
631 cf.set('non-string', 'dict', {'pi': 3.14159})
632 self.assertEqual(cf.get('non-string', 'int'), 1)
633 self.assertEqual(cf.get('non-string', 'list'),
634 [0, 1, 1, 2, 3, 5, 8, 13])
635 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Tim Petersab9b32c2004-10-03 18:35:19 +0000636
Georg Brandl96a60ae2010-07-28 13:13:46 +0000637class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
638 delimiters = (':=', '$')
639 comment_prefixes = ('//', '"')
640
641class RawConfigParserTestSambaConf(BasicTestCase):
642 config_class = configparser.RawConfigParser
643 comment_prefixes = ('#', ';', '//', '----')
644 empty_lines_in_values = False
645
646 def test_reading(self):
647 smbconf = support.findfile("cfgparser.2")
648 # check when we pass a mix of readable and non-readable files:
649 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000650 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000651 self.assertEqual(parsed_files, [smbconf])
652 sections = ['global', 'homes', 'printers',
653 'print$', 'pdf-generator', 'tmp', 'Agustin']
654 self.assertEqual(cf.sections(), sections)
655 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
656 self.assertEqual(cf.getint("global", "max log size"), 50)
657 self.assertEqual(cf.get("global", "hosts allow"), "127.")
658 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000659
Fred Drake0eebd5c2002-10-25 21:52:00 +0000660class SafeConfigParserTestCase(ConfigParserTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000661 config_class = configparser.SafeConfigParser
Fred Drake0eebd5c2002-10-25 21:52:00 +0000662
663 def test_safe_interpolation(self):
664 # See http://www.python.org/sf/511737
665 cf = self.fromstring("[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000666 "option1{eq}xxx\n"
667 "option2{eq}%(option1)s/xxx\n"
668 "ok{eq}%(option1)s/%%s\n"
669 "not_ok{eq}%(option2)s/%%s".format(
670 eq=self.delimiters[0]))
Fred Drake0eebd5c2002-10-25 21:52:00 +0000671 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
672 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
673
Guido van Rossumd8faa362007-04-27 19:54:29 +0000674 def test_set_malformatted_interpolation(self):
675 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000676 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000677
678 self.assertEqual(cf.get('sect', "option1"), "foo")
679
680 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
681 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
682 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
683
684 self.assertEqual(cf.get('sect', "option1"), "foo")
685
Georg Brandl1f9fa312009-04-27 16:42:58 +0000686 # bug #5741: double percents are *not* malformed
687 cf.set("sect", "option2", "foo%%bar")
688 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
689
David Goodger1cbf2062004-10-03 15:55:09 +0000690 def test_set_nonstring_types(self):
691 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000692 "option1{eq}foo\n".format(eq=self.delimiters[0]))
David Goodger1cbf2062004-10-03 15:55:09 +0000693 # Check that we get a TypeError when setting non-string values
694 # in an existing section:
695 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
696 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
697 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
698 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
699 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
700 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
701
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000702 def test_add_section_default_1(self):
703 cf = self.newconfig()
704 self.assertRaises(ValueError, cf.add_section, "default")
705
706 def test_add_section_default_2(self):
707 cf = self.newconfig()
708 self.assertRaises(ValueError, cf.add_section, "DEFAULT")
709
Georg Brandl96a60ae2010-07-28 13:13:46 +0000710class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
711 delimiters = (':=', '$')
712 comment_prefixes = ('//', '"')
Fred Drake03c44a32010-02-19 06:08:41 +0000713
714class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase):
715 allow_no_value = True
716
Georg Brandl8dcaa732010-07-29 12:17:40 +0000717class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
718 config_class = configparser.SafeConfigParser
719 delimiters = {'='}
720 comment_prefixes = {'#'}
721 allow_no_value = True
722
723 def test_cfgparser_dot_3(self):
724 tricky = support.findfile("cfgparser.3")
725 cf = self.newconfig()
726 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
727 self.assertEqual(cf.sections(), ['strange',
728 'corruption',
729 'yeah, sections can be '
730 'indented as well',
731 'another one!',
732 'no values here',
733 'tricky interpolation',
734 'more interpolation'])
735 #self.assertEqual(cf.getint('DEFAULT', 'go', vars={'interpolate': '-1'}),
736 # -1)
737 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
738 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
739 longname = 'yeah, sections can be indented as well'
740 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
741 self.assertEquals(cf.get(longname, 'lets use some Unicode'),
742 '片仮名')
743 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
744 # `go` from DEFAULT
745 with self.assertRaises(configparser.InterpolationMissingOptionError):
746 cf.items('no values here')
747 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
748 self.assertEqual(cf.get('tricky interpolation', 'lets'),
749 cf.get('tricky interpolation', 'go'))
750 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
751
752 def test_unicode_failure(self):
753 tricky = support.findfile("cfgparser.3")
754 cf = self.newconfig()
755 with self.assertRaises(UnicodeDecodeError):
756 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +0000757
Thomas Wouters89f507f2006-12-13 04:49:30 +0000758class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000759 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +0000760
761 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +0000762 cf = self.fromstring("[b]\n"
763 "o4=1\n"
764 "o3=2\n"
765 "o2=3\n"
766 "o1=4\n"
767 "[a]\n"
768 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +0000769 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +0000770 cf.write(output)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000771 self.assertEquals(output.getvalue(),
772 "[a]\n"
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000773 "k = v\n\n"
Thomas Wouters89f507f2006-12-13 04:49:30 +0000774 "[b]\n"
775 "o1 = 4\n"
776 "o2 = 3\n"
777 "o3 = 2\n"
778 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +0000779
Fred Drake03c44a32010-02-19 06:08:41 +0000780
Georg Brandl96a60ae2010-07-28 13:13:46 +0000781class CompatibleTestCase(CfgParserTestCaseClass):
782 config_class = configparser.RawConfigParser
783 comment_prefixes = configparser.RawConfigParser._COMPATIBLE
784
785 def test_comment_handling(self):
786 config_string = textwrap.dedent("""\
787 [Commented Bar]
788 baz=qwe ; a comment
789 foo: bar # not a comment!
790 # but this is a comment
791 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +0000792 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +0000793 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +0000794 """)
795 cf = self.fromstring(config_string)
796 self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!')
797 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Georg Brandl8dcaa732010-07-29 12:17:40 +0000798 self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000799
800
Fred Drakec6f28912002-10-25 19:40:49 +0000801def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000802 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +0000803 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000804 ConfigParserTestCaseNonStandardDelimiters,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000805 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000806 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000807 RawConfigParserTestCaseNonStandardDelimiters,
808 RawConfigParserTestSambaConf,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000809 SafeConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000810 SafeConfigParserTestCaseNonStandardDelimiters,
Fred Drake03c44a32010-02-19 06:08:41 +0000811 SafeConfigParserTestCaseNoValue,
Georg Brandl8dcaa732010-07-29 12:17:40 +0000812 SafeConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000813 SortedTestCase,
Fred Drakea4923622010-08-09 12:52:45 +0000814 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000815 CompatibleTestCase,
Fred Drake03c44a32010-02-19 06:08:41 +0000816 )
817
Fred Drake3af0eb82002-10-25 18:09:24 +0000818
Fred Drakec6f28912002-10-25 19:40:49 +0000819if __name__ == "__main__":
820 test_main()