blob: 08a313a819e9bb1211ab84cf38d81ce88df89284 [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 = (';', '#')
32 empty_lines_in_values = True
33 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000034 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000035 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000036 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000037
Fred Drakec6f28912002-10-25 19:40:49 +000038 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000039 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000040 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000041 allow_no_value=self.allow_no_value,
42 delimiters=self.delimiters,
43 comment_prefixes=self.comment_prefixes,
44 empty_lines_in_values=self.empty_lines_in_values,
45 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000046 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000047 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000048 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000049 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000050 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000051 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000052
Fred Drakec6f28912002-10-25 19:40:49 +000053 def fromstring(self, string, defaults=None):
54 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000055 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000056 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000057
Georg Brandl96a60ae2010-07-28 13:13:46 +000058class BasicTestCase(CfgParserTestCaseClass):
59
Fred Drakea4923622010-08-09 12:52:45 +000060 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000061 E = ['Commented Bar',
62 'Foo Bar',
63 'Internationalized Stuff',
64 'Long Line',
65 'Section\\with$weird%characters[\t',
66 'Spaces',
67 'Spacey Bar',
68 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000069 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000070 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000071
Fred Drake03c44a32010-02-19 06:08:41 +000072 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000073 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000074 E.sort()
Łukasz Langa26d513c2010-11-10 18:57:39 +000075
76 # API access
77 L = cf.sections()
78 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000079 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000080 eq(L, E)
Fred Drake8ef67672000-09-27 22:45:25 +000081
Łukasz Langa26d513c2010-11-10 18:57:39 +000082 # mapping access
83 L = [section for section in cf]
84 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +000085 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +000086 E.sort()
87 eq(L, E)
88
Fred Drakec6f28912002-10-25 19:40:49 +000089 # The use of spaces in the section names serves as a
90 # regression test for SourceForge bug #583248:
91 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +000092
93 # API access
94 eq(cf.get('Foo Bar', 'foo'), 'bar1')
95 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
96 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +000097 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +000098 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +000099 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000100 eq(cf.get('Spaces', 'key with spaces'), 'value')
101 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000102 eq(cf.getint('Types', 'int'), 42)
103 eq(cf.get('Types', 'int'), "42")
104 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
105 eq(cf.get('Types', 'float'), "0.44")
106 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000107 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000108 if self.allow_no_value:
109 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000110
Łukasz Langa26d513c2010-11-10 18:57:39 +0000111 # test vars= and fallback=
112 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000113 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
114 with self.assertRaises(configparser.NoSectionError):
115 cf.get('No Such Foo Bar', 'foo')
116 with self.assertRaises(configparser.NoOptionError):
117 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000118 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
119 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
120 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
121 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
122 eq(cf.getint('Types', 'int', fallback=18), 42)
123 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
124 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Fred Drakecc645b92010-09-04 04:35:34 +0000125 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000126 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000127 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000128 fallback=0.0), 0.0)
129 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
130 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
131 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000132 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000133 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
134 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000135 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000136 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000137 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000138 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000139
Łukasz Langa26d513c2010-11-10 18:57:39 +0000140 # mapping access
141 eq(cf['Foo Bar']['foo'], 'bar1')
142 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000143 section = cf['Spacey Bar From The Beginning']
144 eq(section.name, 'Spacey Bar From The Beginning')
145 self.assertIs(section.parser, cf)
146 with self.assertRaises(AttributeError):
147 section.name = 'Name is read-only'
148 with self.assertRaises(AttributeError):
149 section.parser = 'Parser is read-only'
150 eq(section['foo'], 'bar3')
151 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000152 eq(cf['Commented Bar']['foo'], 'bar4')
153 eq(cf['Commented Bar']['baz'], 'qwe')
154 eq(cf['Spaces']['key with spaces'], 'value')
155 eq(cf['Spaces']['another with spaces'], 'splat!')
156 eq(cf['Long Line']['foo'],
157 'this line is much, much longer than my editor\nlikes it.')
158 if self.allow_no_value:
159 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000160 # test vars= and fallback=
161 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
162 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
163 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
164 with self.assertRaises(KeyError):
165 cf['No Such Foo Bar']['foo']
166 with self.assertRaises(KeyError):
167 cf['Foo Bar']['no-such-foo']
168 with self.assertRaises(KeyError):
169 cf['No Such Foo Bar'].get('foo', fallback='baz')
170 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
171 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
172 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
173 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
174 with self.assertRaises(KeyError):
175 cf['No Such Spacey Bar'].get('foo', None)
176 eq(cf['Types'].getint('int', 18), 42)
177 eq(cf['Types'].getint('int', fallback=18), 42)
178 eq(cf['Types'].getint('no-such-int', 18), 18)
179 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
180 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
181 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
182 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
183 self.assertAlmostEqual(cf['Types'].getfloat('float',
184 fallback=0.0), 0.44)
185 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
186 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
187 fallback=0.0), 0.0)
188 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
189 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
190 eq(cf['Types'].getboolean('boolean', True), False)
191 eq(cf['Types'].getboolean('boolean', fallback=True), False)
192 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
193 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
194 "yes") # sic!
195 eq(cf['Types'].getboolean('no-such-boolean', True), True)
196 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
197 if self.allow_no_value:
198 eq(cf['NoValue'].get('option-without-value', False), None)
199 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
200 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
201 eq(cf['NoValue'].get('no-such-option-without-value',
202 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000203
Fred Drakec6f28912002-10-25 19:40:49 +0000204 # Make sure the right things happen for remove_option();
205 # added to include check for SourceForge bug #123324:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000206
207 # API acceess
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000208 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000209 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000210 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000211 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000212 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000213 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000214 " that was removed")
215
Michael Foordbd6c0792010-07-25 23:09:25 +0000216 with self.assertRaises(configparser.NoSectionError) as cm:
217 cf.remove_option('No Such Section', 'foo')
218 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000219
220 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000221 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000222
Łukasz Langa26d513c2010-11-10 18:57:39 +0000223 # mapping access
224 del cf['Spacey Bar']['foo']
225 self.assertFalse('foo' in cf['Spacey Bar'])
226 with self.assertRaises(KeyError):
227 del cf['Spacey Bar']['foo']
228 with self.assertRaises(KeyError):
229 del cf['No Such Section']['foo']
230
Fred Drakea4923622010-08-09 12:52:45 +0000231 def test_basic(self):
232 config_string = """\
233[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000234foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000235[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000236foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000237[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000238 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000239 baz {0[0]} qwe
240[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000241foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000242baz{0[0]}qwe {1[0]}another one
243[Long Line]
244foo{0[1]} this line is much, much longer than my editor
245 likes it.
246[Section\\with$weird%characters[\t]
247[Internationalized Stuff]
248foo[bg]{0[1]} Bulgarian
249foo{0[0]}Default
250foo[en]{0[0]}English
251foo[de]{0[0]}Deutsch
252[Spaces]
253key with spaces {0[1]} value
254another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000255[Types]
256int {0[1]} 42
257float {0[0]} 0.44
258boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000259123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000260""".format(self.delimiters, self.comment_prefixes)
261 if self.allow_no_value:
262 config_string += (
263 "[NoValue]\n"
264 "option-without-value\n"
265 )
266 cf = self.fromstring(config_string)
267 self.basic_test(cf)
268 if self.strict:
269 with self.assertRaises(configparser.DuplicateOptionError):
270 cf.read_string(textwrap.dedent("""\
271 [Duplicate Options Here]
272 option {0[0]} with a value
273 option {0[1]} with another value
274 """.format(self.delimiters)))
275 with self.assertRaises(configparser.DuplicateSectionError):
276 cf.read_string(textwrap.dedent("""\
277 [And Now For Something]
278 completely different {0[0]} True
279 [And Now For Something]
280 the larch {0[1]} 1
281 """.format(self.delimiters)))
282 else:
283 cf.read_string(textwrap.dedent("""\
284 [Duplicate Options Here]
285 option {0[0]} with a value
286 option {0[1]} with another value
287 """.format(self.delimiters)))
288
289 cf.read_string(textwrap.dedent("""\
290 [And Now For Something]
291 completely different {0[0]} True
292 [And Now For Something]
293 the larch {0[1]} 1
294 """.format(self.delimiters)))
295
296 def test_basic_from_dict(self):
297 config = {
298 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000299 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000300 },
301 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000302 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000303 },
304 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000305 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000306 "baz": "qwe",
307 },
308 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000309 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000310 "baz": "qwe",
311 },
312 "Long Line": {
313 "foo": "this line is much, much longer than my editor\nlikes "
314 "it.",
315 },
316 "Section\\with$weird%characters[\t": {
317 },
318 "Internationalized Stuff": {
319 "foo[bg]": "Bulgarian",
320 "foo": "Default",
321 "foo[en]": "English",
322 "foo[de]": "Deutsch",
323 },
324 "Spaces": {
325 "key with spaces": "value",
326 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000327 },
328 "Types": {
329 "int": 42,
330 "float": 0.44,
331 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000332 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000333 },
Fred Drakea4923622010-08-09 12:52:45 +0000334 }
335 if self.allow_no_value:
336 config.update({
337 "NoValue": {
338 "option-without-value": None,
339 }
340 })
341 cf = self.newconfig()
342 cf.read_dict(config)
343 self.basic_test(cf)
344 if self.strict:
345 with self.assertRaises(configparser.DuplicateOptionError):
346 cf.read_dict({
347 "Duplicate Options Here": {
348 'option': 'with a value',
349 'OPTION': 'with another value',
350 },
351 })
352 else:
353 cf.read_dict({
354 "Duplicate Options Here": {
355 'option': 'with a value',
356 'OPTION': 'with another value',
357 },
358 })
359
360
Fred Drakec6f28912002-10-25 19:40:49 +0000361 def test_case_sensitivity(self):
362 cf = self.newconfig()
363 cf.add_section("A")
364 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000365 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000366 L = cf.sections()
367 L.sort()
368 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000369 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000370 cf.set("a", "B", "value")
371 eq(cf.options("a"), ["b"])
372 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000373 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000374 with self.assertRaises(configparser.NoSectionError):
375 # section names are case-sensitive
376 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000377 self.assertTrue(cf.has_option("a", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000378 cf.set("A", "A-B", "A-B value")
379 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000380 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000381 cf.has_option("A", opt),
382 "has_option() returned false for option which should exist")
383 eq(cf.options("A"), ["a-b"])
384 eq(cf.options("a"), ["b"])
385 cf.remove_option("a", "B")
386 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000387
Fred Drakec6f28912002-10-25 19:40:49 +0000388 # SF bug #432369:
389 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000390 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000391 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000392 eq(cf.options("MySection"), ["option"])
393 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000394
Fred Drakec6f28912002-10-25 19:40:49 +0000395 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000396 cf = self.fromstring("[section]\n"
397 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000398 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000399 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000400
Fred Drake3c823aa2001-02-26 21:55:34 +0000401
Łukasz Langa26d513c2010-11-10 18:57:39 +0000402 def test_case_sensitivity_mapping_access(self):
403 cf = self.newconfig()
404 cf["A"] = {}
405 cf["a"] = {"B": "value"}
406 cf["B"] = {}
407 L = [section for section in cf]
408 L.sort()
409 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000410 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000411 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000412 eq(cf["a"].keys(), {"b"})
413 eq(cf["a"]["b"], "value",
414 "could not locate option, expecting case-insensitive option names")
415 with self.assertRaises(KeyError):
416 # section names are case-sensitive
417 cf["b"]["A"] = "value"
418 self.assertTrue("b" in cf["a"])
419 cf["A"]["A-B"] = "A-B value"
420 for opt in ("a-b", "A-b", "a-B", "A-B"):
421 self.assertTrue(
422 opt in cf["A"],
423 "has_option() returned false for option which should exist")
424 eq(cf["A"].keys(), {"a-b"})
425 eq(cf["a"].keys(), {"b"})
426 del cf["a"]["B"]
427 elem_eq(cf["a"].keys(), {})
428
429 # SF bug #432369:
430 cf = self.fromstring(
431 "[MySection]\nOption{} first line \n\tsecond line \n".format(
432 self.delimiters[0]))
433 eq(cf["MySection"].keys(), {"option"})
434 eq(cf["MySection"]["Option"], "first line\nsecond line")
435
436 # SF bug #561822:
437 cf = self.fromstring("[section]\n"
438 "nekey{}nevalue\n".format(self.delimiters[0]),
439 defaults={"key":"value"})
440 self.assertTrue("Key" in cf["section"])
441
David Goodger68a1abd2004-10-03 15:40:25 +0000442 def test_default_case_sensitivity(self):
443 cf = self.newconfig({"foo": "Bar"})
444 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000445 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000446 "could not locate option, expecting case-insensitive option names")
447 cf = self.newconfig({"Foo": "Bar"})
448 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000449 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000450 "could not locate option, expecting case-insensitive defaults")
451
Fred Drakec6f28912002-10-25 19:40:49 +0000452 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000453 cf = self.newconfig()
454 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000455 "[Foo]\n"
456 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000457 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[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000460 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000461 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000462 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000463 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000464 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000465 "[Foo]\n wrong-indent\n")
466 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000467 # read_file on a real file
468 tricky = support.findfile("cfgparser.3")
469 if self.delimiters[0] == '=':
470 error = configparser.ParsingError
471 expected = (tricky,)
472 else:
473 error = configparser.MissingSectionHeaderError
474 expected = (tricky, 1,
475 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000476 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000477 e = self.parse_error(cf, error, f)
478 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000479
Fred Drakea4923622010-08-09 12:52:45 +0000480 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000481 if hasattr(src, 'readline'):
482 sio = src
483 else:
484 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000485 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000486 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000487 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000488
Fred Drakec6f28912002-10-25 19:40:49 +0000489 def test_query_errors(self):
490 cf = self.newconfig()
491 self.assertEqual(cf.sections(), [],
492 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000493 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000494 "new ConfigParser should have no acknowledged "
495 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000496 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000497 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000498 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000499 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000500 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000501 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000502 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000503 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000504 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000505
Fred Drakea4923622010-08-09 12:52:45 +0000506 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000507 try:
Fred Drakea4923622010-08-09 12:52:45 +0000508 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000509 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000510 return e
511 else:
512 self.fail("expected exception type %s.%s"
513 % (exc.__module__, exc.__name__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000514
Fred Drakec6f28912002-10-25 19:40:49 +0000515 def test_boolean(self):
516 cf = self.fromstring(
517 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000518 "T1{equals}1\n"
519 "T2{equals}TRUE\n"
520 "T3{equals}True\n"
521 "T4{equals}oN\n"
522 "T5{equals}yes\n"
523 "F1{equals}0\n"
524 "F2{equals}FALSE\n"
525 "F3{equals}False\n"
526 "F4{equals}oFF\n"
527 "F5{equals}nO\n"
528 "E1{equals}2\n"
529 "E2{equals}foo\n"
530 "E3{equals}-1\n"
531 "E4{equals}0.1\n"
532 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000533 )
534 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000535 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
536 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000537 self.assertRaises(ValueError,
538 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000539
Fred Drakec6f28912002-10-25 19:40:49 +0000540 def test_weird_errors(self):
541 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000542 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000543 with self.assertRaises(configparser.DuplicateSectionError) as cm:
544 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000545 e = cm.exception
546 self.assertEqual(str(e), "Section 'Foo' already exists")
547 self.assertEqual(e.args, ("Foo", None, None))
548
549 if self.strict:
550 with self.assertRaises(configparser.DuplicateSectionError) as cm:
551 cf.read_string(textwrap.dedent("""\
552 [Foo]
553 will this be added{equals}True
554 [Bar]
555 what about this{equals}True
556 [Foo]
557 oops{equals}this won't
558 """.format(equals=self.delimiters[0])), source='<foo-bar>')
559 e = cm.exception
560 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
561 "section 'Foo' already exists")
562 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
563
564 with self.assertRaises(configparser.DuplicateOptionError) as cm:
565 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
566 e = cm.exception
567 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
568 "in section 'Bar' already exists")
569 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000570
571 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000572 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000573 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000574 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000575 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000576 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000577 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000578 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000579 "[Long Line - With Comments!]\n"
580 "test {0[1]} we {comment} can\n"
581 " also {comment} place\n"
582 " comments {comment} in\n"
583 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000584 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
585 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000586 )
Fred Drake03c44a32010-02-19 06:08:41 +0000587 if self.allow_no_value:
588 config_string += (
589 "[Valueless]\n"
590 "option-without-value\n"
591 )
592
593 cf = self.fromstring(config_string)
Guido van Rossum34d19282007-08-09 01:03:29 +0000594 output = io.StringIO()
Fred Drakec6f28912002-10-25 19:40:49 +0000595 cf.write(output)
Fred Drake03c44a32010-02-19 06:08:41 +0000596 expect_string = (
Łukasz Langac264c092010-11-20 16:15:37 +0000597 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000598 "foo {equals} another very\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000599 "\tlong line\n"
600 "\n"
601 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000602 "foo {equals} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000603 "\tlikes it.\n"
604 "\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000605 "[Long Line - With Comments!]\n"
606 "test {equals} we\n"
607 "\talso\n"
608 "\tcomments\n"
609 "\tmultiline\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000610 "\n".format(equals=self.delimiters[0],
611 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000612 )
Fred Drake03c44a32010-02-19 06:08:41 +0000613 if self.allow_no_value:
614 expect_string += (
615 "[Valueless]\n"
616 "option-without-value\n"
617 "\n"
618 )
619 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000620
Fred Drakeabc086f2004-05-18 03:29:52 +0000621 def test_set_string_types(self):
622 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000623 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000624 # Check that we don't get an exception when setting values in
625 # an existing section using strings:
626 class mystr(str):
627 pass
628 cf.set("sect", "option1", "splat")
629 cf.set("sect", "option1", mystr("splat"))
630 cf.set("sect", "option2", "splat")
631 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000632 cf.set("sect", "option1", "splat")
633 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000634
Fred Drake82903142004-05-18 04:24:02 +0000635 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000636 if self.delimiters[0] != '=':
637 # skip reading the file if we're using an incompatible format
638 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000639 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000640 # check when we pass a mix of readable and non-readable files:
641 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000642 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000643 self.assertEqual(parsed_files, [file1])
644 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
645 # check when we pass only a filename:
646 cf = self.newconfig()
647 parsed_files = cf.read(file1)
648 self.assertEqual(parsed_files, [file1])
649 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
650 # check when we pass only missing files:
651 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000652 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000653 self.assertEqual(parsed_files, [])
654 # check when we pass no files:
655 cf = self.newconfig()
656 parsed_files = cf.read([])
657 self.assertEqual(parsed_files, [])
658
Fred Drakec6f28912002-10-25 19:40:49 +0000659 # shared by subclasses
660 def get_interpolation_config(self):
661 return self.fromstring(
662 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000663 "bar{equals}something %(with1)s interpolation (1 step)\n"
664 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
665 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
666 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
667 "with11{equals}%(with10)s\n"
668 "with10{equals}%(with9)s\n"
669 "with9{equals}%(with8)s\n"
670 "with8{equals}%(With7)s\n"
671 "with7{equals}%(WITH6)s\n"
672 "with6{equals}%(with5)s\n"
673 "With5{equals}%(with4)s\n"
674 "WITH4{equals}%(with3)s\n"
675 "with3{equals}%(with2)s\n"
676 "with2{equals}%(with1)s\n"
677 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000678 "\n"
679 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000680 "foo{equals}%(bar)s\n"
681 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000682 "\n"
683 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000684 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000685 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000686
Fred Drake98e3b292002-10-25 20:42:44 +0000687 def check_items_config(self, expected):
688 cf = self.fromstring(
689 "[section]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000690 "name {0[0]} value\n"
691 "key{0[1]} |%(name)s| \n"
Łukasz Langa5c863392010-11-21 13:41:35 +0000692 "getdefault{0[1]} |%(default)s|\n".format(self.delimiters),
Fred Drake98e3b292002-10-25 20:42:44 +0000693 defaults={"default": "<default>"})
694 L = list(cf.items("section"))
695 L.sort()
696 self.assertEqual(L, expected)
697
Fred Drake8ef67672000-09-27 22:45:25 +0000698
Fred Drakea4923622010-08-09 12:52:45 +0000699class StrictTestCase(BasicTestCase):
700 config_class = configparser.RawConfigParser
701 strict = True
702
703
Georg Brandl96a60ae2010-07-28 13:13:46 +0000704class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000705 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000706
707 def test_interpolation(self):
708 cf = self.get_interpolation_config()
709 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000710 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
711 eq(cf.get("Foo", "bar9"),
712 "something with lots of interpolation (9 steps)")
713 eq(cf.get("Foo", "bar10"),
714 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000715 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000716 if self.interpolation == configparser._UNSET:
717 self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
718 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
719 self.assertEqual(e.args, ("bar11", "Foo",
720 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000721
Fred Drake54782192002-12-31 06:57:25 +0000722 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000723 cf = self.get_interpolation_config()
724 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000725 "Interpolation Error", "name")
726 self.assertEqual(e.reference, "reference")
727 self.assertEqual(e.section, "Interpolation Error")
728 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000729 if self.interpolation == configparser._UNSET:
730 self.assertEqual(e.args, ('name', 'Interpolation Error',
731 '', 'reference'))
732 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
733 self.assertEqual(e.args, ('name', 'Interpolation Error',
734 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000735
Fred Drake98e3b292002-10-25 20:42:44 +0000736 def test_items(self):
737 self.check_items_config([('default', '<default>'),
738 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000739 ('key', '|value|'),
740 ('name', 'value')])
741
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000742 def test_safe_interpolation(self):
743 # See http://www.python.org/sf/511737
744 cf = self.fromstring("[section]\n"
745 "option1{eq}xxx\n"
746 "option2{eq}%(option1)s/xxx\n"
747 "ok{eq}%(option1)s/%%s\n"
748 "not_ok{eq}%(option2)s/%%s".format(
749 eq=self.delimiters[0]))
750 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
751 if self.interpolation == configparser._UNSET:
752 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
753 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
754 with self.assertRaises(TypeError):
755 cf.get("section", "not_ok")
756
757 def test_set_malformatted_interpolation(self):
758 cf = self.fromstring("[sect]\n"
759 "option1{eq}foo\n".format(eq=self.delimiters[0]))
760
761 self.assertEqual(cf.get('sect', "option1"), "foo")
762
763 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
764 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
765 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
766
767 self.assertEqual(cf.get('sect', "option1"), "foo")
768
769 # bug #5741: double percents are *not* malformed
770 cf.set("sect", "option2", "foo%%bar")
771 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
772
David Goodger1cbf2062004-10-03 15:55:09 +0000773 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000774 cf = self.fromstring("[sect]\n"
775 "option1{eq}foo\n".format(eq=self.delimiters[0]))
776 # Check that we get a TypeError when setting non-string values
777 # in an existing section:
778 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
779 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
780 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
781 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
782 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
783 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
784 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
785 self.assertRaises(TypeError, cf.add_section, 123)
786
787 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000788 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000789 self.assertRaises(ValueError, cf.add_section, self.default_section)
790
791class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
792 config_class = configparser.ConfigParser
793 interpolation = configparser.LegacyInterpolation()
794
795 def test_set_malformatted_interpolation(self):
796 cf = self.fromstring("[sect]\n"
797 "option1{eq}foo\n".format(eq=self.delimiters[0]))
798
799 self.assertEqual(cf.get('sect', "option1"), "foo")
800
801 cf.set("sect", "option1", "%foo")
802 self.assertEqual(cf.get('sect', "option1"), "%foo")
803 cf.set("sect", "option1", "foo%")
804 self.assertEqual(cf.get('sect', "option1"), "foo%")
805 cf.set("sect", "option1", "f%oo")
806 self.assertEqual(cf.get('sect', "option1"), "f%oo")
807
808 # bug #5741: double percents are *not* malformed
809 cf.set("sect", "option2", "foo%%bar")
810 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000811
Georg Brandl96a60ae2010-07-28 13:13:46 +0000812class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
813 delimiters = (':=', '$')
814 comment_prefixes = ('//', '"')
815
Łukasz Langac264c092010-11-20 16:15:37 +0000816class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
817 default_section = 'general'
818
Georg Brandl96a60ae2010-07-28 13:13:46 +0000819class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000820 config_class = configparser.ConfigParser
821 wonderful_spam = ("I'm having spam spam spam spam "
822 "spam spam spam beaked beans spam "
823 "spam spam and spam!").replace(' ', '\t\n')
824
825 def setUp(self):
826 cf = self.newconfig()
827 for i in range(100):
828 s = 'section{}'.format(i)
829 cf.add_section(s)
830 for j in range(10):
831 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
832 with open(support.TESTFN, 'w') as f:
833 cf.write(f)
834
835 def tearDown(self):
836 os.unlink(support.TESTFN)
837
838 def test_dominating_multiline_values(self):
839 # We're reading from file because this is where the code changed
840 # during performance updates in Python 3.2
841 cf_from_file = self.newconfig()
842 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000843 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000844 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
845 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000846
Georg Brandl96a60ae2010-07-28 13:13:46 +0000847class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000848 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000849
850 def test_interpolation(self):
851 cf = self.get_interpolation_config()
852 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000853 eq(cf.get("Foo", "bar"),
854 "something %(with1)s interpolation (1 step)")
855 eq(cf.get("Foo", "bar9"),
856 "something %(with9)s lots of interpolation (9 steps)")
857 eq(cf.get("Foo", "bar10"),
858 "something %(with10)s lots of interpolation (10 steps)")
859 eq(cf.get("Foo", "bar11"),
860 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000861
Fred Drake98e3b292002-10-25 20:42:44 +0000862 def test_items(self):
863 self.check_items_config([('default', '<default>'),
864 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000865 ('key', '|%(name)s|'),
866 ('name', 'value')])
867
David Goodger1cbf2062004-10-03 15:55:09 +0000868 def test_set_nonstring_types(self):
869 cf = self.newconfig()
870 cf.add_section('non-string')
871 cf.set('non-string', 'int', 1)
872 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
873 cf.set('non-string', 'dict', {'pi': 3.14159})
874 self.assertEqual(cf.get('non-string', 'int'), 1)
875 self.assertEqual(cf.get('non-string', 'list'),
876 [0, 1, 1, 2, 3, 5, 8, 13])
877 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000878 cf.add_section(123)
879 cf.set(123, 'this is sick', True)
880 self.assertEqual(cf.get(123, 'this is sick'), True)
881 if cf._dict.__class__ is configparser._default_dict:
882 # would not work for SortedDict; only checking for the most common
883 # default dictionary (OrderedDict)
884 cf.optionxform = lambda x: x
885 cf.set('non-string', 1, 1)
886 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +0000887
Georg Brandl96a60ae2010-07-28 13:13:46 +0000888class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
889 delimiters = (':=', '$')
890 comment_prefixes = ('//', '"')
891
892class RawConfigParserTestSambaConf(BasicTestCase):
893 config_class = configparser.RawConfigParser
894 comment_prefixes = ('#', ';', '//', '----')
895 empty_lines_in_values = False
896
897 def test_reading(self):
898 smbconf = support.findfile("cfgparser.2")
899 # check when we pass a mix of readable and non-readable files:
900 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000901 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000902 self.assertEqual(parsed_files, [smbconf])
903 sections = ['global', 'homes', 'printers',
904 'print$', 'pdf-generator', 'tmp', 'Agustin']
905 self.assertEqual(cf.sections(), sections)
906 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
907 self.assertEqual(cf.getint("global", "max log size"), 50)
908 self.assertEqual(cf.get("global", "hosts allow"), "127.")
909 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000910
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000911class ConfigParserTestCaseExtendedInterpolation(BasicTestCase):
912 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +0000913 interpolation = configparser.ExtendedInterpolation()
914 default_section = 'common'
915
916 def test_extended_interpolation(self):
917 cf = self.fromstring(textwrap.dedent("""
918 [common]
919 favourite Beatle = Paul
920 favourite color = green
921
922 [tom]
923 favourite band = ${favourite color} day
924 favourite pope = John ${favourite Beatle} II
925 sequel = ${favourite pope}I
926
927 [ambv]
928 favourite Beatle = George
929 son of Edward VII = ${favourite Beatle} V
930 son of George V = ${son of Edward VII}I
931
932 [stanley]
933 favourite Beatle = ${ambv:favourite Beatle}
934 favourite pope = ${tom:favourite pope}
935 favourite color = black
936 favourite state of mind = paranoid
937 favourite movie = soylent ${common:favourite color}
938 favourite song = ${favourite color} sabbath - ${favourite state of mind}
939 """).strip())
940
941 eq = self.assertEqual
942 eq(cf['common']['favourite Beatle'], 'Paul')
943 eq(cf['common']['favourite color'], 'green')
944 eq(cf['tom']['favourite Beatle'], 'Paul')
945 eq(cf['tom']['favourite color'], 'green')
946 eq(cf['tom']['favourite band'], 'green day')
947 eq(cf['tom']['favourite pope'], 'John Paul II')
948 eq(cf['tom']['sequel'], 'John Paul III')
949 eq(cf['ambv']['favourite Beatle'], 'George')
950 eq(cf['ambv']['favourite color'], 'green')
951 eq(cf['ambv']['son of Edward VII'], 'George V')
952 eq(cf['ambv']['son of George V'], 'George VI')
953 eq(cf['stanley']['favourite Beatle'], 'George')
954 eq(cf['stanley']['favourite color'], 'black')
955 eq(cf['stanley']['favourite state of mind'], 'paranoid')
956 eq(cf['stanley']['favourite movie'], 'soylent green')
957 eq(cf['stanley']['favourite pope'], 'John Paul II')
958 eq(cf['stanley']['favourite song'],
959 'black sabbath - paranoid')
960
961 def test_endless_loop(self):
962 cf = self.fromstring(textwrap.dedent("""
963 [one for you]
964 ping = ${one for me:pong}
965
966 [one for me]
967 pong = ${one for you:ping}
968 """).strip())
969
970 with self.assertRaises(configparser.InterpolationDepthError):
971 cf['one for you']['ping']
972
973
974
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000975class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +0000976 allow_no_value = True
977
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000978class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
979 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +0000980 delimiters = {'='}
981 comment_prefixes = {'#'}
982 allow_no_value = True
983
984 def test_cfgparser_dot_3(self):
985 tricky = support.findfile("cfgparser.3")
986 cf = self.newconfig()
987 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
988 self.assertEqual(cf.sections(), ['strange',
989 'corruption',
990 'yeah, sections can be '
991 'indented as well',
992 'another one!',
993 'no values here',
994 'tricky interpolation',
995 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +0000996 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +0000997 vars={'interpolate': '-1'}), -1)
998 with self.assertRaises(ValueError):
999 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001000 cf.getint(self.default_section, 'go', raw=True,
1001 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001002 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1003 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1004 longname = 'yeah, sections can be indented as well'
1005 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001006 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001007 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1008 # `go` from DEFAULT
1009 with self.assertRaises(configparser.InterpolationMissingOptionError):
1010 cf.items('no values here')
1011 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1012 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1013 cf.get('tricky interpolation', 'go'))
1014 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1015
1016 def test_unicode_failure(self):
1017 tricky = support.findfile("cfgparser.3")
1018 cf = self.newconfig()
1019 with self.assertRaises(UnicodeDecodeError):
1020 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001021
Fred Drake88444412010-09-03 04:22:36 +00001022
1023class Issue7005TestCase(unittest.TestCase):
1024 """Test output when None is set() as a value and allow_no_value == False.
1025
1026 http://bugs.python.org/issue7005
1027
1028 """
1029
1030 expected_output = "[section]\noption = None\n\n"
1031
1032 def prepare(self, config_class):
1033 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001034 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001035 cp.add_section("section")
1036 cp.set("section", "option", None)
1037 sio = io.StringIO()
1038 cp.write(sio)
1039 return sio.getvalue()
1040
1041 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001042 cp = configparser.ConfigParser(allow_no_value=False)
1043 cp.add_section("section")
1044 with self.assertRaises(TypeError):
1045 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001046
1047 def test_none_as_value_stringified_raw(self):
1048 output = self.prepare(configparser.RawConfigParser)
1049 self.assertEqual(output, self.expected_output)
1050
1051
Thomas Wouters89f507f2006-12-13 04:49:30 +00001052class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001053 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001054
1055 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001056 cf = self.fromstring("[b]\n"
1057 "o4=1\n"
1058 "o3=2\n"
1059 "o2=3\n"
1060 "o1=4\n"
1061 "[a]\n"
1062 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001063 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001064 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001065 self.assertEqual(output.getvalue(),
1066 "[a]\n"
1067 "k = v\n\n"
1068 "[b]\n"
1069 "o1 = 4\n"
1070 "o2 = 3\n"
1071 "o3 = 2\n"
1072 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001073
Fred Drake03c44a32010-02-19 06:08:41 +00001074
Georg Brandl96a60ae2010-07-28 13:13:46 +00001075class CompatibleTestCase(CfgParserTestCaseClass):
1076 config_class = configparser.RawConfigParser
Fred Drakecc645b92010-09-04 04:35:34 +00001077 comment_prefixes = configparser._COMPATIBLE
Georg Brandl96a60ae2010-07-28 13:13:46 +00001078
1079 def test_comment_handling(self):
1080 config_string = textwrap.dedent("""\
1081 [Commented Bar]
1082 baz=qwe ; a comment
1083 foo: bar # not a comment!
1084 # but this is a comment
1085 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001086 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001087 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001088 """)
1089 cf = self.fromstring(config_string)
1090 self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!')
1091 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001092 self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001093
1094
Fred Drakec6f28912002-10-25 19:40:49 +00001095def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001096 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001097 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001098 ConfigParserTestCaseNonStandardDelimiters,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001099 ConfigParserTestCaseNoValue,
1100 ConfigParserTestCaseExtendedInterpolation,
1101 ConfigParserTestCaseLegacyInterpolation,
1102 ConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001103 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001104 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001105 RawConfigParserTestCaseNonStandardDelimiters,
1106 RawConfigParserTestSambaConf,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001107 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001108 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001109 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001110 CompatibleTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001111 ConfigParserTestCaseNonStandardDefaultSection,
Fred Drake03c44a32010-02-19 06:08:41 +00001112 )
1113
Łukasz Langa535c0772010-12-04 13:48:13 +00001114def test_coverage(coverdir):
1115 trace = support.import_module('trace')
1116 tracer=trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
1117 count=1)
1118 tracer.run('test_main()')
1119 r=tracer.results()
1120 print("Writing coverage results...")
1121 r.write_results(show_missing=True, summary=True, coverdir=coverdir)
Fred Drake3af0eb82002-10-25 18:09:24 +00001122
Fred Drakec6f28912002-10-25 19:40:49 +00001123if __name__ == "__main__":
Łukasz Langa535c0772010-12-04 13:48:13 +00001124 if "-c" in sys.argv:
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001125 test_coverage('/tmp/configparser.cover')
Łukasz Langa535c0772010-12-04 13:48:13 +00001126 else:
1127 test_main()