blob: 4b2d2dfb6ebca8c03598579db9a5b6de94019277 [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
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 = (';', '#')
Łukasz Langab25a7912010-12-17 01:32:29 +000032 inline_comment_prefixes = (';', '#')
Georg Brandl96a60ae2010-07-28 13:13:46 +000033 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,
Łukasz Langab25a7912010-12-17 01:32:29 +000045 inline_comment_prefixes=self.inline_comment_prefixes,
Georg Brandl96a60ae2010-07-28 13:13:46 +000046 empty_lines_in_values=self.empty_lines_in_values,
47 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000048 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000049 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000050 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000051 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000052 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000053 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)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000162 # test vars= and fallback=
163 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
164 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
165 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
166 with self.assertRaises(KeyError):
167 cf['No Such Foo Bar']['foo']
168 with self.assertRaises(KeyError):
169 cf['Foo Bar']['no-such-foo']
170 with self.assertRaises(KeyError):
171 cf['No Such Foo Bar'].get('foo', fallback='baz')
172 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
173 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
174 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
175 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
176 with self.assertRaises(KeyError):
177 cf['No Such Spacey Bar'].get('foo', None)
178 eq(cf['Types'].getint('int', 18), 42)
179 eq(cf['Types'].getint('int', fallback=18), 42)
180 eq(cf['Types'].getint('no-such-int', 18), 18)
181 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
182 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
183 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
184 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
185 self.assertAlmostEqual(cf['Types'].getfloat('float',
186 fallback=0.0), 0.44)
187 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
188 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
189 fallback=0.0), 0.0)
190 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
191 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
192 eq(cf['Types'].getboolean('boolean', True), False)
193 eq(cf['Types'].getboolean('boolean', fallback=True), False)
194 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
195 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
196 "yes") # sic!
197 eq(cf['Types'].getboolean('no-such-boolean', True), True)
198 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
199 if self.allow_no_value:
200 eq(cf['NoValue'].get('option-without-value', False), None)
201 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
202 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
203 eq(cf['NoValue'].get('no-such-option-without-value',
204 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000205
Fred Drakec6f28912002-10-25 19:40:49 +0000206 # Make sure the right things happen for remove_option();
207 # added to include check for SourceForge bug #123324:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000208
209 # API acceess
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000210 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000211 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000212 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000213 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000214 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000215 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000216 " that was removed")
217
Michael Foordbd6c0792010-07-25 23:09:25 +0000218 with self.assertRaises(configparser.NoSectionError) as cm:
219 cf.remove_option('No Such Section', 'foo')
220 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000221
222 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000223 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000224
Łukasz Langa26d513c2010-11-10 18:57:39 +0000225 # mapping access
226 del cf['Spacey Bar']['foo']
227 self.assertFalse('foo' in cf['Spacey Bar'])
228 with self.assertRaises(KeyError):
229 del cf['Spacey Bar']['foo']
230 with self.assertRaises(KeyError):
231 del cf['No Such Section']['foo']
232
Fred Drakea4923622010-08-09 12:52:45 +0000233 def test_basic(self):
234 config_string = """\
235[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000236foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000237[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000238foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000239[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000240 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000241 baz {0[0]} qwe
242[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000243foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000244baz{0[0]}qwe {1[0]}another one
245[Long Line]
246foo{0[1]} this line is much, much longer than my editor
247 likes it.
248[Section\\with$weird%characters[\t]
249[Internationalized Stuff]
250foo[bg]{0[1]} Bulgarian
251foo{0[0]}Default
252foo[en]{0[0]}English
253foo[de]{0[0]}Deutsch
254[Spaces]
255key with spaces {0[1]} value
256another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000257[Types]
258int {0[1]} 42
259float {0[0]} 0.44
260boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000261123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000262""".format(self.delimiters, self.comment_prefixes)
263 if self.allow_no_value:
264 config_string += (
265 "[NoValue]\n"
266 "option-without-value\n"
267 )
268 cf = self.fromstring(config_string)
269 self.basic_test(cf)
270 if self.strict:
271 with self.assertRaises(configparser.DuplicateOptionError):
272 cf.read_string(textwrap.dedent("""\
273 [Duplicate Options Here]
274 option {0[0]} with a value
275 option {0[1]} with another value
276 """.format(self.delimiters)))
277 with self.assertRaises(configparser.DuplicateSectionError):
278 cf.read_string(textwrap.dedent("""\
279 [And Now For Something]
280 completely different {0[0]} True
281 [And Now For Something]
282 the larch {0[1]} 1
283 """.format(self.delimiters)))
284 else:
285 cf.read_string(textwrap.dedent("""\
286 [Duplicate Options Here]
287 option {0[0]} with a value
288 option {0[1]} with another value
289 """.format(self.delimiters)))
290
291 cf.read_string(textwrap.dedent("""\
292 [And Now For Something]
293 completely different {0[0]} True
294 [And Now For Something]
295 the larch {0[1]} 1
296 """.format(self.delimiters)))
297
298 def test_basic_from_dict(self):
299 config = {
300 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000301 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000302 },
303 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000304 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000305 },
306 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000307 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000308 "baz": "qwe",
309 },
310 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000311 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000312 "baz": "qwe",
313 },
314 "Long Line": {
315 "foo": "this line is much, much longer than my editor\nlikes "
316 "it.",
317 },
318 "Section\\with$weird%characters[\t": {
319 },
320 "Internationalized Stuff": {
321 "foo[bg]": "Bulgarian",
322 "foo": "Default",
323 "foo[en]": "English",
324 "foo[de]": "Deutsch",
325 },
326 "Spaces": {
327 "key with spaces": "value",
328 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000329 },
330 "Types": {
331 "int": 42,
332 "float": 0.44,
333 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000334 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000335 },
Fred Drakea4923622010-08-09 12:52:45 +0000336 }
337 if self.allow_no_value:
338 config.update({
339 "NoValue": {
340 "option-without-value": None,
341 }
342 })
343 cf = self.newconfig()
344 cf.read_dict(config)
345 self.basic_test(cf)
346 if self.strict:
347 with self.assertRaises(configparser.DuplicateOptionError):
348 cf.read_dict({
349 "Duplicate Options Here": {
350 'option': 'with a value',
351 'OPTION': 'with another value',
352 },
353 })
354 else:
355 cf.read_dict({
356 "Duplicate Options Here": {
357 'option': 'with a value',
358 'OPTION': 'with another value',
359 },
360 })
361
362
Fred Drakec6f28912002-10-25 19:40:49 +0000363 def test_case_sensitivity(self):
364 cf = self.newconfig()
365 cf.add_section("A")
366 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000367 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000368 L = cf.sections()
369 L.sort()
370 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000371 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000372 cf.set("a", "B", "value")
373 eq(cf.options("a"), ["b"])
374 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000375 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000376 with self.assertRaises(configparser.NoSectionError):
377 # section names are case-sensitive
378 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000379 self.assertTrue(cf.has_option("a", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000380 cf.set("A", "A-B", "A-B value")
381 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000382 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000383 cf.has_option("A", opt),
384 "has_option() returned false for option which should exist")
385 eq(cf.options("A"), ["a-b"])
386 eq(cf.options("a"), ["b"])
387 cf.remove_option("a", "B")
388 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000389
Fred Drakec6f28912002-10-25 19:40:49 +0000390 # SF bug #432369:
391 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000392 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000393 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000394 eq(cf.options("MySection"), ["option"])
395 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000396
Fred Drakec6f28912002-10-25 19:40:49 +0000397 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000398 cf = self.fromstring("[section]\n"
399 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000400 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000401 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000402
Fred Drake3c823aa2001-02-26 21:55:34 +0000403
Łukasz Langa26d513c2010-11-10 18:57:39 +0000404 def test_case_sensitivity_mapping_access(self):
405 cf = self.newconfig()
406 cf["A"] = {}
407 cf["a"] = {"B": "value"}
408 cf["B"] = {}
409 L = [section for section in cf]
410 L.sort()
411 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000412 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000413 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000414 eq(cf["a"].keys(), {"b"})
415 eq(cf["a"]["b"], "value",
416 "could not locate option, expecting case-insensitive option names")
417 with self.assertRaises(KeyError):
418 # section names are case-sensitive
419 cf["b"]["A"] = "value"
420 self.assertTrue("b" in cf["a"])
421 cf["A"]["A-B"] = "A-B value"
422 for opt in ("a-b", "A-b", "a-B", "A-B"):
423 self.assertTrue(
424 opt in cf["A"],
425 "has_option() returned false for option which should exist")
426 eq(cf["A"].keys(), {"a-b"})
427 eq(cf["a"].keys(), {"b"})
428 del cf["a"]["B"]
429 elem_eq(cf["a"].keys(), {})
430
431 # SF bug #432369:
432 cf = self.fromstring(
433 "[MySection]\nOption{} first line \n\tsecond line \n".format(
434 self.delimiters[0]))
435 eq(cf["MySection"].keys(), {"option"})
436 eq(cf["MySection"]["Option"], "first line\nsecond line")
437
438 # SF bug #561822:
439 cf = self.fromstring("[section]\n"
440 "nekey{}nevalue\n".format(self.delimiters[0]),
441 defaults={"key":"value"})
442 self.assertTrue("Key" in cf["section"])
443
David Goodger68a1abd2004-10-03 15:40:25 +0000444 def test_default_case_sensitivity(self):
445 cf = self.newconfig({"foo": "Bar"})
446 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000447 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000448 "could not locate option, expecting case-insensitive option names")
449 cf = self.newconfig({"Foo": "Bar"})
450 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000451 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000452 "could not locate option, expecting case-insensitive defaults")
453
Fred Drakec6f28912002-10-25 19:40:49 +0000454 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000455 cf = self.newconfig()
456 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000457 "[Foo]\n"
458 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000459 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000460 "[Foo]\n"
461 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000462 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000463 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000464 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000465 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000466 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000467 "[Foo]\n wrong-indent\n")
468 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000469 # read_file on a real file
470 tricky = support.findfile("cfgparser.3")
471 if self.delimiters[0] == '=':
472 error = configparser.ParsingError
473 expected = (tricky,)
474 else:
475 error = configparser.MissingSectionHeaderError
476 expected = (tricky, 1,
477 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000478 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000479 e = self.parse_error(cf, error, f)
480 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000481
Fred Drakea4923622010-08-09 12:52:45 +0000482 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000483 if hasattr(src, 'readline'):
484 sio = src
485 else:
486 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000487 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000488 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000489 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000490
Fred Drakec6f28912002-10-25 19:40:49 +0000491 def test_query_errors(self):
492 cf = self.newconfig()
493 self.assertEqual(cf.sections(), [],
494 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000495 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000496 "new ConfigParser should have no acknowledged "
497 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000498 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000499 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000500 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000501 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000502 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000503 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000504 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000505 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000506 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000507
Fred Drakea4923622010-08-09 12:52:45 +0000508 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000509 try:
Fred Drakea4923622010-08-09 12:52:45 +0000510 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000511 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000512 return e
513 else:
514 self.fail("expected exception type %s.%s"
515 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000516
Fred Drakec6f28912002-10-25 19:40:49 +0000517 def test_boolean(self):
518 cf = self.fromstring(
519 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000520 "T1{equals}1\n"
521 "T2{equals}TRUE\n"
522 "T3{equals}True\n"
523 "T4{equals}oN\n"
524 "T5{equals}yes\n"
525 "F1{equals}0\n"
526 "F2{equals}FALSE\n"
527 "F3{equals}False\n"
528 "F4{equals}oFF\n"
529 "F5{equals}nO\n"
530 "E1{equals}2\n"
531 "E2{equals}foo\n"
532 "E3{equals}-1\n"
533 "E4{equals}0.1\n"
534 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000535 )
536 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000537 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
538 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000539 self.assertRaises(ValueError,
540 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000541
Fred Drakec6f28912002-10-25 19:40:49 +0000542 def test_weird_errors(self):
543 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000544 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000545 with self.assertRaises(configparser.DuplicateSectionError) as cm:
546 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000547 e = cm.exception
548 self.assertEqual(str(e), "Section 'Foo' already exists")
549 self.assertEqual(e.args, ("Foo", None, None))
550
551 if self.strict:
552 with self.assertRaises(configparser.DuplicateSectionError) as cm:
553 cf.read_string(textwrap.dedent("""\
554 [Foo]
555 will this be added{equals}True
556 [Bar]
557 what about this{equals}True
558 [Foo]
559 oops{equals}this won't
560 """.format(equals=self.delimiters[0])), source='<foo-bar>')
561 e = cm.exception
562 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
563 "section 'Foo' already exists")
564 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
565
566 with self.assertRaises(configparser.DuplicateOptionError) as cm:
567 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
568 e = cm.exception
569 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
570 "in section 'Bar' already exists")
571 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000572
573 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000574 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000575 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000576 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000577 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000578 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000579 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000580 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000581 "[Long Line - With Comments!]\n"
582 "test {0[1]} we {comment} can\n"
583 " also {comment} place\n"
584 " comments {comment} in\n"
585 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000586 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
587 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000588 )
Fred Drake03c44a32010-02-19 06:08:41 +0000589 if self.allow_no_value:
590 config_string += (
591 "[Valueless]\n"
592 "option-without-value\n"
593 )
594
595 cf = self.fromstring(config_string)
Guido van Rossum34d19282007-08-09 01:03:29 +0000596 output = io.StringIO()
Fred Drakec6f28912002-10-25 19:40:49 +0000597 cf.write(output)
Fred Drake03c44a32010-02-19 06:08:41 +0000598 expect_string = (
Łukasz Langac264c092010-11-20 16:15:37 +0000599 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000600 "foo {equals} another very\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000601 "\tlong line\n"
602 "\n"
603 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000604 "foo {equals} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000605 "\tlikes it.\n"
606 "\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000607 "[Long Line - With Comments!]\n"
608 "test {equals} we\n"
609 "\talso\n"
610 "\tcomments\n"
611 "\tmultiline\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000612 "\n".format(equals=self.delimiters[0],
613 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000614 )
Fred Drake03c44a32010-02-19 06:08:41 +0000615 if self.allow_no_value:
616 expect_string += (
617 "[Valueless]\n"
618 "option-without-value\n"
619 "\n"
620 )
621 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000622
Fred Drakeabc086f2004-05-18 03:29:52 +0000623 def test_set_string_types(self):
624 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000625 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000626 # Check that we don't get an exception when setting values in
627 # an existing section using strings:
628 class mystr(str):
629 pass
630 cf.set("sect", "option1", "splat")
631 cf.set("sect", "option1", mystr("splat"))
632 cf.set("sect", "option2", "splat")
633 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000634 cf.set("sect", "option1", "splat")
635 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000636
Fred Drake82903142004-05-18 04:24:02 +0000637 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000638 if self.delimiters[0] != '=':
639 # skip reading the file if we're using an incompatible format
640 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000641 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000642 # check when we pass a mix of readable and non-readable files:
643 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000644 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000645 self.assertEqual(parsed_files, [file1])
646 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
647 # check when we pass only a filename:
648 cf = self.newconfig()
649 parsed_files = cf.read(file1)
650 self.assertEqual(parsed_files, [file1])
651 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
652 # check when we pass only missing files:
653 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000654 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000655 self.assertEqual(parsed_files, [])
656 # check when we pass no files:
657 cf = self.newconfig()
658 parsed_files = cf.read([])
659 self.assertEqual(parsed_files, [])
660
Fred Drakec6f28912002-10-25 19:40:49 +0000661 # shared by subclasses
662 def get_interpolation_config(self):
663 return self.fromstring(
664 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000665 "bar{equals}something %(with1)s interpolation (1 step)\n"
666 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
667 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
668 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
669 "with11{equals}%(with10)s\n"
670 "with10{equals}%(with9)s\n"
671 "with9{equals}%(with8)s\n"
672 "with8{equals}%(With7)s\n"
673 "with7{equals}%(WITH6)s\n"
674 "with6{equals}%(with5)s\n"
675 "With5{equals}%(with4)s\n"
676 "WITH4{equals}%(with3)s\n"
677 "with3{equals}%(with2)s\n"
678 "with2{equals}%(with1)s\n"
679 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000680 "\n"
681 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000682 "foo{equals}%(bar)s\n"
683 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000684 "\n"
685 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000686 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000687 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000688
Fred Drake98e3b292002-10-25 20:42:44 +0000689 def check_items_config(self, expected):
690 cf = self.fromstring(
691 "[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000692 "name {0[0]} value\n"
693 "key{0[1]} |%(name)s| \n"
Łukasz Langa5c863392010-11-21 13:41:35 +0000694 "getdefault{0[1]} |%(default)s|\n".format(self.delimiters),
Fred Drake98e3b292002-10-25 20:42:44 +0000695 defaults={"default": "<default>"})
696 L = list(cf.items("section"))
697 L.sort()
698 self.assertEqual(L, expected)
699
Fred Drake8ef67672000-09-27 22:45:25 +0000700
Fred Drakea4923622010-08-09 12:52:45 +0000701class StrictTestCase(BasicTestCase):
702 config_class = configparser.RawConfigParser
703 strict = True
704
705
Georg Brandl96a60ae2010-07-28 13:13:46 +0000706class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000707 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000708
709 def test_interpolation(self):
710 cf = self.get_interpolation_config()
711 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000712 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
713 eq(cf.get("Foo", "bar9"),
714 "something with lots of interpolation (9 steps)")
715 eq(cf.get("Foo", "bar10"),
716 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000717 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000718 if self.interpolation == configparser._UNSET:
719 self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
720 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
721 self.assertEqual(e.args, ("bar11", "Foo",
722 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000723
Fred Drake54782192002-12-31 06:57:25 +0000724 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000725 cf = self.get_interpolation_config()
726 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000727 "Interpolation Error", "name")
728 self.assertEqual(e.reference, "reference")
729 self.assertEqual(e.section, "Interpolation Error")
730 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000731 if self.interpolation == configparser._UNSET:
732 self.assertEqual(e.args, ('name', 'Interpolation Error',
733 '', 'reference'))
734 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
735 self.assertEqual(e.args, ('name', 'Interpolation Error',
736 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000737
Fred Drake98e3b292002-10-25 20:42:44 +0000738 def test_items(self):
739 self.check_items_config([('default', '<default>'),
740 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000741 ('key', '|value|'),
742 ('name', 'value')])
743
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000744 def test_safe_interpolation(self):
745 # See http://www.python.org/sf/511737
746 cf = self.fromstring("[section]\n"
747 "option1{eq}xxx\n"
748 "option2{eq}%(option1)s/xxx\n"
749 "ok{eq}%(option1)s/%%s\n"
750 "not_ok{eq}%(option2)s/%%s".format(
751 eq=self.delimiters[0]))
752 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
753 if self.interpolation == configparser._UNSET:
754 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
755 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
756 with self.assertRaises(TypeError):
757 cf.get("section", "not_ok")
758
759 def test_set_malformatted_interpolation(self):
760 cf = self.fromstring("[sect]\n"
761 "option1{eq}foo\n".format(eq=self.delimiters[0]))
762
763 self.assertEqual(cf.get('sect', "option1"), "foo")
764
765 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
766 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
767 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
768
769 self.assertEqual(cf.get('sect', "option1"), "foo")
770
771 # bug #5741: double percents are *not* malformed
772 cf.set("sect", "option2", "foo%%bar")
773 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
774
David Goodger1cbf2062004-10-03 15:55:09 +0000775 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000776 cf = self.fromstring("[sect]\n"
777 "option1{eq}foo\n".format(eq=self.delimiters[0]))
778 # Check that we get a TypeError when setting non-string values
779 # in an existing section:
780 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
781 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
782 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
783 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
784 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
785 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
786 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
787 self.assertRaises(TypeError, cf.add_section, 123)
788
789 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000790 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000791 self.assertRaises(ValueError, cf.add_section, self.default_section)
792
793class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
794 config_class = configparser.ConfigParser
795 interpolation = configparser.LegacyInterpolation()
796
797 def test_set_malformatted_interpolation(self):
798 cf = self.fromstring("[sect]\n"
799 "option1{eq}foo\n".format(eq=self.delimiters[0]))
800
801 self.assertEqual(cf.get('sect', "option1"), "foo")
802
803 cf.set("sect", "option1", "%foo")
804 self.assertEqual(cf.get('sect', "option1"), "%foo")
805 cf.set("sect", "option1", "foo%")
806 self.assertEqual(cf.get('sect', "option1"), "foo%")
807 cf.set("sect", "option1", "f%oo")
808 self.assertEqual(cf.get('sect', "option1"), "f%oo")
809
810 # bug #5741: double percents are *not* malformed
811 cf.set("sect", "option2", "foo%%bar")
812 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000813
Georg Brandl96a60ae2010-07-28 13:13:46 +0000814class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
815 delimiters = (':=', '$')
816 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000817 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000818
Łukasz Langac264c092010-11-20 16:15:37 +0000819class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
820 default_section = 'general'
821
Georg Brandl96a60ae2010-07-28 13:13:46 +0000822class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000823 config_class = configparser.ConfigParser
824 wonderful_spam = ("I'm having spam spam spam spam "
825 "spam spam spam beaked beans spam "
826 "spam spam and spam!").replace(' ', '\t\n')
827
828 def setUp(self):
829 cf = self.newconfig()
830 for i in range(100):
831 s = 'section{}'.format(i)
832 cf.add_section(s)
833 for j in range(10):
834 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
835 with open(support.TESTFN, 'w') as f:
836 cf.write(f)
837
838 def tearDown(self):
839 os.unlink(support.TESTFN)
840
841 def test_dominating_multiline_values(self):
842 # We're reading from file because this is where the code changed
843 # during performance updates in Python 3.2
844 cf_from_file = self.newconfig()
845 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000846 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000847 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
848 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000849
Georg Brandl96a60ae2010-07-28 13:13:46 +0000850class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000851 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000852
853 def test_interpolation(self):
854 cf = self.get_interpolation_config()
855 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000856 eq(cf.get("Foo", "bar"),
857 "something %(with1)s interpolation (1 step)")
858 eq(cf.get("Foo", "bar9"),
859 "something %(with9)s lots of interpolation (9 steps)")
860 eq(cf.get("Foo", "bar10"),
861 "something %(with10)s lots of interpolation (10 steps)")
862 eq(cf.get("Foo", "bar11"),
863 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000864
Fred Drake98e3b292002-10-25 20:42:44 +0000865 def test_items(self):
866 self.check_items_config([('default', '<default>'),
867 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000868 ('key', '|%(name)s|'),
869 ('name', 'value')])
870
David Goodger1cbf2062004-10-03 15:55:09 +0000871 def test_set_nonstring_types(self):
872 cf = self.newconfig()
873 cf.add_section('non-string')
874 cf.set('non-string', 'int', 1)
875 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
876 cf.set('non-string', 'dict', {'pi': 3.14159})
877 self.assertEqual(cf.get('non-string', 'int'), 1)
878 self.assertEqual(cf.get('non-string', 'list'),
879 [0, 1, 1, 2, 3, 5, 8, 13])
880 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000881 cf.add_section(123)
882 cf.set(123, 'this is sick', True)
883 self.assertEqual(cf.get(123, 'this is sick'), True)
884 if cf._dict.__class__ is configparser._default_dict:
885 # would not work for SortedDict; only checking for the most common
886 # default dictionary (OrderedDict)
887 cf.optionxform = lambda x: x
888 cf.set('non-string', 1, 1)
889 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +0000890
Georg Brandl96a60ae2010-07-28 13:13:46 +0000891class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
892 delimiters = (':=', '$')
893 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000894 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000895
Łukasz Langab25a7912010-12-17 01:32:29 +0000896class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000897 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +0000898 comment_prefixes = ('#', ';', '----')
899 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +0000900 empty_lines_in_values = False
901
902 def test_reading(self):
903 smbconf = support.findfile("cfgparser.2")
904 # check when we pass a mix of readable and non-readable files:
905 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000906 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000907 self.assertEqual(parsed_files, [smbconf])
908 sections = ['global', 'homes', 'printers',
909 'print$', 'pdf-generator', 'tmp', 'Agustin']
910 self.assertEqual(cf.sections(), sections)
911 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
912 self.assertEqual(cf.getint("global", "max log size"), 50)
913 self.assertEqual(cf.get("global", "hosts allow"), "127.")
914 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000915
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000916class ConfigParserTestCaseExtendedInterpolation(BasicTestCase):
917 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +0000918 interpolation = configparser.ExtendedInterpolation()
919 default_section = 'common'
920
921 def test_extended_interpolation(self):
922 cf = self.fromstring(textwrap.dedent("""
923 [common]
924 favourite Beatle = Paul
925 favourite color = green
926
927 [tom]
928 favourite band = ${favourite color} day
929 favourite pope = John ${favourite Beatle} II
930 sequel = ${favourite pope}I
931
932 [ambv]
933 favourite Beatle = George
934 son of Edward VII = ${favourite Beatle} V
935 son of George V = ${son of Edward VII}I
936
937 [stanley]
938 favourite Beatle = ${ambv:favourite Beatle}
939 favourite pope = ${tom:favourite pope}
940 favourite color = black
941 favourite state of mind = paranoid
942 favourite movie = soylent ${common:favourite color}
943 favourite song = ${favourite color} sabbath - ${favourite state of mind}
944 """).strip())
945
946 eq = self.assertEqual
947 eq(cf['common']['favourite Beatle'], 'Paul')
948 eq(cf['common']['favourite color'], 'green')
949 eq(cf['tom']['favourite Beatle'], 'Paul')
950 eq(cf['tom']['favourite color'], 'green')
951 eq(cf['tom']['favourite band'], 'green day')
952 eq(cf['tom']['favourite pope'], 'John Paul II')
953 eq(cf['tom']['sequel'], 'John Paul III')
954 eq(cf['ambv']['favourite Beatle'], 'George')
955 eq(cf['ambv']['favourite color'], 'green')
956 eq(cf['ambv']['son of Edward VII'], 'George V')
957 eq(cf['ambv']['son of George V'], 'George VI')
958 eq(cf['stanley']['favourite Beatle'], 'George')
959 eq(cf['stanley']['favourite color'], 'black')
960 eq(cf['stanley']['favourite state of mind'], 'paranoid')
961 eq(cf['stanley']['favourite movie'], 'soylent green')
962 eq(cf['stanley']['favourite pope'], 'John Paul II')
963 eq(cf['stanley']['favourite song'],
964 'black sabbath - paranoid')
965
966 def test_endless_loop(self):
967 cf = self.fromstring(textwrap.dedent("""
968 [one for you]
969 ping = ${one for me:pong}
970
971 [one for me]
972 pong = ${one for you:ping}
973 """).strip())
974
975 with self.assertRaises(configparser.InterpolationDepthError):
976 cf['one for you']['ping']
977
978
979
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000980class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +0000981 allow_no_value = True
982
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000983class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
984 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +0000985 delimiters = {'='}
986 comment_prefixes = {'#'}
987 allow_no_value = True
988
989 def test_cfgparser_dot_3(self):
990 tricky = support.findfile("cfgparser.3")
991 cf = self.newconfig()
992 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
993 self.assertEqual(cf.sections(), ['strange',
994 'corruption',
995 'yeah, sections can be '
996 'indented as well',
997 'another one!',
998 'no values here',
999 'tricky interpolation',
1000 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001001 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001002 vars={'interpolate': '-1'}), -1)
1003 with self.assertRaises(ValueError):
1004 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001005 cf.getint(self.default_section, 'go', raw=True,
1006 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001007 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1008 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1009 longname = 'yeah, sections can be indented as well'
1010 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001011 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001012 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1013 # `go` from DEFAULT
1014 with self.assertRaises(configparser.InterpolationMissingOptionError):
1015 cf.items('no values here')
1016 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1017 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1018 cf.get('tricky interpolation', 'go'))
1019 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1020
1021 def test_unicode_failure(self):
1022 tricky = support.findfile("cfgparser.3")
1023 cf = self.newconfig()
1024 with self.assertRaises(UnicodeDecodeError):
1025 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001026
Fred Drake88444412010-09-03 04:22:36 +00001027
1028class Issue7005TestCase(unittest.TestCase):
1029 """Test output when None is set() as a value and allow_no_value == False.
1030
1031 http://bugs.python.org/issue7005
1032
1033 """
1034
1035 expected_output = "[section]\noption = None\n\n"
1036
1037 def prepare(self, config_class):
1038 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001039 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001040 cp.add_section("section")
1041 cp.set("section", "option", None)
1042 sio = io.StringIO()
1043 cp.write(sio)
1044 return sio.getvalue()
1045
1046 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001047 cp = configparser.ConfigParser(allow_no_value=False)
1048 cp.add_section("section")
1049 with self.assertRaises(TypeError):
1050 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001051
1052 def test_none_as_value_stringified_raw(self):
1053 output = self.prepare(configparser.RawConfigParser)
1054 self.assertEqual(output, self.expected_output)
1055
1056
Thomas Wouters89f507f2006-12-13 04:49:30 +00001057class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001058 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001059
1060 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001061 cf = self.fromstring("[b]\n"
1062 "o4=1\n"
1063 "o3=2\n"
1064 "o2=3\n"
1065 "o1=4\n"
1066 "[a]\n"
1067 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001068 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001069 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001070 self.assertEqual(output.getvalue(),
1071 "[a]\n"
1072 "k = v\n\n"
1073 "[b]\n"
1074 "o1 = 4\n"
1075 "o2 = 3\n"
1076 "o3 = 2\n"
1077 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001078
Fred Drake03c44a32010-02-19 06:08:41 +00001079
Georg Brandl96a60ae2010-07-28 13:13:46 +00001080class CompatibleTestCase(CfgParserTestCaseClass):
1081 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001082 comment_prefixes = '#;'
1083 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001084
1085 def test_comment_handling(self):
1086 config_string = textwrap.dedent("""\
1087 [Commented Bar]
1088 baz=qwe ; a comment
1089 foo: bar # not a comment!
1090 # but this is a comment
1091 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001092 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001093 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001094 """)
1095 cf = self.fromstring(config_string)
1096 self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!')
1097 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001098 self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001099
1100
Fred Drakec6f28912002-10-25 19:40:49 +00001101def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001102 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001103 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001104 ConfigParserTestCaseNonStandardDelimiters,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001105 ConfigParserTestCaseNoValue,
1106 ConfigParserTestCaseExtendedInterpolation,
1107 ConfigParserTestCaseLegacyInterpolation,
1108 ConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001109 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001110 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001111 RawConfigParserTestCaseNonStandardDelimiters,
1112 RawConfigParserTestSambaConf,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001113 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001114 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001115 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001116 CompatibleTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001117 ConfigParserTestCaseNonStandardDefaultSection,
Fred Drake03c44a32010-02-19 06:08:41 +00001118 )
1119
Łukasz Langa535c0772010-12-04 13:48:13 +00001120def test_coverage(coverdir):
1121 trace = support.import_module('trace')
1122 tracer=trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
1123 count=1)
1124 tracer.run('test_main()')
1125 r=tracer.results()
1126 print("Writing coverage results...")
1127 r.write_results(show_missing=True, summary=True, coverdir=coverdir)
Fred Drake3af0eb82002-10-25 18:09:24 +00001128
Fred Drakec6f28912002-10-25 19:40:49 +00001129if __name__ == "__main__":
Łukasz Langa535c0772010-12-04 13:48:13 +00001130 if "-c" in sys.argv:
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001131 test_coverage('/tmp/configparser.cover')
Łukasz Langa535c0772010-12-04 13:48:13 +00001132 else:
1133 test_main()