blob: 24158ad104b7728dfe1cdc005a520f1b59b877d4 [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
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00007import warnings
Fred Drake8ef67672000-09-27 22:45:25 +00008
Benjamin Petersonee8712c2008-05-20 21:35:26 +00009from test import support
Fred Drake3d5f7e82000-12-04 16:30:40 +000010
Raymond Hettingerf80680d2008-02-06 00:07:11 +000011class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000012
Thomas Wouters89f507f2006-12-13 04:49:30 +000013 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000014 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000015
16 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000017 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000018
Thomas Wouters89f507f2006-12-13 04:49:30 +000019 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000020 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000021
22 def iteritems(self): return iter(self.items())
23 def iterkeys(self): return iter(self.keys())
24 __iter__ = iterkeys
25 def itervalues(self): return iter(self.values())
Fred Drake3d5f7e82000-12-04 16:30:40 +000026
Fred Drake03c44a32010-02-19 06:08:41 +000027
Georg Brandl96a60ae2010-07-28 13:13:46 +000028class CfgParserTestCaseClass(unittest.TestCase):
Fred Drake03c44a32010-02-19 06:08:41 +000029 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000030 delimiters = ('=', ':')
31 comment_prefixes = (';', '#')
32 empty_lines_in_values = True
33 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000034 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000035 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000036 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000037
Fred Drakec6f28912002-10-25 19:40:49 +000038 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000039 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000040 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000041 allow_no_value=self.allow_no_value,
42 delimiters=self.delimiters,
43 comment_prefixes=self.comment_prefixes,
44 empty_lines_in_values=self.empty_lines_in_values,
45 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000046 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000047 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000048 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000049 )
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000050 with warnings.catch_warnings():
51 warnings.simplefilter("ignore", category=DeprecationWarning)
52 instance = self.config_class(**arguments)
53 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000054
Fred Drakec6f28912002-10-25 19:40:49 +000055 def fromstring(self, string, defaults=None):
56 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000057 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000058 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000059
Georg Brandl96a60ae2010-07-28 13:13:46 +000060class BasicTestCase(CfgParserTestCaseClass):
61
Fred Drakea4923622010-08-09 12:52:45 +000062 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000063 E = ['Commented Bar',
64 'Foo Bar',
65 'Internationalized Stuff',
66 'Long Line',
67 'Section\\with$weird%characters[\t',
68 'Spaces',
69 'Spacey Bar',
70 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000071 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000072 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000073
Fred Drake03c44a32010-02-19 06:08:41 +000074 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000075 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000076 E.sort()
Łukasz Langa26d513c2010-11-10 18:57:39 +000077
78 # API access
79 L = cf.sections()
80 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000081 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000082 eq(L, E)
Fred Drake8ef67672000-09-27 22:45:25 +000083
Łukasz Langa26d513c2010-11-10 18:57:39 +000084 # mapping access
85 L = [section for section in cf]
86 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +000087 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +000088 E.sort()
89 eq(L, E)
90
Fred Drakec6f28912002-10-25 19:40:49 +000091 # The use of spaces in the section names serves as a
92 # regression test for SourceForge bug #583248:
93 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +000094
95 # API access
96 eq(cf.get('Foo Bar', 'foo'), 'bar1')
97 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
98 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +000099 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000100 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000101 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000102 eq(cf.get('Spaces', 'key with spaces'), 'value')
103 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000104 eq(cf.getint('Types', 'int'), 42)
105 eq(cf.get('Types', 'int'), "42")
106 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
107 eq(cf.get('Types', 'float'), "0.44")
108 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000109 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000110 if self.allow_no_value:
111 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000112
Łukasz Langa26d513c2010-11-10 18:57:39 +0000113 # test vars= and fallback=
114 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000115 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
116 with self.assertRaises(configparser.NoSectionError):
117 cf.get('No Such Foo Bar', 'foo')
118 with self.assertRaises(configparser.NoOptionError):
119 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000120 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
121 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
122 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
123 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
124 eq(cf.getint('Types', 'int', fallback=18), 42)
125 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
126 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Fred Drakecc645b92010-09-04 04:35:34 +0000127 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000128 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000129 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000130 fallback=0.0), 0.0)
131 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
132 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
133 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000134 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000135 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
136 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000137 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000138 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000139 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000140 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000141
Łukasz Langa26d513c2010-11-10 18:57:39 +0000142 # mapping access
143 eq(cf['Foo Bar']['foo'], 'bar1')
144 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000145 section = cf['Spacey Bar From The Beginning']
146 eq(section.name, 'Spacey Bar From The Beginning')
147 self.assertIs(section.parser, cf)
148 with self.assertRaises(AttributeError):
149 section.name = 'Name is read-only'
150 with self.assertRaises(AttributeError):
151 section.parser = 'Parser is read-only'
152 eq(section['foo'], 'bar3')
153 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000154 eq(cf['Commented Bar']['foo'], 'bar4')
155 eq(cf['Commented Bar']['baz'], 'qwe')
156 eq(cf['Spaces']['key with spaces'], 'value')
157 eq(cf['Spaces']['another with spaces'], 'splat!')
158 eq(cf['Long Line']['foo'],
159 'this line is much, much longer than my editor\nlikes it.')
160 if self.allow_no_value:
161 eq(cf['NoValue']['option-without-value'], None)
162
Fred Drakec6f28912002-10-25 19:40:49 +0000163 # Make sure the right things happen for remove_option();
164 # added to include check for SourceForge bug #123324:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000165
166 # API acceess
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000167 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000168 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000169 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000170 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000171 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000172 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000173 " that was removed")
174
Michael Foordbd6c0792010-07-25 23:09:25 +0000175 with self.assertRaises(configparser.NoSectionError) as cm:
176 cf.remove_option('No Such Section', 'foo')
177 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000178
179 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000180 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000181
Łukasz Langa26d513c2010-11-10 18:57:39 +0000182 # mapping access
183 del cf['Spacey Bar']['foo']
184 self.assertFalse('foo' in cf['Spacey Bar'])
185 with self.assertRaises(KeyError):
186 del cf['Spacey Bar']['foo']
187 with self.assertRaises(KeyError):
188 del cf['No Such Section']['foo']
189
Fred Drakea4923622010-08-09 12:52:45 +0000190 def test_basic(self):
191 config_string = """\
192[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000193foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000194[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000195foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000196[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000197 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000198 baz {0[0]} qwe
199[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000200foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000201baz{0[0]}qwe {1[0]}another one
202[Long Line]
203foo{0[1]} this line is much, much longer than my editor
204 likes it.
205[Section\\with$weird%characters[\t]
206[Internationalized Stuff]
207foo[bg]{0[1]} Bulgarian
208foo{0[0]}Default
209foo[en]{0[0]}English
210foo[de]{0[0]}Deutsch
211[Spaces]
212key with spaces {0[1]} value
213another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000214[Types]
215int {0[1]} 42
216float {0[0]} 0.44
217boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000218123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000219""".format(self.delimiters, self.comment_prefixes)
220 if self.allow_no_value:
221 config_string += (
222 "[NoValue]\n"
223 "option-without-value\n"
224 )
225 cf = self.fromstring(config_string)
226 self.basic_test(cf)
227 if self.strict:
228 with self.assertRaises(configparser.DuplicateOptionError):
229 cf.read_string(textwrap.dedent("""\
230 [Duplicate Options Here]
231 option {0[0]} with a value
232 option {0[1]} with another value
233 """.format(self.delimiters)))
234 with self.assertRaises(configparser.DuplicateSectionError):
235 cf.read_string(textwrap.dedent("""\
236 [And Now For Something]
237 completely different {0[0]} True
238 [And Now For Something]
239 the larch {0[1]} 1
240 """.format(self.delimiters)))
241 else:
242 cf.read_string(textwrap.dedent("""\
243 [Duplicate Options Here]
244 option {0[0]} with a value
245 option {0[1]} with another value
246 """.format(self.delimiters)))
247
248 cf.read_string(textwrap.dedent("""\
249 [And Now For Something]
250 completely different {0[0]} True
251 [And Now For Something]
252 the larch {0[1]} 1
253 """.format(self.delimiters)))
254
255 def test_basic_from_dict(self):
256 config = {
257 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000258 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000259 },
260 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000261 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000262 },
263 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000264 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000265 "baz": "qwe",
266 },
267 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000268 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000269 "baz": "qwe",
270 },
271 "Long Line": {
272 "foo": "this line is much, much longer than my editor\nlikes "
273 "it.",
274 },
275 "Section\\with$weird%characters[\t": {
276 },
277 "Internationalized Stuff": {
278 "foo[bg]": "Bulgarian",
279 "foo": "Default",
280 "foo[en]": "English",
281 "foo[de]": "Deutsch",
282 },
283 "Spaces": {
284 "key with spaces": "value",
285 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000286 },
287 "Types": {
288 "int": 42,
289 "float": 0.44,
290 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000291 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000292 },
Fred Drakea4923622010-08-09 12:52:45 +0000293 }
294 if self.allow_no_value:
295 config.update({
296 "NoValue": {
297 "option-without-value": None,
298 }
299 })
300 cf = self.newconfig()
301 cf.read_dict(config)
302 self.basic_test(cf)
303 if self.strict:
304 with self.assertRaises(configparser.DuplicateOptionError):
305 cf.read_dict({
306 "Duplicate Options Here": {
307 'option': 'with a value',
308 'OPTION': 'with another value',
309 },
310 })
311 else:
312 cf.read_dict({
313 "Duplicate Options Here": {
314 'option': 'with a value',
315 'OPTION': 'with another value',
316 },
317 })
318
319
Fred Drakec6f28912002-10-25 19:40:49 +0000320 def test_case_sensitivity(self):
321 cf = self.newconfig()
322 cf.add_section("A")
323 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000324 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000325 L = cf.sections()
326 L.sort()
327 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000328 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000329 cf.set("a", "B", "value")
330 eq(cf.options("a"), ["b"])
331 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000332 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000333 with self.assertRaises(configparser.NoSectionError):
334 # section names are case-sensitive
335 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000336 self.assertTrue(cf.has_option("a", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000337 cf.set("A", "A-B", "A-B value")
338 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000339 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000340 cf.has_option("A", opt),
341 "has_option() returned false for option which should exist")
342 eq(cf.options("A"), ["a-b"])
343 eq(cf.options("a"), ["b"])
344 cf.remove_option("a", "B")
345 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000346
Fred Drakec6f28912002-10-25 19:40:49 +0000347 # SF bug #432369:
348 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000349 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000350 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000351 eq(cf.options("MySection"), ["option"])
352 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000353
Fred Drakec6f28912002-10-25 19:40:49 +0000354 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000355 cf = self.fromstring("[section]\n"
356 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000357 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000358 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000359
Fred Drake3c823aa2001-02-26 21:55:34 +0000360
Łukasz Langa26d513c2010-11-10 18:57:39 +0000361 def test_case_sensitivity_mapping_access(self):
362 cf = self.newconfig()
363 cf["A"] = {}
364 cf["a"] = {"B": "value"}
365 cf["B"] = {}
366 L = [section for section in cf]
367 L.sort()
368 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000369 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000370 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000371 eq(cf["a"].keys(), {"b"})
372 eq(cf["a"]["b"], "value",
373 "could not locate option, expecting case-insensitive option names")
374 with self.assertRaises(KeyError):
375 # section names are case-sensitive
376 cf["b"]["A"] = "value"
377 self.assertTrue("b" in cf["a"])
378 cf["A"]["A-B"] = "A-B value"
379 for opt in ("a-b", "A-b", "a-B", "A-B"):
380 self.assertTrue(
381 opt in cf["A"],
382 "has_option() returned false for option which should exist")
383 eq(cf["A"].keys(), {"a-b"})
384 eq(cf["a"].keys(), {"b"})
385 del cf["a"]["B"]
386 elem_eq(cf["a"].keys(), {})
387
388 # SF bug #432369:
389 cf = self.fromstring(
390 "[MySection]\nOption{} first line \n\tsecond line \n".format(
391 self.delimiters[0]))
392 eq(cf["MySection"].keys(), {"option"})
393 eq(cf["MySection"]["Option"], "first line\nsecond line")
394
395 # SF bug #561822:
396 cf = self.fromstring("[section]\n"
397 "nekey{}nevalue\n".format(self.delimiters[0]),
398 defaults={"key":"value"})
399 self.assertTrue("Key" in cf["section"])
400
David Goodger68a1abd2004-10-03 15:40:25 +0000401 def test_default_case_sensitivity(self):
402 cf = self.newconfig({"foo": "Bar"})
403 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000404 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000405 "could not locate option, expecting case-insensitive option names")
406 cf = self.newconfig({"Foo": "Bar"})
407 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000408 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000409 "could not locate option, expecting case-insensitive defaults")
410
Fred Drakec6f28912002-10-25 19:40:49 +0000411 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000412 cf = self.newconfig()
413 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000414 "[Foo]\n"
415 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000416 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000417 "[Foo]\n"
418 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000419 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000420 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000421 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000422 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000423 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000424 "[Foo]\n wrong-indent\n")
425 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000426 # read_file on a real file
427 tricky = support.findfile("cfgparser.3")
428 if self.delimiters[0] == '=':
429 error = configparser.ParsingError
430 expected = (tricky,)
431 else:
432 error = configparser.MissingSectionHeaderError
433 expected = (tricky, 1,
434 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000435 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000436 e = self.parse_error(cf, error, f)
437 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000438
Fred Drakea4923622010-08-09 12:52:45 +0000439 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000440 if hasattr(src, 'readline'):
441 sio = src
442 else:
443 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000444 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000445 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000446 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000447
Fred Drakec6f28912002-10-25 19:40:49 +0000448 def test_query_errors(self):
449 cf = self.newconfig()
450 self.assertEqual(cf.sections(), [],
451 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000452 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000453 "new ConfigParser should have no acknowledged "
454 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000455 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000456 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000457 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000458 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000459 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000460 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000461 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000462 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000463 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000464
Fred Drakea4923622010-08-09 12:52:45 +0000465 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000466 try:
Fred Drakea4923622010-08-09 12:52:45 +0000467 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000468 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000469 return e
470 else:
471 self.fail("expected exception type %s.%s"
472 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000473
Fred Drakec6f28912002-10-25 19:40:49 +0000474 def test_boolean(self):
475 cf = self.fromstring(
476 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000477 "T1{equals}1\n"
478 "T2{equals}TRUE\n"
479 "T3{equals}True\n"
480 "T4{equals}oN\n"
481 "T5{equals}yes\n"
482 "F1{equals}0\n"
483 "F2{equals}FALSE\n"
484 "F3{equals}False\n"
485 "F4{equals}oFF\n"
486 "F5{equals}nO\n"
487 "E1{equals}2\n"
488 "E2{equals}foo\n"
489 "E3{equals}-1\n"
490 "E4{equals}0.1\n"
491 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000492 )
493 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000494 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
495 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000496 self.assertRaises(ValueError,
497 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000498
Fred Drakec6f28912002-10-25 19:40:49 +0000499 def test_weird_errors(self):
500 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000501 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000502 with self.assertRaises(configparser.DuplicateSectionError) as cm:
503 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000504 e = cm.exception
505 self.assertEqual(str(e), "Section 'Foo' already exists")
506 self.assertEqual(e.args, ("Foo", None, None))
507
508 if self.strict:
509 with self.assertRaises(configparser.DuplicateSectionError) as cm:
510 cf.read_string(textwrap.dedent("""\
511 [Foo]
512 will this be added{equals}True
513 [Bar]
514 what about this{equals}True
515 [Foo]
516 oops{equals}this won't
517 """.format(equals=self.delimiters[0])), source='<foo-bar>')
518 e = cm.exception
519 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
520 "section 'Foo' already exists")
521 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
522
523 with self.assertRaises(configparser.DuplicateOptionError) as cm:
524 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
525 e = cm.exception
526 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
527 "in section 'Bar' already exists")
528 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000529
530 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000531 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000532 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000533 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000534 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000535 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000536 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000537 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000538 "[Long Line - With Comments!]\n"
539 "test {0[1]} we {comment} can\n"
540 " also {comment} place\n"
541 " comments {comment} in\n"
542 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000543 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
544 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000545 )
Fred Drake03c44a32010-02-19 06:08:41 +0000546 if self.allow_no_value:
547 config_string += (
548 "[Valueless]\n"
549 "option-without-value\n"
550 )
551
552 cf = self.fromstring(config_string)
Guido van Rossum34d19282007-08-09 01:03:29 +0000553 output = io.StringIO()
Fred Drakec6f28912002-10-25 19:40:49 +0000554 cf.write(output)
Fred Drake03c44a32010-02-19 06:08:41 +0000555 expect_string = (
Łukasz Langac264c092010-11-20 16:15:37 +0000556 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000557 "foo {equals} another very\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000558 "\tlong line\n"
559 "\n"
560 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000561 "foo {equals} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000562 "\tlikes it.\n"
563 "\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000564 "[Long Line - With Comments!]\n"
565 "test {equals} we\n"
566 "\talso\n"
567 "\tcomments\n"
568 "\tmultiline\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000569 "\n".format(equals=self.delimiters[0],
570 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000571 )
Fred Drake03c44a32010-02-19 06:08:41 +0000572 if self.allow_no_value:
573 expect_string += (
574 "[Valueless]\n"
575 "option-without-value\n"
576 "\n"
577 )
578 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000579
Fred Drakeabc086f2004-05-18 03:29:52 +0000580 def test_set_string_types(self):
581 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000582 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000583 # Check that we don't get an exception when setting values in
584 # an existing section using strings:
585 class mystr(str):
586 pass
587 cf.set("sect", "option1", "splat")
588 cf.set("sect", "option1", mystr("splat"))
589 cf.set("sect", "option2", "splat")
590 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000591 cf.set("sect", "option1", "splat")
592 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000593
Fred Drake82903142004-05-18 04:24:02 +0000594 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000595 if self.delimiters[0] != '=':
596 # skip reading the file if we're using an incompatible format
597 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000598 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000599 # check when we pass a mix of readable and non-readable files:
600 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000601 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000602 self.assertEqual(parsed_files, [file1])
603 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
604 # check when we pass only a filename:
605 cf = self.newconfig()
606 parsed_files = cf.read(file1)
607 self.assertEqual(parsed_files, [file1])
608 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
609 # check when we pass only missing files:
610 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000611 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000612 self.assertEqual(parsed_files, [])
613 # check when we pass no files:
614 cf = self.newconfig()
615 parsed_files = cf.read([])
616 self.assertEqual(parsed_files, [])
617
Fred Drakec6f28912002-10-25 19:40:49 +0000618 # shared by subclasses
619 def get_interpolation_config(self):
620 return self.fromstring(
621 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000622 "bar{equals}something %(with1)s interpolation (1 step)\n"
623 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
624 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
625 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
626 "with11{equals}%(with10)s\n"
627 "with10{equals}%(with9)s\n"
628 "with9{equals}%(with8)s\n"
629 "with8{equals}%(With7)s\n"
630 "with7{equals}%(WITH6)s\n"
631 "with6{equals}%(with5)s\n"
632 "With5{equals}%(with4)s\n"
633 "WITH4{equals}%(with3)s\n"
634 "with3{equals}%(with2)s\n"
635 "with2{equals}%(with1)s\n"
636 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000637 "\n"
638 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000639 "foo{equals}%(bar)s\n"
640 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000641 "\n"
642 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000643 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000644 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000645
Fred Drake98e3b292002-10-25 20:42:44 +0000646 def check_items_config(self, expected):
647 cf = self.fromstring(
648 "[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000649 "name {0[0]} value\n"
650 "key{0[1]} |%(name)s| \n"
Łukasz Langa5c863392010-11-21 13:41:35 +0000651 "getdefault{0[1]} |%(default)s|\n".format(self.delimiters),
Fred Drake98e3b292002-10-25 20:42:44 +0000652 defaults={"default": "<default>"})
653 L = list(cf.items("section"))
654 L.sort()
655 self.assertEqual(L, expected)
656
Fred Drake8ef67672000-09-27 22:45:25 +0000657
Fred Drakea4923622010-08-09 12:52:45 +0000658class StrictTestCase(BasicTestCase):
659 config_class = configparser.RawConfigParser
660 strict = True
661
662
Georg Brandl96a60ae2010-07-28 13:13:46 +0000663class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000664 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000665
666 def test_interpolation(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000667 rawval = {
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000668 configparser.ConfigParser: ("something %(with11)s "
669 "lots of interpolation (11 steps)"),
Michael Foordbd6c0792010-07-25 23:09:25 +0000670 configparser.SafeConfigParser: "%(with1)s",
671 }
Fred Drakec6f28912002-10-25 19:40:49 +0000672 cf = self.get_interpolation_config()
673 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000674 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
675 eq(cf.get("Foo", "bar9"),
676 "something with lots of interpolation (9 steps)")
677 eq(cf.get("Foo", "bar10"),
678 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000679 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Michael Foordbd6c0792010-07-25 23:09:25 +0000680 self.assertEqual(e.args, ("bar11", "Foo", rawval[self.config_class]))
Fred Drake95b96d32001-02-12 17:23:20 +0000681
Fred Drake54782192002-12-31 06:57:25 +0000682 def test_interpolation_missing_value(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000683 rawval = {
684 configparser.ConfigParser: '%(reference)s',
685 configparser.SafeConfigParser: '',
686 }
Fred Drakea4923622010-08-09 12:52:45 +0000687 cf = self.get_interpolation_config()
688 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000689 "Interpolation Error", "name")
690 self.assertEqual(e.reference, "reference")
691 self.assertEqual(e.section, "Interpolation Error")
692 self.assertEqual(e.option, "name")
Michael Foordbd6c0792010-07-25 23:09:25 +0000693 self.assertEqual(e.args, ('name', 'Interpolation Error',
694 rawval[self.config_class], 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000695
Fred Drake98e3b292002-10-25 20:42:44 +0000696 def test_items(self):
697 self.check_items_config([('default', '<default>'),
698 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000699 ('key', '|value|'),
700 ('name', 'value')])
701
David Goodger1cbf2062004-10-03 15:55:09 +0000702 def test_set_nonstring_types(self):
703 cf = self.newconfig()
704 cf.add_section('non-string')
705 cf.set('non-string', 'int', 1)
706 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
707 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
708 '%(list)': '%(list)'})
709 cf.set('non-string', 'string_with_interpolation', '%(list)s')
710 self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
711 self.assertRaises(TypeError, cf.get, 'non-string', 'int')
712 self.assertEqual(cf.get('non-string', 'list', raw=True),
713 [0, 1, 1, 2, 3, 5, 8, 13, '%('])
714 self.assertRaises(TypeError, cf.get, 'non-string', 'list')
715 self.assertEqual(cf.get('non-string', 'dict', raw=True),
716 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
717 self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
718 self.assertEqual(cf.get('non-string', 'string_with_interpolation',
719 raw=True), '%(list)s')
720 self.assertRaises(ValueError, cf.get, 'non-string',
721 'string_with_interpolation', raw=False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000722 cf.add_section(123)
723 cf.set(123, 'this is sick', True)
724 self.assertEqual(cf.get(123, 'this is sick', raw=True), True)
725 with self.assertRaises(TypeError):
726 cf.get(123, 'this is sick')
727 cf.optionxform = lambda x: x
728 cf.set('non-string', 1, 1)
729 self.assertRaises(TypeError, cf.get, 'non-string', 1, 1)
730 self.assertEqual(cf.get('non-string', 1, raw=True), 1)
David Goodger1cbf2062004-10-03 15:55:09 +0000731
Georg Brandl96a60ae2010-07-28 13:13:46 +0000732class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
733 delimiters = (':=', '$')
734 comment_prefixes = ('//', '"')
735
Łukasz Langac264c092010-11-20 16:15:37 +0000736class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
737 default_section = 'general'
738
Georg Brandl96a60ae2010-07-28 13:13:46 +0000739class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000740 config_class = configparser.ConfigParser
741 wonderful_spam = ("I'm having spam spam spam spam "
742 "spam spam spam beaked beans spam "
743 "spam spam and spam!").replace(' ', '\t\n')
744
745 def setUp(self):
746 cf = self.newconfig()
747 for i in range(100):
748 s = 'section{}'.format(i)
749 cf.add_section(s)
750 for j in range(10):
751 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
752 with open(support.TESTFN, 'w') as f:
753 cf.write(f)
754
755 def tearDown(self):
756 os.unlink(support.TESTFN)
757
758 def test_dominating_multiline_values(self):
759 # We're reading from file because this is where the code changed
760 # during performance updates in Python 3.2
761 cf_from_file = self.newconfig()
762 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000763 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000764 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
765 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000766
Georg Brandl96a60ae2010-07-28 13:13:46 +0000767class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000768 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000769
770 def test_interpolation(self):
771 cf = self.get_interpolation_config()
772 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000773 eq(cf.get("Foo", "bar"),
774 "something %(with1)s interpolation (1 step)")
775 eq(cf.get("Foo", "bar9"),
776 "something %(with9)s lots of interpolation (9 steps)")
777 eq(cf.get("Foo", "bar10"),
778 "something %(with10)s lots of interpolation (10 steps)")
779 eq(cf.get("Foo", "bar11"),
780 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000781
Fred Drake98e3b292002-10-25 20:42:44 +0000782 def test_items(self):
783 self.check_items_config([('default', '<default>'),
784 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000785 ('key', '|%(name)s|'),
786 ('name', 'value')])
787
David Goodger1cbf2062004-10-03 15:55:09 +0000788 def test_set_nonstring_types(self):
789 cf = self.newconfig()
790 cf.add_section('non-string')
791 cf.set('non-string', 'int', 1)
792 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
793 cf.set('non-string', 'dict', {'pi': 3.14159})
794 self.assertEqual(cf.get('non-string', 'int'), 1)
795 self.assertEqual(cf.get('non-string', 'list'),
796 [0, 1, 1, 2, 3, 5, 8, 13])
797 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000798 cf.add_section(123)
799 cf.set(123, 'this is sick', True)
800 self.assertEqual(cf.get(123, 'this is sick'), True)
801 if cf._dict.__class__ is configparser._default_dict:
802 # would not work for SortedDict; only checking for the most common
803 # default dictionary (OrderedDict)
804 cf.optionxform = lambda x: x
805 cf.set('non-string', 1, 1)
806 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +0000807
Georg Brandl96a60ae2010-07-28 13:13:46 +0000808class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
809 delimiters = (':=', '$')
810 comment_prefixes = ('//', '"')
811
812class RawConfigParserTestSambaConf(BasicTestCase):
813 config_class = configparser.RawConfigParser
814 comment_prefixes = ('#', ';', '//', '----')
815 empty_lines_in_values = False
816
817 def test_reading(self):
818 smbconf = support.findfile("cfgparser.2")
819 # check when we pass a mix of readable and non-readable files:
820 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000821 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000822 self.assertEqual(parsed_files, [smbconf])
823 sections = ['global', 'homes', 'printers',
824 'print$', 'pdf-generator', 'tmp', 'Agustin']
825 self.assertEqual(cf.sections(), sections)
826 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
827 self.assertEqual(cf.getint("global", "max log size"), 50)
828 self.assertEqual(cf.get("global", "hosts allow"), "127.")
829 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000830
Fred Drake0eebd5c2002-10-25 21:52:00 +0000831class SafeConfigParserTestCase(ConfigParserTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000832 config_class = configparser.SafeConfigParser
Fred Drake0eebd5c2002-10-25 21:52:00 +0000833
834 def test_safe_interpolation(self):
835 # See http://www.python.org/sf/511737
836 cf = self.fromstring("[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000837 "option1{eq}xxx\n"
838 "option2{eq}%(option1)s/xxx\n"
839 "ok{eq}%(option1)s/%%s\n"
840 "not_ok{eq}%(option2)s/%%s".format(
841 eq=self.delimiters[0]))
Fred Drake0eebd5c2002-10-25 21:52:00 +0000842 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
843 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
844
Guido van Rossumd8faa362007-04-27 19:54:29 +0000845 def test_set_malformatted_interpolation(self):
846 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000847 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000848
849 self.assertEqual(cf.get('sect', "option1"), "foo")
850
851 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
852 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
853 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
854
855 self.assertEqual(cf.get('sect', "option1"), "foo")
856
Georg Brandl1f9fa312009-04-27 16:42:58 +0000857 # bug #5741: double percents are *not* malformed
858 cf.set("sect", "option2", "foo%%bar")
859 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
860
David Goodger1cbf2062004-10-03 15:55:09 +0000861 def test_set_nonstring_types(self):
862 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000863 "option1{eq}foo\n".format(eq=self.delimiters[0]))
David Goodger1cbf2062004-10-03 15:55:09 +0000864 # Check that we get a TypeError when setting non-string values
865 # in an existing section:
866 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
867 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
868 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
869 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
870 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
871 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000872 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
873 self.assertRaises(TypeError, cf.add_section, 123)
David Goodger1cbf2062004-10-03 15:55:09 +0000874
Łukasz Langac264c092010-11-20 16:15:37 +0000875 def test_add_section_default(self):
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000876 cf = self.newconfig()
Łukasz Langac264c092010-11-20 16:15:37 +0000877 self.assertRaises(ValueError, cf.add_section, self.default_section)
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000878
Łukasz Langab6a6f5f2010-12-03 16:28:00 +0000879class SafeConfigParserTestCaseExtendedInterpolation(BasicTestCase):
880 config_class = configparser.SafeConfigParser
881 interpolation = configparser.ExtendedInterpolation()
882 default_section = 'common'
883
884 def test_extended_interpolation(self):
885 cf = self.fromstring(textwrap.dedent("""
886 [common]
887 favourite Beatle = Paul
888 favourite color = green
889
890 [tom]
891 favourite band = ${favourite color} day
892 favourite pope = John ${favourite Beatle} II
893 sequel = ${favourite pope}I
894
895 [ambv]
896 favourite Beatle = George
897 son of Edward VII = ${favourite Beatle} V
898 son of George V = ${son of Edward VII}I
899
900 [stanley]
901 favourite Beatle = ${ambv:favourite Beatle}
902 favourite pope = ${tom:favourite pope}
903 favourite color = black
904 favourite state of mind = paranoid
905 favourite movie = soylent ${common:favourite color}
906 favourite song = ${favourite color} sabbath - ${favourite state of mind}
907 """).strip())
908
909 eq = self.assertEqual
910 eq(cf['common']['favourite Beatle'], 'Paul')
911 eq(cf['common']['favourite color'], 'green')
912 eq(cf['tom']['favourite Beatle'], 'Paul')
913 eq(cf['tom']['favourite color'], 'green')
914 eq(cf['tom']['favourite band'], 'green day')
915 eq(cf['tom']['favourite pope'], 'John Paul II')
916 eq(cf['tom']['sequel'], 'John Paul III')
917 eq(cf['ambv']['favourite Beatle'], 'George')
918 eq(cf['ambv']['favourite color'], 'green')
919 eq(cf['ambv']['son of Edward VII'], 'George V')
920 eq(cf['ambv']['son of George V'], 'George VI')
921 eq(cf['stanley']['favourite Beatle'], 'George')
922 eq(cf['stanley']['favourite color'], 'black')
923 eq(cf['stanley']['favourite state of mind'], 'paranoid')
924 eq(cf['stanley']['favourite movie'], 'soylent green')
925 eq(cf['stanley']['favourite pope'], 'John Paul II')
926 eq(cf['stanley']['favourite song'],
927 'black sabbath - paranoid')
928
929 def test_endless_loop(self):
930 cf = self.fromstring(textwrap.dedent("""
931 [one for you]
932 ping = ${one for me:pong}
933
934 [one for me]
935 pong = ${one for you:ping}
936 """).strip())
937
938 with self.assertRaises(configparser.InterpolationDepthError):
939 cf['one for you']['ping']
940
941
942
Georg Brandl96a60ae2010-07-28 13:13:46 +0000943class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
944 delimiters = (':=', '$')
945 comment_prefixes = ('//', '"')
Fred Drake03c44a32010-02-19 06:08:41 +0000946
947class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase):
948 allow_no_value = True
949
Georg Brandl8dcaa732010-07-29 12:17:40 +0000950class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
951 config_class = configparser.SafeConfigParser
952 delimiters = {'='}
953 comment_prefixes = {'#'}
954 allow_no_value = True
955
956 def test_cfgparser_dot_3(self):
957 tricky = support.findfile("cfgparser.3")
958 cf = self.newconfig()
959 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
960 self.assertEqual(cf.sections(), ['strange',
961 'corruption',
962 'yeah, sections can be '
963 'indented as well',
964 'another one!',
965 'no values here',
966 'tricky interpolation',
967 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +0000968 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +0000969 vars={'interpolate': '-1'}), -1)
970 with self.assertRaises(ValueError):
971 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +0000972 cf.getint(self.default_section, 'go', raw=True,
973 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +0000974 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
975 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
976 longname = 'yeah, sections can be indented as well'
977 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000978 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +0000979 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
980 # `go` from DEFAULT
981 with self.assertRaises(configparser.InterpolationMissingOptionError):
982 cf.items('no values here')
983 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
984 self.assertEqual(cf.get('tricky interpolation', 'lets'),
985 cf.get('tricky interpolation', 'go'))
986 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
987
988 def test_unicode_failure(self):
989 tricky = support.findfile("cfgparser.3")
990 cf = self.newconfig()
991 with self.assertRaises(UnicodeDecodeError):
992 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +0000993
Fred Drake88444412010-09-03 04:22:36 +0000994
995class Issue7005TestCase(unittest.TestCase):
996 """Test output when None is set() as a value and allow_no_value == False.
997
998 http://bugs.python.org/issue7005
999
1000 """
1001
1002 expected_output = "[section]\noption = None\n\n"
1003
1004 def prepare(self, config_class):
1005 # This is the default, but that's the point.
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001006 with warnings.catch_warnings():
1007 warnings.simplefilter("ignore", category=DeprecationWarning)
1008 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001009 cp.add_section("section")
1010 cp.set("section", "option", None)
1011 sio = io.StringIO()
1012 cp.write(sio)
1013 return sio.getvalue()
1014
1015 def test_none_as_value_stringified(self):
1016 output = self.prepare(configparser.ConfigParser)
1017 self.assertEqual(output, self.expected_output)
1018
1019 def test_none_as_value_stringified_raw(self):
1020 output = self.prepare(configparser.RawConfigParser)
1021 self.assertEqual(output, self.expected_output)
1022
1023
Thomas Wouters89f507f2006-12-13 04:49:30 +00001024class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001025 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001026
1027 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001028 cf = self.fromstring("[b]\n"
1029 "o4=1\n"
1030 "o3=2\n"
1031 "o2=3\n"
1032 "o1=4\n"
1033 "[a]\n"
1034 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001035 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001036 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001037 self.assertEqual(output.getvalue(),
1038 "[a]\n"
1039 "k = v\n\n"
1040 "[b]\n"
1041 "o1 = 4\n"
1042 "o2 = 3\n"
1043 "o3 = 2\n"
1044 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001045
Fred Drake03c44a32010-02-19 06:08:41 +00001046
Georg Brandl96a60ae2010-07-28 13:13:46 +00001047class CompatibleTestCase(CfgParserTestCaseClass):
1048 config_class = configparser.RawConfigParser
Fred Drakecc645b92010-09-04 04:35:34 +00001049 comment_prefixes = configparser._COMPATIBLE
Georg Brandl96a60ae2010-07-28 13:13:46 +00001050
1051 def test_comment_handling(self):
1052 config_string = textwrap.dedent("""\
1053 [Commented Bar]
1054 baz=qwe ; a comment
1055 foo: bar # not a comment!
1056 # but this is a comment
1057 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001058 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001059 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001060 """)
1061 cf = self.fromstring(config_string)
1062 self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!')
1063 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001064 self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001065
1066
Fred Drakec6f28912002-10-25 19:40:49 +00001067def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001068 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001069 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001070 ConfigParserTestCaseNonStandardDelimiters,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001071 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001072 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001073 RawConfigParserTestCaseNonStandardDelimiters,
1074 RawConfigParserTestSambaConf,
Thomas Wouters89f507f2006-12-13 04:49:30 +00001075 SafeConfigParserTestCase,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001076 SafeConfigParserTestCaseExtendedInterpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001077 SafeConfigParserTestCaseNonStandardDelimiters,
Fred Drake03c44a32010-02-19 06:08:41 +00001078 SafeConfigParserTestCaseNoValue,
Georg Brandl8dcaa732010-07-29 12:17:40 +00001079 SafeConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001080 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001081 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001082 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001083 CompatibleTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001084 ConfigParserTestCaseNonStandardDefaultSection,
Fred Drake03c44a32010-02-19 06:08:41 +00001085 )
1086
Fred Drake3af0eb82002-10-25 18:09:24 +00001087
Fred Drakec6f28912002-10-25 19:40:49 +00001088if __name__ == "__main__":
1089 test_main()