blob: 7b91518dacc8c73943c63acbcc081168c9692f75 [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
Łukasz Langa535c0772010-12-04 13:48:13 +00005import sys
Georg Brandl96a60ae2010-07-28 13:13:46 +00006import textwrap
Łukasz Langa535c0772010-12-04 13:48:13 +00007import unittest
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00008import warnings
Fred Drake8ef67672000-09-27 22:45:25 +00009
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Fred Drake3d5f7e82000-12-04 16:30:40 +000011
Raymond Hettingerf80680d2008-02-06 00:07:11 +000012class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000013
Thomas Wouters89f507f2006-12-13 04:49:30 +000014 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000015 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000016
17 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000018 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000019
Thomas Wouters89f507f2006-12-13 04:49:30 +000020 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000021 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000022
23 def iteritems(self): return iter(self.items())
24 def iterkeys(self): return iter(self.keys())
25 __iter__ = iterkeys
26 def itervalues(self): return iter(self.values())
Fred Drake3d5f7e82000-12-04 16:30:40 +000027
Fred Drake03c44a32010-02-19 06:08:41 +000028
Georg Brandl96a60ae2010-07-28 13:13:46 +000029class CfgParserTestCaseClass(unittest.TestCase):
Fred Drake03c44a32010-02-19 06:08:41 +000030 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000031 delimiters = ('=', ':')
32 comment_prefixes = (';', '#')
33 empty_lines_in_values = True
34 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000035 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000036 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000037 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000038
Fred Drakec6f28912002-10-25 19:40:49 +000039 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000040 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000041 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000042 allow_no_value=self.allow_no_value,
43 delimiters=self.delimiters,
44 comment_prefixes=self.comment_prefixes,
45 empty_lines_in_values=self.empty_lines_in_values,
46 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000047 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000048 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000049 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000050 )
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000051 with warnings.catch_warnings():
52 warnings.simplefilter("ignore", category=DeprecationWarning)
53 instance = self.config_class(**arguments)
54 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000055
Fred Drakec6f28912002-10-25 19:40:49 +000056 def fromstring(self, string, defaults=None):
57 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000058 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000059 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000060
Georg Brandl96a60ae2010-07-28 13:13:46 +000061class BasicTestCase(CfgParserTestCaseClass):
62
Fred Drakea4923622010-08-09 12:52:45 +000063 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000064 E = ['Commented Bar',
65 'Foo Bar',
66 'Internationalized Stuff',
67 'Long Line',
68 'Section\\with$weird%characters[\t',
69 'Spaces',
70 'Spacey Bar',
71 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000072 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000073 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000074
Fred Drake03c44a32010-02-19 06:08:41 +000075 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000076 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000077 E.sort()
Łukasz Langa26d513c2010-11-10 18:57:39 +000078
79 # API access
80 L = cf.sections()
81 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000082 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000083 eq(L, E)
Fred Drake8ef67672000-09-27 22:45:25 +000084
Łukasz Langa26d513c2010-11-10 18:57:39 +000085 # mapping access
86 L = [section for section in cf]
87 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +000088 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +000089 E.sort()
90 eq(L, E)
91
Fred Drakec6f28912002-10-25 19:40:49 +000092 # The use of spaces in the section names serves as a
93 # regression test for SourceForge bug #583248:
94 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +000095
96 # API access
97 eq(cf.get('Foo Bar', 'foo'), 'bar1')
98 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
99 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000100 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000101 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000102 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000103 eq(cf.get('Spaces', 'key with spaces'), 'value')
104 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000105 eq(cf.getint('Types', 'int'), 42)
106 eq(cf.get('Types', 'int'), "42")
107 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
108 eq(cf.get('Types', 'float'), "0.44")
109 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000110 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000111 if self.allow_no_value:
112 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000113
Łukasz Langa26d513c2010-11-10 18:57:39 +0000114 # test vars= and fallback=
115 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000116 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
117 with self.assertRaises(configparser.NoSectionError):
118 cf.get('No Such Foo Bar', 'foo')
119 with self.assertRaises(configparser.NoOptionError):
120 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000121 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
122 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
123 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
124 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
125 eq(cf.getint('Types', 'int', fallback=18), 42)
126 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
127 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Fred Drakecc645b92010-09-04 04:35:34 +0000128 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000129 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000130 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000131 fallback=0.0), 0.0)
132 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
133 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
134 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000135 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000136 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
137 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000138 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000139 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000140 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000141 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000142
Łukasz Langa26d513c2010-11-10 18:57:39 +0000143 # mapping access
144 eq(cf['Foo Bar']['foo'], 'bar1')
145 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000146 section = cf['Spacey Bar From The Beginning']
147 eq(section.name, 'Spacey Bar From The Beginning')
148 self.assertIs(section.parser, cf)
149 with self.assertRaises(AttributeError):
150 section.name = 'Name is read-only'
151 with self.assertRaises(AttributeError):
152 section.parser = 'Parser is read-only'
153 eq(section['foo'], 'bar3')
154 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000155 eq(cf['Commented Bar']['foo'], 'bar4')
156 eq(cf['Commented Bar']['baz'], 'qwe')
157 eq(cf['Spaces']['key with spaces'], 'value')
158 eq(cf['Spaces']['another with spaces'], 'splat!')
159 eq(cf['Long Line']['foo'],
160 'this line is much, much longer than my editor\nlikes it.')
161 if self.allow_no_value:
162 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000163 # test vars= and fallback=
164 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
165 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
166 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
167 with self.assertRaises(KeyError):
168 cf['No Such Foo Bar']['foo']
169 with self.assertRaises(KeyError):
170 cf['Foo Bar']['no-such-foo']
171 with self.assertRaises(KeyError):
172 cf['No Such Foo Bar'].get('foo', fallback='baz')
173 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
174 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
175 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
176 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
177 with self.assertRaises(KeyError):
178 cf['No Such Spacey Bar'].get('foo', None)
179 eq(cf['Types'].getint('int', 18), 42)
180 eq(cf['Types'].getint('int', fallback=18), 42)
181 eq(cf['Types'].getint('no-such-int', 18), 18)
182 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
183 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
184 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
185 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
186 self.assertAlmostEqual(cf['Types'].getfloat('float',
187 fallback=0.0), 0.44)
188 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
189 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
190 fallback=0.0), 0.0)
191 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
192 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
193 eq(cf['Types'].getboolean('boolean', True), False)
194 eq(cf['Types'].getboolean('boolean', fallback=True), False)
195 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
196 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
197 "yes") # sic!
198 eq(cf['Types'].getboolean('no-such-boolean', True), True)
199 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
200 if self.allow_no_value:
201 eq(cf['NoValue'].get('option-without-value', False), None)
202 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
203 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
204 eq(cf['NoValue'].get('no-such-option-without-value',
205 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000206
Fred Drakec6f28912002-10-25 19:40:49 +0000207 # Make sure the right things happen for remove_option();
208 # added to include check for SourceForge bug #123324:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000209
210 # API acceess
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000211 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000212 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000213 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000214 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000215 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000216 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000217 " that was removed")
218
Michael Foordbd6c0792010-07-25 23:09:25 +0000219 with self.assertRaises(configparser.NoSectionError) as cm:
220 cf.remove_option('No Such Section', 'foo')
221 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000222
223 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000224 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000225
Łukasz Langa26d513c2010-11-10 18:57:39 +0000226 # mapping access
227 del cf['Spacey Bar']['foo']
228 self.assertFalse('foo' in cf['Spacey Bar'])
229 with self.assertRaises(KeyError):
230 del cf['Spacey Bar']['foo']
231 with self.assertRaises(KeyError):
232 del cf['No Such Section']['foo']
233
Fred Drakea4923622010-08-09 12:52:45 +0000234 def test_basic(self):
235 config_string = """\
236[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000237foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000238[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000239foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000240[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000241 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000242 baz {0[0]} qwe
243[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000244foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000245baz{0[0]}qwe {1[0]}another one
246[Long Line]
247foo{0[1]} this line is much, much longer than my editor
248 likes it.
249[Section\\with$weird%characters[\t]
250[Internationalized Stuff]
251foo[bg]{0[1]} Bulgarian
252foo{0[0]}Default
253foo[en]{0[0]}English
254foo[de]{0[0]}Deutsch
255[Spaces]
256key with spaces {0[1]} value
257another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000258[Types]
259int {0[1]} 42
260float {0[0]} 0.44
261boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000262123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000263""".format(self.delimiters, self.comment_prefixes)
264 if self.allow_no_value:
265 config_string += (
266 "[NoValue]\n"
267 "option-without-value\n"
268 )
269 cf = self.fromstring(config_string)
270 self.basic_test(cf)
271 if self.strict:
272 with self.assertRaises(configparser.DuplicateOptionError):
273 cf.read_string(textwrap.dedent("""\
274 [Duplicate Options Here]
275 option {0[0]} with a value
276 option {0[1]} with another value
277 """.format(self.delimiters)))
278 with self.assertRaises(configparser.DuplicateSectionError):
279 cf.read_string(textwrap.dedent("""\
280 [And Now For Something]
281 completely different {0[0]} True
282 [And Now For Something]
283 the larch {0[1]} 1
284 """.format(self.delimiters)))
285 else:
286 cf.read_string(textwrap.dedent("""\
287 [Duplicate Options Here]
288 option {0[0]} with a value
289 option {0[1]} with another value
290 """.format(self.delimiters)))
291
292 cf.read_string(textwrap.dedent("""\
293 [And Now For Something]
294 completely different {0[0]} True
295 [And Now For Something]
296 the larch {0[1]} 1
297 """.format(self.delimiters)))
298
299 def test_basic_from_dict(self):
300 config = {
301 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000302 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000303 },
304 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000305 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000306 },
307 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000308 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000309 "baz": "qwe",
310 },
311 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000312 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000313 "baz": "qwe",
314 },
315 "Long Line": {
316 "foo": "this line is much, much longer than my editor\nlikes "
317 "it.",
318 },
319 "Section\\with$weird%characters[\t": {
320 },
321 "Internationalized Stuff": {
322 "foo[bg]": "Bulgarian",
323 "foo": "Default",
324 "foo[en]": "English",
325 "foo[de]": "Deutsch",
326 },
327 "Spaces": {
328 "key with spaces": "value",
329 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000330 },
331 "Types": {
332 "int": 42,
333 "float": 0.44,
334 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000335 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000336 },
Fred Drakea4923622010-08-09 12:52:45 +0000337 }
338 if self.allow_no_value:
339 config.update({
340 "NoValue": {
341 "option-without-value": None,
342 }
343 })
344 cf = self.newconfig()
345 cf.read_dict(config)
346 self.basic_test(cf)
347 if self.strict:
348 with self.assertRaises(configparser.DuplicateOptionError):
349 cf.read_dict({
350 "Duplicate Options Here": {
351 'option': 'with a value',
352 'OPTION': 'with another value',
353 },
354 })
355 else:
356 cf.read_dict({
357 "Duplicate Options Here": {
358 'option': 'with a value',
359 'OPTION': 'with another value',
360 },
361 })
362
363
Fred Drakec6f28912002-10-25 19:40:49 +0000364 def test_case_sensitivity(self):
365 cf = self.newconfig()
366 cf.add_section("A")
367 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000368 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000369 L = cf.sections()
370 L.sort()
371 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000372 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000373 cf.set("a", "B", "value")
374 eq(cf.options("a"), ["b"])
375 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000376 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000377 with self.assertRaises(configparser.NoSectionError):
378 # section names are case-sensitive
379 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000380 self.assertTrue(cf.has_option("a", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000381 cf.set("A", "A-B", "A-B value")
382 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000383 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000384 cf.has_option("A", opt),
385 "has_option() returned false for option which should exist")
386 eq(cf.options("A"), ["a-b"])
387 eq(cf.options("a"), ["b"])
388 cf.remove_option("a", "B")
389 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000390
Fred Drakec6f28912002-10-25 19:40:49 +0000391 # SF bug #432369:
392 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000393 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000394 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000395 eq(cf.options("MySection"), ["option"])
396 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000397
Fred Drakec6f28912002-10-25 19:40:49 +0000398 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000399 cf = self.fromstring("[section]\n"
400 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000401 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000402 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000403
Fred Drake3c823aa2001-02-26 21:55:34 +0000404
Łukasz Langa26d513c2010-11-10 18:57:39 +0000405 def test_case_sensitivity_mapping_access(self):
406 cf = self.newconfig()
407 cf["A"] = {}
408 cf["a"] = {"B": "value"}
409 cf["B"] = {}
410 L = [section for section in cf]
411 L.sort()
412 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000413 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000414 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000415 eq(cf["a"].keys(), {"b"})
416 eq(cf["a"]["b"], "value",
417 "could not locate option, expecting case-insensitive option names")
418 with self.assertRaises(KeyError):
419 # section names are case-sensitive
420 cf["b"]["A"] = "value"
421 self.assertTrue("b" in cf["a"])
422 cf["A"]["A-B"] = "A-B value"
423 for opt in ("a-b", "A-b", "a-B", "A-B"):
424 self.assertTrue(
425 opt in cf["A"],
426 "has_option() returned false for option which should exist")
427 eq(cf["A"].keys(), {"a-b"})
428 eq(cf["a"].keys(), {"b"})
429 del cf["a"]["B"]
430 elem_eq(cf["a"].keys(), {})
431
432 # SF bug #432369:
433 cf = self.fromstring(
434 "[MySection]\nOption{} first line \n\tsecond line \n".format(
435 self.delimiters[0]))
436 eq(cf["MySection"].keys(), {"option"})
437 eq(cf["MySection"]["Option"], "first line\nsecond line")
438
439 # SF bug #561822:
440 cf = self.fromstring("[section]\n"
441 "nekey{}nevalue\n".format(self.delimiters[0]),
442 defaults={"key":"value"})
443 self.assertTrue("Key" in cf["section"])
444
David Goodger68a1abd2004-10-03 15:40:25 +0000445 def test_default_case_sensitivity(self):
446 cf = self.newconfig({"foo": "Bar"})
447 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000448 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000449 "could not locate option, expecting case-insensitive option names")
450 cf = self.newconfig({"Foo": "Bar"})
451 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000452 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000453 "could not locate option, expecting case-insensitive defaults")
454
Fred Drakec6f28912002-10-25 19:40:49 +0000455 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000456 cf = self.newconfig()
457 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000458 "[Foo]\n"
459 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000460 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000461 "[Foo]\n"
462 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000463 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000464 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000465 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000466 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000467 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000468 "[Foo]\n wrong-indent\n")
469 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000470 # read_file on a real file
471 tricky = support.findfile("cfgparser.3")
472 if self.delimiters[0] == '=':
473 error = configparser.ParsingError
474 expected = (tricky,)
475 else:
476 error = configparser.MissingSectionHeaderError
477 expected = (tricky, 1,
478 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000479 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000480 e = self.parse_error(cf, error, f)
481 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000482
Fred Drakea4923622010-08-09 12:52:45 +0000483 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000484 if hasattr(src, 'readline'):
485 sio = src
486 else:
487 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000488 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000489 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000490 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000491
Fred Drakec6f28912002-10-25 19:40:49 +0000492 def test_query_errors(self):
493 cf = self.newconfig()
494 self.assertEqual(cf.sections(), [],
495 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000496 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000497 "new ConfigParser should have no acknowledged "
498 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000499 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000500 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000501 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000502 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000503 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000504 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000505 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000506 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000507 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000508
Fred Drakea4923622010-08-09 12:52:45 +0000509 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000510 try:
Fred Drakea4923622010-08-09 12:52:45 +0000511 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000512 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000513 return e
514 else:
515 self.fail("expected exception type %s.%s"
516 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000517
Fred Drakec6f28912002-10-25 19:40:49 +0000518 def test_boolean(self):
519 cf = self.fromstring(
520 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000521 "T1{equals}1\n"
522 "T2{equals}TRUE\n"
523 "T3{equals}True\n"
524 "T4{equals}oN\n"
525 "T5{equals}yes\n"
526 "F1{equals}0\n"
527 "F2{equals}FALSE\n"
528 "F3{equals}False\n"
529 "F4{equals}oFF\n"
530 "F5{equals}nO\n"
531 "E1{equals}2\n"
532 "E2{equals}foo\n"
533 "E3{equals}-1\n"
534 "E4{equals}0.1\n"
535 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000536 )
537 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000538 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
539 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000540 self.assertRaises(ValueError,
541 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000542
Fred Drakec6f28912002-10-25 19:40:49 +0000543 def test_weird_errors(self):
544 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000545 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000546 with self.assertRaises(configparser.DuplicateSectionError) as cm:
547 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000548 e = cm.exception
549 self.assertEqual(str(e), "Section 'Foo' already exists")
550 self.assertEqual(e.args, ("Foo", None, None))
551
552 if self.strict:
553 with self.assertRaises(configparser.DuplicateSectionError) as cm:
554 cf.read_string(textwrap.dedent("""\
555 [Foo]
556 will this be added{equals}True
557 [Bar]
558 what about this{equals}True
559 [Foo]
560 oops{equals}this won't
561 """.format(equals=self.delimiters[0])), source='<foo-bar>')
562 e = cm.exception
563 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
564 "section 'Foo' already exists")
565 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
566
567 with self.assertRaises(configparser.DuplicateOptionError) as cm:
568 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
569 e = cm.exception
570 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
571 "in section 'Bar' already exists")
572 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000573
574 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000575 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000576 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000577 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000578 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000579 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000580 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000581 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000582 "[Long Line - With Comments!]\n"
583 "test {0[1]} we {comment} can\n"
584 " also {comment} place\n"
585 " comments {comment} in\n"
586 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000587 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
588 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000589 )
Fred Drake03c44a32010-02-19 06:08:41 +0000590 if self.allow_no_value:
591 config_string += (
592 "[Valueless]\n"
593 "option-without-value\n"
594 )
595
596 cf = self.fromstring(config_string)
Guido van Rossum34d19282007-08-09 01:03:29 +0000597 output = io.StringIO()
Fred Drakec6f28912002-10-25 19:40:49 +0000598 cf.write(output)
Fred Drake03c44a32010-02-19 06:08:41 +0000599 expect_string = (
Łukasz Langac264c092010-11-20 16:15:37 +0000600 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000601 "foo {equals} another very\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000602 "\tlong line\n"
603 "\n"
604 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000605 "foo {equals} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000606 "\tlikes it.\n"
607 "\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000608 "[Long Line - With Comments!]\n"
609 "test {equals} we\n"
610 "\talso\n"
611 "\tcomments\n"
612 "\tmultiline\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000613 "\n".format(equals=self.delimiters[0],
614 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000615 )
Fred Drake03c44a32010-02-19 06:08:41 +0000616 if self.allow_no_value:
617 expect_string += (
618 "[Valueless]\n"
619 "option-without-value\n"
620 "\n"
621 )
622 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000623
Fred Drakeabc086f2004-05-18 03:29:52 +0000624 def test_set_string_types(self):
625 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000626 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000627 # Check that we don't get an exception when setting values in
628 # an existing section using strings:
629 class mystr(str):
630 pass
631 cf.set("sect", "option1", "splat")
632 cf.set("sect", "option1", mystr("splat"))
633 cf.set("sect", "option2", "splat")
634 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000635 cf.set("sect", "option1", "splat")
636 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000637
Fred Drake82903142004-05-18 04:24:02 +0000638 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000639 if self.delimiters[0] != '=':
640 # skip reading the file if we're using an incompatible format
641 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000642 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000643 # check when we pass a mix of readable and non-readable files:
644 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000645 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000646 self.assertEqual(parsed_files, [file1])
647 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
648 # check when we pass only a filename:
649 cf = self.newconfig()
650 parsed_files = cf.read(file1)
651 self.assertEqual(parsed_files, [file1])
652 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
653 # check when we pass only missing files:
654 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000655 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000656 self.assertEqual(parsed_files, [])
657 # check when we pass no files:
658 cf = self.newconfig()
659 parsed_files = cf.read([])
660 self.assertEqual(parsed_files, [])
661
Fred Drakec6f28912002-10-25 19:40:49 +0000662 # shared by subclasses
663 def get_interpolation_config(self):
664 return self.fromstring(
665 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000666 "bar{equals}something %(with1)s interpolation (1 step)\n"
667 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
668 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
669 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
670 "with11{equals}%(with10)s\n"
671 "with10{equals}%(with9)s\n"
672 "with9{equals}%(with8)s\n"
673 "with8{equals}%(With7)s\n"
674 "with7{equals}%(WITH6)s\n"
675 "with6{equals}%(with5)s\n"
676 "With5{equals}%(with4)s\n"
677 "WITH4{equals}%(with3)s\n"
678 "with3{equals}%(with2)s\n"
679 "with2{equals}%(with1)s\n"
680 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000681 "\n"
682 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000683 "foo{equals}%(bar)s\n"
684 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000685 "\n"
686 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000687 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000688 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000689
Fred Drake98e3b292002-10-25 20:42:44 +0000690 def check_items_config(self, expected):
691 cf = self.fromstring(
692 "[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000693 "name {0[0]} value\n"
694 "key{0[1]} |%(name)s| \n"
Łukasz Langa5c863392010-11-21 13:41:35 +0000695 "getdefault{0[1]} |%(default)s|\n".format(self.delimiters),
Fred Drake98e3b292002-10-25 20:42:44 +0000696 defaults={"default": "<default>"})
697 L = list(cf.items("section"))
698 L.sort()
699 self.assertEqual(L, expected)
700
Fred Drake8ef67672000-09-27 22:45:25 +0000701
Fred Drakea4923622010-08-09 12:52:45 +0000702class StrictTestCase(BasicTestCase):
703 config_class = configparser.RawConfigParser
704 strict = True
705
706
Georg Brandl96a60ae2010-07-28 13:13:46 +0000707class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000708 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000709
710 def test_interpolation(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000711 rawval = {
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000712 configparser.ConfigParser: ("something %(with11)s "
713 "lots of interpolation (11 steps)"),
Michael Foordbd6c0792010-07-25 23:09:25 +0000714 configparser.SafeConfigParser: "%(with1)s",
715 }
Fred Drakec6f28912002-10-25 19:40:49 +0000716 cf = self.get_interpolation_config()
717 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000718 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
719 eq(cf.get("Foo", "bar9"),
720 "something with lots of interpolation (9 steps)")
721 eq(cf.get("Foo", "bar10"),
722 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000723 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Michael Foordbd6c0792010-07-25 23:09:25 +0000724 self.assertEqual(e.args, ("bar11", "Foo", rawval[self.config_class]))
Fred Drake95b96d32001-02-12 17:23:20 +0000725
Fred Drake54782192002-12-31 06:57:25 +0000726 def test_interpolation_missing_value(self):
Michael Foordbd6c0792010-07-25 23:09:25 +0000727 rawval = {
728 configparser.ConfigParser: '%(reference)s',
729 configparser.SafeConfigParser: '',
730 }
Fred Drakea4923622010-08-09 12:52:45 +0000731 cf = self.get_interpolation_config()
732 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000733 "Interpolation Error", "name")
734 self.assertEqual(e.reference, "reference")
735 self.assertEqual(e.section, "Interpolation Error")
736 self.assertEqual(e.option, "name")
Michael Foordbd6c0792010-07-25 23:09:25 +0000737 self.assertEqual(e.args, ('name', 'Interpolation Error',
738 rawval[self.config_class], 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000739
Fred Drake98e3b292002-10-25 20:42:44 +0000740 def test_items(self):
741 self.check_items_config([('default', '<default>'),
742 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000743 ('key', '|value|'),
744 ('name', 'value')])
745
David Goodger1cbf2062004-10-03 15:55:09 +0000746 def test_set_nonstring_types(self):
747 cf = self.newconfig()
748 cf.add_section('non-string')
749 cf.set('non-string', 'int', 1)
750 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%('])
751 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1,
752 '%(list)': '%(list)'})
753 cf.set('non-string', 'string_with_interpolation', '%(list)s')
754 self.assertEqual(cf.get('non-string', 'int', raw=True), 1)
755 self.assertRaises(TypeError, cf.get, 'non-string', 'int')
756 self.assertEqual(cf.get('non-string', 'list', raw=True),
757 [0, 1, 1, 2, 3, 5, 8, 13, '%('])
758 self.assertRaises(TypeError, cf.get, 'non-string', 'list')
759 self.assertEqual(cf.get('non-string', 'dict', raw=True),
760 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'})
761 self.assertRaises(TypeError, cf.get, 'non-string', 'dict')
762 self.assertEqual(cf.get('non-string', 'string_with_interpolation',
763 raw=True), '%(list)s')
764 self.assertRaises(ValueError, cf.get, 'non-string',
765 'string_with_interpolation', raw=False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000766 cf.add_section(123)
767 cf.set(123, 'this is sick', True)
768 self.assertEqual(cf.get(123, 'this is sick', raw=True), True)
769 with self.assertRaises(TypeError):
770 cf.get(123, 'this is sick')
771 cf.optionxform = lambda x: x
772 cf.set('non-string', 1, 1)
773 self.assertRaises(TypeError, cf.get, 'non-string', 1, 1)
774 self.assertEqual(cf.get('non-string', 1, raw=True), 1)
David Goodger1cbf2062004-10-03 15:55:09 +0000775
Georg Brandl96a60ae2010-07-28 13:13:46 +0000776class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
777 delimiters = (':=', '$')
778 comment_prefixes = ('//', '"')
779
Łukasz Langac264c092010-11-20 16:15:37 +0000780class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
781 default_section = 'general'
782
Georg Brandl96a60ae2010-07-28 13:13:46 +0000783class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000784 config_class = configparser.ConfigParser
785 wonderful_spam = ("I'm having spam spam spam spam "
786 "spam spam spam beaked beans spam "
787 "spam spam and spam!").replace(' ', '\t\n')
788
789 def setUp(self):
790 cf = self.newconfig()
791 for i in range(100):
792 s = 'section{}'.format(i)
793 cf.add_section(s)
794 for j in range(10):
795 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
796 with open(support.TESTFN, 'w') as f:
797 cf.write(f)
798
799 def tearDown(self):
800 os.unlink(support.TESTFN)
801
802 def test_dominating_multiline_values(self):
803 # We're reading from file because this is where the code changed
804 # during performance updates in Python 3.2
805 cf_from_file = self.newconfig()
806 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000807 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000808 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
809 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000810
Georg Brandl96a60ae2010-07-28 13:13:46 +0000811class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000812 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000813
814 def test_interpolation(self):
815 cf = self.get_interpolation_config()
816 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000817 eq(cf.get("Foo", "bar"),
818 "something %(with1)s interpolation (1 step)")
819 eq(cf.get("Foo", "bar9"),
820 "something %(with9)s lots of interpolation (9 steps)")
821 eq(cf.get("Foo", "bar10"),
822 "something %(with10)s lots of interpolation (10 steps)")
823 eq(cf.get("Foo", "bar11"),
824 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000825
Fred Drake98e3b292002-10-25 20:42:44 +0000826 def test_items(self):
827 self.check_items_config([('default', '<default>'),
828 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000829 ('key', '|%(name)s|'),
830 ('name', 'value')])
831
David Goodger1cbf2062004-10-03 15:55:09 +0000832 def test_set_nonstring_types(self):
833 cf = self.newconfig()
834 cf.add_section('non-string')
835 cf.set('non-string', 'int', 1)
836 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
837 cf.set('non-string', 'dict', {'pi': 3.14159})
838 self.assertEqual(cf.get('non-string', 'int'), 1)
839 self.assertEqual(cf.get('non-string', 'list'),
840 [0, 1, 1, 2, 3, 5, 8, 13])
841 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000842 cf.add_section(123)
843 cf.set(123, 'this is sick', True)
844 self.assertEqual(cf.get(123, 'this is sick'), True)
845 if cf._dict.__class__ is configparser._default_dict:
846 # would not work for SortedDict; only checking for the most common
847 # default dictionary (OrderedDict)
848 cf.optionxform = lambda x: x
849 cf.set('non-string', 1, 1)
850 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +0000851
Georg Brandl96a60ae2010-07-28 13:13:46 +0000852class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
853 delimiters = (':=', '$')
854 comment_prefixes = ('//', '"')
855
856class RawConfigParserTestSambaConf(BasicTestCase):
857 config_class = configparser.RawConfigParser
858 comment_prefixes = ('#', ';', '//', '----')
859 empty_lines_in_values = False
860
861 def test_reading(self):
862 smbconf = support.findfile("cfgparser.2")
863 # check when we pass a mix of readable and non-readable files:
864 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000865 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000866 self.assertEqual(parsed_files, [smbconf])
867 sections = ['global', 'homes', 'printers',
868 'print$', 'pdf-generator', 'tmp', 'Agustin']
869 self.assertEqual(cf.sections(), sections)
870 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
871 self.assertEqual(cf.getint("global", "max log size"), 50)
872 self.assertEqual(cf.get("global", "hosts allow"), "127.")
873 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000874
Fred Drake0eebd5c2002-10-25 21:52:00 +0000875class SafeConfigParserTestCase(ConfigParserTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000876 config_class = configparser.SafeConfigParser
Fred Drake0eebd5c2002-10-25 21:52:00 +0000877
878 def test_safe_interpolation(self):
879 # See http://www.python.org/sf/511737
880 cf = self.fromstring("[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000881 "option1{eq}xxx\n"
882 "option2{eq}%(option1)s/xxx\n"
883 "ok{eq}%(option1)s/%%s\n"
884 "not_ok{eq}%(option2)s/%%s".format(
885 eq=self.delimiters[0]))
Fred Drake0eebd5c2002-10-25 21:52:00 +0000886 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
887 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
888
Guido van Rossumd8faa362007-04-27 19:54:29 +0000889 def test_set_malformatted_interpolation(self):
890 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000891 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000892
893 self.assertEqual(cf.get('sect', "option1"), "foo")
894
895 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
896 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
897 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
898
899 self.assertEqual(cf.get('sect', "option1"), "foo")
900
Georg Brandl1f9fa312009-04-27 16:42:58 +0000901 # bug #5741: double percents are *not* malformed
902 cf.set("sect", "option2", "foo%%bar")
903 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
904
David Goodger1cbf2062004-10-03 15:55:09 +0000905 def test_set_nonstring_types(self):
906 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000907 "option1{eq}foo\n".format(eq=self.delimiters[0]))
David Goodger1cbf2062004-10-03 15:55:09 +0000908 # Check that we get a TypeError when setting non-string values
909 # in an existing section:
910 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
911 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
912 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
913 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
914 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
915 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000916 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
917 self.assertRaises(TypeError, cf.add_section, 123)
David Goodger1cbf2062004-10-03 15:55:09 +0000918
Łukasz Langac264c092010-11-20 16:15:37 +0000919 def test_add_section_default(self):
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000920 cf = self.newconfig()
Łukasz Langac264c092010-11-20 16:15:37 +0000921 self.assertRaises(ValueError, cf.add_section, self.default_section)
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000922
Łukasz Langab6a6f5f2010-12-03 16:28:00 +0000923class SafeConfigParserTestCaseExtendedInterpolation(BasicTestCase):
924 config_class = configparser.SafeConfigParser
925 interpolation = configparser.ExtendedInterpolation()
926 default_section = 'common'
927
928 def test_extended_interpolation(self):
929 cf = self.fromstring(textwrap.dedent("""
930 [common]
931 favourite Beatle = Paul
932 favourite color = green
933
934 [tom]
935 favourite band = ${favourite color} day
936 favourite pope = John ${favourite Beatle} II
937 sequel = ${favourite pope}I
938
939 [ambv]
940 favourite Beatle = George
941 son of Edward VII = ${favourite Beatle} V
942 son of George V = ${son of Edward VII}I
943
944 [stanley]
945 favourite Beatle = ${ambv:favourite Beatle}
946 favourite pope = ${tom:favourite pope}
947 favourite color = black
948 favourite state of mind = paranoid
949 favourite movie = soylent ${common:favourite color}
950 favourite song = ${favourite color} sabbath - ${favourite state of mind}
951 """).strip())
952
953 eq = self.assertEqual
954 eq(cf['common']['favourite Beatle'], 'Paul')
955 eq(cf['common']['favourite color'], 'green')
956 eq(cf['tom']['favourite Beatle'], 'Paul')
957 eq(cf['tom']['favourite color'], 'green')
958 eq(cf['tom']['favourite band'], 'green day')
959 eq(cf['tom']['favourite pope'], 'John Paul II')
960 eq(cf['tom']['sequel'], 'John Paul III')
961 eq(cf['ambv']['favourite Beatle'], 'George')
962 eq(cf['ambv']['favourite color'], 'green')
963 eq(cf['ambv']['son of Edward VII'], 'George V')
964 eq(cf['ambv']['son of George V'], 'George VI')
965 eq(cf['stanley']['favourite Beatle'], 'George')
966 eq(cf['stanley']['favourite color'], 'black')
967 eq(cf['stanley']['favourite state of mind'], 'paranoid')
968 eq(cf['stanley']['favourite movie'], 'soylent green')
969 eq(cf['stanley']['favourite pope'], 'John Paul II')
970 eq(cf['stanley']['favourite song'],
971 'black sabbath - paranoid')
972
973 def test_endless_loop(self):
974 cf = self.fromstring(textwrap.dedent("""
975 [one for you]
976 ping = ${one for me:pong}
977
978 [one for me]
979 pong = ${one for you:ping}
980 """).strip())
981
982 with self.assertRaises(configparser.InterpolationDepthError):
983 cf['one for you']['ping']
984
985
986
Georg Brandl96a60ae2010-07-28 13:13:46 +0000987class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
988 delimiters = (':=', '$')
989 comment_prefixes = ('//', '"')
Fred Drake03c44a32010-02-19 06:08:41 +0000990
991class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase):
992 allow_no_value = True
993
Georg Brandl8dcaa732010-07-29 12:17:40 +0000994class SafeConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
995 config_class = configparser.SafeConfigParser
996 delimiters = {'='}
997 comment_prefixes = {'#'}
998 allow_no_value = True
999
1000 def test_cfgparser_dot_3(self):
1001 tricky = support.findfile("cfgparser.3")
1002 cf = self.newconfig()
1003 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1004 self.assertEqual(cf.sections(), ['strange',
1005 'corruption',
1006 'yeah, sections can be '
1007 'indented as well',
1008 'another one!',
1009 'no values here',
1010 'tricky interpolation',
1011 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001012 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001013 vars={'interpolate': '-1'}), -1)
1014 with self.assertRaises(ValueError):
1015 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001016 cf.getint(self.default_section, 'go', raw=True,
1017 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001018 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1019 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1020 longname = 'yeah, sections can be indented as well'
1021 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001022 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001023 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1024 # `go` from DEFAULT
1025 with self.assertRaises(configparser.InterpolationMissingOptionError):
1026 cf.items('no values here')
1027 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1028 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1029 cf.get('tricky interpolation', 'go'))
1030 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1031
1032 def test_unicode_failure(self):
1033 tricky = support.findfile("cfgparser.3")
1034 cf = self.newconfig()
1035 with self.assertRaises(UnicodeDecodeError):
1036 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001037
Fred Drake88444412010-09-03 04:22:36 +00001038
1039class Issue7005TestCase(unittest.TestCase):
1040 """Test output when None is set() as a value and allow_no_value == False.
1041
1042 http://bugs.python.org/issue7005
1043
1044 """
1045
1046 expected_output = "[section]\noption = None\n\n"
1047
1048 def prepare(self, config_class):
1049 # This is the default, but that's the point.
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001050 with warnings.catch_warnings():
1051 warnings.simplefilter("ignore", category=DeprecationWarning)
1052 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001053 cp.add_section("section")
1054 cp.set("section", "option", None)
1055 sio = io.StringIO()
1056 cp.write(sio)
1057 return sio.getvalue()
1058
1059 def test_none_as_value_stringified(self):
1060 output = self.prepare(configparser.ConfigParser)
1061 self.assertEqual(output, self.expected_output)
1062
1063 def test_none_as_value_stringified_raw(self):
1064 output = self.prepare(configparser.RawConfigParser)
1065 self.assertEqual(output, self.expected_output)
1066
1067
Thomas Wouters89f507f2006-12-13 04:49:30 +00001068class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001069 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001070
1071 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001072 cf = self.fromstring("[b]\n"
1073 "o4=1\n"
1074 "o3=2\n"
1075 "o2=3\n"
1076 "o1=4\n"
1077 "[a]\n"
1078 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001079 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001080 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001081 self.assertEqual(output.getvalue(),
1082 "[a]\n"
1083 "k = v\n\n"
1084 "[b]\n"
1085 "o1 = 4\n"
1086 "o2 = 3\n"
1087 "o3 = 2\n"
1088 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001089
Fred Drake03c44a32010-02-19 06:08:41 +00001090
Georg Brandl96a60ae2010-07-28 13:13:46 +00001091class CompatibleTestCase(CfgParserTestCaseClass):
1092 config_class = configparser.RawConfigParser
Fred Drakecc645b92010-09-04 04:35:34 +00001093 comment_prefixes = configparser._COMPATIBLE
Georg Brandl96a60ae2010-07-28 13:13:46 +00001094
1095 def test_comment_handling(self):
1096 config_string = textwrap.dedent("""\
1097 [Commented Bar]
1098 baz=qwe ; a comment
1099 foo: bar # not a comment!
1100 # but this is a comment
1101 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001102 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001103 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001104 """)
1105 cf = self.fromstring(config_string)
1106 self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!')
1107 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001108 self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001109
1110
Fred Drakec6f28912002-10-25 19:40:49 +00001111def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001112 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001113 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001114 ConfigParserTestCaseNonStandardDelimiters,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001115 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001116 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001117 RawConfigParserTestCaseNonStandardDelimiters,
1118 RawConfigParserTestSambaConf,
Thomas Wouters89f507f2006-12-13 04:49:30 +00001119 SafeConfigParserTestCase,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001120 SafeConfigParserTestCaseExtendedInterpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001121 SafeConfigParserTestCaseNonStandardDelimiters,
Fred Drake03c44a32010-02-19 06:08:41 +00001122 SafeConfigParserTestCaseNoValue,
Georg Brandl8dcaa732010-07-29 12:17:40 +00001123 SafeConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001124 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001125 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001126 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001127 CompatibleTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001128 ConfigParserTestCaseNonStandardDefaultSection,
Fred Drake03c44a32010-02-19 06:08:41 +00001129 )
1130
Łukasz Langa535c0772010-12-04 13:48:13 +00001131def test_coverage(coverdir):
1132 trace = support.import_module('trace')
1133 tracer=trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
1134 count=1)
1135 tracer.run('test_main()')
1136 r=tracer.results()
1137 print("Writing coverage results...")
1138 r.write_results(show_missing=True, summary=True, coverdir=coverdir)
Fred Drake3af0eb82002-10-25 18:09:24 +00001139
Fred Drakec6f28912002-10-25 19:40:49 +00001140if __name__ == "__main__":
Łukasz Langa535c0772010-12-04 13:48:13 +00001141 if "-c" in sys.argv:
1142 test_coverage('/tmp/cmd.cover')
1143 else:
1144 test_main()