blob: 696f642621e0fa0a2cadbc3e48c7c237b6204822 [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
Georg Brandl96a60ae2010-07-28 13:13:46 +00005import textwrap
Łukasz Langa535c0772010-12-04 13:48:13 +00006import unittest
Łukasz Langa71b37a52010-12-17 21:56:32 +00007import warnings
Fred Drake8ef67672000-09-27 22:45:25 +00008
Benjamin Petersonee8712c2008-05-20 21:35:26 +00009from test import support
Fred Drake3d5f7e82000-12-04 16:30:40 +000010
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080011
Raymond Hettingerf80680d2008-02-06 00:07:11 +000012class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000013
Thomas Wouters89f507f2006-12-13 04:49:30 +000014 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000015 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000016
17 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000018 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000019
Thomas Wouters89f507f2006-12-13 04:49:30 +000020 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000021 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000022
Łukasz Langae698cd52011-04-28 10:58:57 +020023 def iteritems(self):
24 return iter(self.items())
25
26 def iterkeys(self):
27 return iter(self.keys())
28
29 def itervalues(self):
30 return iter(self.values())
31
Thomas Wouters89f507f2006-12-13 04:49:30 +000032 __iter__ = iterkeys
Fred Drake3d5f7e82000-12-04 16:30:40 +000033
Fred Drake03c44a32010-02-19 06:08:41 +000034
Ezio Melottidc1fa802013-01-11 06:30:57 +020035class CfgParserTestCaseClass:
Fred Drake03c44a32010-02-19 06:08:41 +000036 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000037 delimiters = ('=', ':')
38 comment_prefixes = (';', '#')
Łukasz Langab25a7912010-12-17 01:32:29 +000039 inline_comment_prefixes = (';', '#')
Georg Brandl96a60ae2010-07-28 13:13:46 +000040 empty_lines_in_values = True
41 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000042 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000043 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000044 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000045
Fred Drakec6f28912002-10-25 19:40:49 +000046 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000047 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000048 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000049 allow_no_value=self.allow_no_value,
50 delimiters=self.delimiters,
51 comment_prefixes=self.comment_prefixes,
Łukasz Langab25a7912010-12-17 01:32:29 +000052 inline_comment_prefixes=self.inline_comment_prefixes,
Georg Brandl96a60ae2010-07-28 13:13:46 +000053 empty_lines_in_values=self.empty_lines_in_values,
54 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000055 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000056 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000057 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000058 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000059 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000060 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000061
Fred Drakec6f28912002-10-25 19:40:49 +000062 def fromstring(self, string, defaults=None):
63 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000064 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000065 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000066
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080067
Georg Brandl96a60ae2010-07-28 13:13:46 +000068class BasicTestCase(CfgParserTestCaseClass):
69
Fred Drakea4923622010-08-09 12:52:45 +000070 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000071 E = ['Commented Bar',
72 'Foo Bar',
73 'Internationalized Stuff',
74 'Long Line',
75 'Section\\with$weird%characters[\t',
76 'Spaces',
77 'Spacey Bar',
78 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000079 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000080 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000081
Fred Drake03c44a32010-02-19 06:08:41 +000082 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000083 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000084 E.sort()
Łukasz Langa71b37a52010-12-17 21:56:32 +000085 F = [('baz', 'qwe'), ('foo', 'bar3')]
Łukasz Langa26d513c2010-11-10 18:57:39 +000086
87 # API access
88 L = cf.sections()
89 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000090 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000091 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +000092 L = cf.items('Spacey Bar From The Beginning')
93 L.sort()
94 eq(L, F)
Fred Drake8ef67672000-09-27 22:45:25 +000095
Łukasz Langa26d513c2010-11-10 18:57:39 +000096 # mapping access
97 L = [section for section in cf]
98 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +000099 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000100 E.sort()
101 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000102 L = cf['Spacey Bar From The Beginning'].items()
103 L = sorted(list(L))
104 eq(L, F)
105 L = cf.items()
106 L = sorted(list(L))
107 self.assertEqual(len(L), len(E))
108 for name, section in L:
109 eq(name, section.name)
110 eq(cf.defaults(), cf[self.default_section])
Łukasz Langa26d513c2010-11-10 18:57:39 +0000111
Fred Drakec6f28912002-10-25 19:40:49 +0000112 # The use of spaces in the section names serves as a
113 # regression test for SourceForge bug #583248:
114 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +0000115
116 # API access
117 eq(cf.get('Foo Bar', 'foo'), 'bar1')
118 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
119 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000120 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000121 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000122 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000123 eq(cf.get('Spaces', 'key with spaces'), 'value')
124 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000125 eq(cf.getint('Types', 'int'), 42)
126 eq(cf.get('Types', 'int'), "42")
127 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
128 eq(cf.get('Types', 'float'), "0.44")
129 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000130 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000131 if self.allow_no_value:
132 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000133
Łukasz Langa26d513c2010-11-10 18:57:39 +0000134 # test vars= and fallback=
135 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000136 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
137 with self.assertRaises(configparser.NoSectionError):
138 cf.get('No Such Foo Bar', 'foo')
139 with self.assertRaises(configparser.NoOptionError):
140 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000141 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
142 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
143 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
144 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
145 eq(cf.getint('Types', 'int', fallback=18), 42)
146 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
147 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000148 with self.assertRaises(configparser.NoOptionError):
149 cf.getint('Types', 'no-such-int')
Fred Drakecc645b92010-09-04 04:35:34 +0000150 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000151 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000152 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000153 fallback=0.0), 0.0)
154 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000155 with self.assertRaises(configparser.NoOptionError):
156 cf.getfloat('Types', 'no-such-float')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000157 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
158 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000159 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000160 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000161 with self.assertRaises(configparser.NoOptionError):
162 cf.getboolean('Types', 'no-such-boolean')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000163 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000164 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000165 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000166 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000167 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000168
Łukasz Langa26d513c2010-11-10 18:57:39 +0000169 # mapping access
170 eq(cf['Foo Bar']['foo'], 'bar1')
171 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000172 section = cf['Spacey Bar From The Beginning']
173 eq(section.name, 'Spacey Bar From The Beginning')
174 self.assertIs(section.parser, cf)
175 with self.assertRaises(AttributeError):
176 section.name = 'Name is read-only'
177 with self.assertRaises(AttributeError):
178 section.parser = 'Parser is read-only'
179 eq(section['foo'], 'bar3')
180 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000181 eq(cf['Commented Bar']['foo'], 'bar4')
182 eq(cf['Commented Bar']['baz'], 'qwe')
183 eq(cf['Spaces']['key with spaces'], 'value')
184 eq(cf['Spaces']['another with spaces'], 'splat!')
185 eq(cf['Long Line']['foo'],
186 'this line is much, much longer than my editor\nlikes it.')
187 if self.allow_no_value:
188 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000189 # test vars= and fallback=
190 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
191 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
192 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
193 with self.assertRaises(KeyError):
194 cf['No Such Foo Bar']['foo']
195 with self.assertRaises(KeyError):
196 cf['Foo Bar']['no-such-foo']
197 with self.assertRaises(KeyError):
198 cf['No Such Foo Bar'].get('foo', fallback='baz')
199 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
200 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
Łukasz Langa71b37a52010-12-17 21:56:32 +0000201 eq(cf['Foo Bar'].get('no-such-foo'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000202 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
203 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
204 with self.assertRaises(KeyError):
205 cf['No Such Spacey Bar'].get('foo', None)
206 eq(cf['Types'].getint('int', 18), 42)
207 eq(cf['Types'].getint('int', fallback=18), 42)
208 eq(cf['Types'].getint('no-such-int', 18), 18)
209 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
210 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
211 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000212 eq(cf['Types'].getint('no-such-int'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000213 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
214 self.assertAlmostEqual(cf['Types'].getfloat('float',
215 fallback=0.0), 0.44)
216 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
217 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
218 fallback=0.0), 0.0)
219 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
220 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000221 eq(cf['Types'].getfloat('no-such-float'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000222 eq(cf['Types'].getboolean('boolean', True), False)
223 eq(cf['Types'].getboolean('boolean', fallback=True), False)
224 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
225 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
226 "yes") # sic!
227 eq(cf['Types'].getboolean('no-such-boolean', True), True)
228 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000229 eq(cf['Types'].getboolean('no-such-boolean'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000230 if self.allow_no_value:
231 eq(cf['NoValue'].get('option-without-value', False), None)
232 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
233 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
234 eq(cf['NoValue'].get('no-such-option-without-value',
235 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000236
Łukasz Langa71b37a52010-12-17 21:56:32 +0000237 # Make sure the right things happen for remove_section() and
238 # remove_option(); added to include check for SourceForge bug #123324.
Łukasz Langa26d513c2010-11-10 18:57:39 +0000239
Łukasz Langa71b37a52010-12-17 21:56:32 +0000240 cf[self.default_section]['this_value'] = '1'
241 cf[self.default_section]['that_value'] = '2'
242
243 # API access
244 self.assertTrue(cf.remove_section('Spaces'))
245 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
246 self.assertFalse(cf.remove_section('Spaces'))
247 self.assertFalse(cf.remove_section(self.default_section))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000248 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000249 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000250 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000251 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000252 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000253 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000254 " that was removed")
Łukasz Langa71b37a52010-12-17 21:56:32 +0000255 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
256 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
257 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
258 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
259 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
Fred Drakec6f28912002-10-25 19:40:49 +0000260
Michael Foordbd6c0792010-07-25 23:09:25 +0000261 with self.assertRaises(configparser.NoSectionError) as cm:
262 cf.remove_option('No Such Section', 'foo')
263 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000264
265 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000266 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000267
Łukasz Langa26d513c2010-11-10 18:57:39 +0000268 # mapping access
Łukasz Langa71b37a52010-12-17 21:56:32 +0000269 del cf['Types']
270 self.assertFalse('Types' in cf)
271 with self.assertRaises(KeyError):
272 del cf['Types']
273 with self.assertRaises(ValueError):
274 del cf[self.default_section]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000275 del cf['Spacey Bar']['foo']
276 self.assertFalse('foo' in cf['Spacey Bar'])
277 with self.assertRaises(KeyError):
278 del cf['Spacey Bar']['foo']
Łukasz Langa71b37a52010-12-17 21:56:32 +0000279 self.assertTrue('that_value' in cf['Spacey Bar'])
280 with self.assertRaises(KeyError):
281 del cf['Spacey Bar']['that_value']
282 del cf[self.default_section]['that_value']
283 self.assertFalse('that_value' in cf['Spacey Bar'])
284 with self.assertRaises(KeyError):
285 del cf[self.default_section]['that_value']
Łukasz Langa26d513c2010-11-10 18:57:39 +0000286 with self.assertRaises(KeyError):
287 del cf['No Such Section']['foo']
288
Łukasz Langa71b37a52010-12-17 21:56:32 +0000289 # Don't add new asserts below in this method as most of the options
290 # and sections are now removed.
291
Fred Drakea4923622010-08-09 12:52:45 +0000292 def test_basic(self):
293 config_string = """\
294[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000295foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000296[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000297foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000298[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000299 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000300 baz {0[0]} qwe
301[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000302foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000303baz{0[0]}qwe {1[0]}another one
304[Long Line]
305foo{0[1]} this line is much, much longer than my editor
306 likes it.
307[Section\\with$weird%characters[\t]
308[Internationalized Stuff]
309foo[bg]{0[1]} Bulgarian
310foo{0[0]}Default
311foo[en]{0[0]}English
312foo[de]{0[0]}Deutsch
313[Spaces]
314key with spaces {0[1]} value
315another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000316[Types]
317int {0[1]} 42
318float {0[0]} 0.44
319boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000320123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000321""".format(self.delimiters, self.comment_prefixes)
322 if self.allow_no_value:
323 config_string += (
324 "[NoValue]\n"
325 "option-without-value\n"
326 )
327 cf = self.fromstring(config_string)
328 self.basic_test(cf)
329 if self.strict:
330 with self.assertRaises(configparser.DuplicateOptionError):
331 cf.read_string(textwrap.dedent("""\
332 [Duplicate Options Here]
333 option {0[0]} with a value
334 option {0[1]} with another value
335 """.format(self.delimiters)))
336 with self.assertRaises(configparser.DuplicateSectionError):
337 cf.read_string(textwrap.dedent("""\
338 [And Now For Something]
339 completely different {0[0]} True
340 [And Now For Something]
341 the larch {0[1]} 1
342 """.format(self.delimiters)))
343 else:
344 cf.read_string(textwrap.dedent("""\
345 [Duplicate Options Here]
346 option {0[0]} with a value
347 option {0[1]} with another value
348 """.format(self.delimiters)))
349
350 cf.read_string(textwrap.dedent("""\
351 [And Now For Something]
352 completely different {0[0]} True
353 [And Now For Something]
354 the larch {0[1]} 1
355 """.format(self.delimiters)))
356
357 def test_basic_from_dict(self):
358 config = {
359 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000360 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000361 },
362 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000363 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000364 },
365 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000366 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000367 "baz": "qwe",
368 },
369 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000370 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000371 "baz": "qwe",
372 },
373 "Long Line": {
374 "foo": "this line is much, much longer than my editor\nlikes "
375 "it.",
376 },
377 "Section\\with$weird%characters[\t": {
378 },
379 "Internationalized Stuff": {
380 "foo[bg]": "Bulgarian",
381 "foo": "Default",
382 "foo[en]": "English",
383 "foo[de]": "Deutsch",
384 },
385 "Spaces": {
386 "key with spaces": "value",
387 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000388 },
389 "Types": {
390 "int": 42,
391 "float": 0.44,
392 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000393 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000394 },
Fred Drakea4923622010-08-09 12:52:45 +0000395 }
396 if self.allow_no_value:
397 config.update({
398 "NoValue": {
399 "option-without-value": None,
400 }
401 })
402 cf = self.newconfig()
403 cf.read_dict(config)
404 self.basic_test(cf)
405 if self.strict:
Łukasz Langa71b37a52010-12-17 21:56:32 +0000406 with self.assertRaises(configparser.DuplicateSectionError):
407 cf.read_dict({
408 '1': {'key': 'value'},
409 1: {'key2': 'value2'},
410 })
Fred Drakea4923622010-08-09 12:52:45 +0000411 with self.assertRaises(configparser.DuplicateOptionError):
412 cf.read_dict({
413 "Duplicate Options Here": {
414 'option': 'with a value',
415 'OPTION': 'with another value',
416 },
417 })
418 else:
419 cf.read_dict({
Łukasz Langa71b37a52010-12-17 21:56:32 +0000420 'section': {'key': 'value'},
421 'SECTION': {'key2': 'value2'},
422 })
423 cf.read_dict({
Fred Drakea4923622010-08-09 12:52:45 +0000424 "Duplicate Options Here": {
425 'option': 'with a value',
426 'OPTION': 'with another value',
427 },
428 })
429
Fred Drakec6f28912002-10-25 19:40:49 +0000430 def test_case_sensitivity(self):
431 cf = self.newconfig()
432 cf.add_section("A")
433 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000434 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000435 L = cf.sections()
436 L.sort()
437 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000438 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000439 cf.set("a", "B", "value")
440 eq(cf.options("a"), ["b"])
441 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000442 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000443 with self.assertRaises(configparser.NoSectionError):
444 # section names are case-sensitive
445 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000446 self.assertTrue(cf.has_option("a", "b"))
Łukasz Langa71b37a52010-12-17 21:56:32 +0000447 self.assertFalse(cf.has_option("b", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000448 cf.set("A", "A-B", "A-B value")
449 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000450 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000451 cf.has_option("A", opt),
452 "has_option() returned false for option which should exist")
453 eq(cf.options("A"), ["a-b"])
454 eq(cf.options("a"), ["b"])
455 cf.remove_option("a", "B")
456 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000457
Fred Drakec6f28912002-10-25 19:40:49 +0000458 # SF bug #432369:
459 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000460 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000461 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000462 eq(cf.options("MySection"), ["option"])
463 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000464
Fred Drakec6f28912002-10-25 19:40:49 +0000465 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000466 cf = self.fromstring("[section]\n"
467 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000468 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000469 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000470
Fred Drake3c823aa2001-02-26 21:55:34 +0000471
Łukasz Langa26d513c2010-11-10 18:57:39 +0000472 def test_case_sensitivity_mapping_access(self):
473 cf = self.newconfig()
474 cf["A"] = {}
475 cf["a"] = {"B": "value"}
476 cf["B"] = {}
477 L = [section for section in cf]
478 L.sort()
479 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000480 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000481 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000482 eq(cf["a"].keys(), {"b"})
483 eq(cf["a"]["b"], "value",
484 "could not locate option, expecting case-insensitive option names")
485 with self.assertRaises(KeyError):
486 # section names are case-sensitive
487 cf["b"]["A"] = "value"
488 self.assertTrue("b" in cf["a"])
489 cf["A"]["A-B"] = "A-B value"
490 for opt in ("a-b", "A-b", "a-B", "A-B"):
491 self.assertTrue(
492 opt in cf["A"],
493 "has_option() returned false for option which should exist")
494 eq(cf["A"].keys(), {"a-b"})
495 eq(cf["a"].keys(), {"b"})
496 del cf["a"]["B"]
497 elem_eq(cf["a"].keys(), {})
498
499 # SF bug #432369:
500 cf = self.fromstring(
501 "[MySection]\nOption{} first line \n\tsecond line \n".format(
502 self.delimiters[0]))
503 eq(cf["MySection"].keys(), {"option"})
504 eq(cf["MySection"]["Option"], "first line\nsecond line")
505
506 # SF bug #561822:
507 cf = self.fromstring("[section]\n"
508 "nekey{}nevalue\n".format(self.delimiters[0]),
509 defaults={"key":"value"})
510 self.assertTrue("Key" in cf["section"])
511
David Goodger68a1abd2004-10-03 15:40:25 +0000512 def test_default_case_sensitivity(self):
513 cf = self.newconfig({"foo": "Bar"})
514 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000515 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000516 "could not locate option, expecting case-insensitive option names")
517 cf = self.newconfig({"Foo": "Bar"})
518 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000519 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000520 "could not locate option, expecting case-insensitive defaults")
521
Fred Drakec6f28912002-10-25 19:40:49 +0000522 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000523 cf = self.newconfig()
524 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000525 "[Foo]\n"
526 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000527 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000528 "[Foo]\n"
529 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000530 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000531 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000532 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000533 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000534 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000535 "[Foo]\n wrong-indent\n")
536 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000537 # read_file on a real file
538 tricky = support.findfile("cfgparser.3")
539 if self.delimiters[0] == '=':
540 error = configparser.ParsingError
541 expected = (tricky,)
542 else:
543 error = configparser.MissingSectionHeaderError
544 expected = (tricky, 1,
545 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000546 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000547 e = self.parse_error(cf, error, f)
548 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000549
Fred Drakea4923622010-08-09 12:52:45 +0000550 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000551 if hasattr(src, 'readline'):
552 sio = src
553 else:
554 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000555 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000556 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000557 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000558
Fred Drakec6f28912002-10-25 19:40:49 +0000559 def test_query_errors(self):
560 cf = self.newconfig()
561 self.assertEqual(cf.sections(), [],
562 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000563 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000564 "new ConfigParser should have no acknowledged "
565 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000566 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000567 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000568 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000569 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000570 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000571 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000572 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000573 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000574 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000575
Fred Drakea4923622010-08-09 12:52:45 +0000576 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000577 try:
Fred Drakea4923622010-08-09 12:52:45 +0000578 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000579 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000580 return e
581 else:
582 self.fail("expected exception type %s.%s"
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300583 % (exc.__module__, exc.__qualname__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000584
Fred Drakec6f28912002-10-25 19:40:49 +0000585 def test_boolean(self):
586 cf = self.fromstring(
587 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000588 "T1{equals}1\n"
589 "T2{equals}TRUE\n"
590 "T3{equals}True\n"
591 "T4{equals}oN\n"
592 "T5{equals}yes\n"
593 "F1{equals}0\n"
594 "F2{equals}FALSE\n"
595 "F3{equals}False\n"
596 "F4{equals}oFF\n"
597 "F5{equals}nO\n"
598 "E1{equals}2\n"
599 "E2{equals}foo\n"
600 "E3{equals}-1\n"
601 "E4{equals}0.1\n"
602 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000603 )
604 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000605 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
606 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000607 self.assertRaises(ValueError,
608 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000609
Fred Drakec6f28912002-10-25 19:40:49 +0000610 def test_weird_errors(self):
611 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000612 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000613 with self.assertRaises(configparser.DuplicateSectionError) as cm:
614 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000615 e = cm.exception
616 self.assertEqual(str(e), "Section 'Foo' already exists")
617 self.assertEqual(e.args, ("Foo", None, None))
618
619 if self.strict:
620 with self.assertRaises(configparser.DuplicateSectionError) as cm:
621 cf.read_string(textwrap.dedent("""\
622 [Foo]
623 will this be added{equals}True
624 [Bar]
625 what about this{equals}True
626 [Foo]
627 oops{equals}this won't
628 """.format(equals=self.delimiters[0])), source='<foo-bar>')
629 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200630 self.assertEqual(str(e), "While reading from '<foo-bar>' "
631 "[line 5]: section 'Foo' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000632 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
633
634 with self.assertRaises(configparser.DuplicateOptionError) as cm:
635 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
636 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200637 self.assertEqual(str(e), "While reading from '<dict>': option "
638 "'opt' in section 'Bar' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000639 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000640
641 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000642 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000643 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000644 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000645 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000646 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000647 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000648 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000649 "[Long Line - With Comments!]\n"
650 "test {0[1]} we {comment} can\n"
651 " also {comment} place\n"
652 " comments {comment} in\n"
653 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000654 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
655 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000656 )
Fred Drake03c44a32010-02-19 06:08:41 +0000657 if self.allow_no_value:
658 config_string += (
659 "[Valueless]\n"
660 "option-without-value\n"
661 )
662
663 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000664 for space_around_delimiters in (True, False):
665 output = io.StringIO()
666 cf.write(output, space_around_delimiters=space_around_delimiters)
667 delimiter = self.delimiters[0]
668 if space_around_delimiters:
669 delimiter = " {} ".format(delimiter)
670 expect_string = (
671 "[{default_section}]\n"
672 "foo{equals}another very\n"
673 "\tlong line\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000674 "\n"
Łukasz Langa71b37a52010-12-17 21:56:32 +0000675 "[Long Line]\n"
676 "foo{equals}this line is much, much longer than my editor\n"
677 "\tlikes it.\n"
678 "\n"
679 "[Long Line - With Comments!]\n"
680 "test{equals}we\n"
681 "\talso\n"
682 "\tcomments\n"
683 "\tmultiline\n"
684 "\n".format(equals=delimiter,
685 default_section=self.default_section)
Fred Drake03c44a32010-02-19 06:08:41 +0000686 )
Łukasz Langa71b37a52010-12-17 21:56:32 +0000687 if self.allow_no_value:
688 expect_string += (
689 "[Valueless]\n"
690 "option-without-value\n"
691 "\n"
692 )
693 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000694
Fred Drakeabc086f2004-05-18 03:29:52 +0000695 def test_set_string_types(self):
696 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000697 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000698 # Check that we don't get an exception when setting values in
699 # an existing section using strings:
700 class mystr(str):
701 pass
702 cf.set("sect", "option1", "splat")
703 cf.set("sect", "option1", mystr("splat"))
704 cf.set("sect", "option2", "splat")
705 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000706 cf.set("sect", "option1", "splat")
707 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000708
Fred Drake82903142004-05-18 04:24:02 +0000709 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000710 if self.delimiters[0] != '=':
Zachary Ware9fe6d862013-12-08 00:20:35 -0600711 self.skipTest('incompatible format')
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000712 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000713 # check when we pass a mix of readable and non-readable files:
714 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000715 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000716 self.assertEqual(parsed_files, [file1])
717 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
718 # check when we pass only a filename:
719 cf = self.newconfig()
720 parsed_files = cf.read(file1)
721 self.assertEqual(parsed_files, [file1])
722 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
723 # check when we pass only missing files:
724 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000725 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000726 self.assertEqual(parsed_files, [])
727 # check when we pass no files:
728 cf = self.newconfig()
729 parsed_files = cf.read([])
730 self.assertEqual(parsed_files, [])
731
Fred Drakec6f28912002-10-25 19:40:49 +0000732 # shared by subclasses
733 def get_interpolation_config(self):
734 return self.fromstring(
735 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000736 "bar{equals}something %(with1)s interpolation (1 step)\n"
737 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
738 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
739 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
740 "with11{equals}%(with10)s\n"
741 "with10{equals}%(with9)s\n"
742 "with9{equals}%(with8)s\n"
743 "with8{equals}%(With7)s\n"
744 "with7{equals}%(WITH6)s\n"
745 "with6{equals}%(with5)s\n"
746 "With5{equals}%(with4)s\n"
747 "WITH4{equals}%(with3)s\n"
748 "with3{equals}%(with2)s\n"
749 "with2{equals}%(with1)s\n"
750 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000751 "\n"
752 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000753 "foo{equals}%(bar)s\n"
754 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000755 "\n"
756 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000757 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000758 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000759
Fred Drake98e3b292002-10-25 20:42:44 +0000760 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000761 cf = self.fromstring("""
762 [section]
763 name {0[0]} %(value)s
764 key{0[1]} |%(name)s|
765 getdefault{0[1]} |%(default)s|
766 """.format(self.delimiters), defaults={"default": "<default>"})
767 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000768 L.sort()
769 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000770 with self.assertRaises(configparser.NoSectionError):
771 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000772
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100773 def test_popitem(self):
774 cf = self.fromstring("""
775 [section1]
776 name1 {0[0]} value1
777 [section2]
778 name2 {0[0]} value2
779 [section3]
780 name3 {0[0]} value3
781 """.format(self.delimiters), defaults={"default": "<default>"})
782 self.assertEqual(cf.popitem()[0], 'section1')
783 self.assertEqual(cf.popitem()[0], 'section2')
784 self.assertEqual(cf.popitem()[0], 'section3')
785 with self.assertRaises(KeyError):
786 cf.popitem()
787
788 def test_clear(self):
789 cf = self.newconfig({"foo": "Bar"})
790 self.assertEqual(
791 cf.get(self.default_section, "Foo"), "Bar",
792 "could not locate option, expecting case-insensitive option names")
793 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
794 self.assertEqual(cf.sections(), ['zing'])
795 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
796 cf.clear()
797 self.assertEqual(set(cf.sections()), set())
798 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
799
Łukasz Langa02101942012-12-31 13:55:11 +0100800 def test_setitem(self):
801 cf = self.fromstring("""
802 [section1]
803 name1 {0[0]} value1
804 [section2]
805 name2 {0[0]} value2
806 [section3]
807 name3 {0[0]} value3
808 """.format(self.delimiters), defaults={"nameD": "valueD"})
809 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
810 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
811 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
812 self.assertEqual(cf['section1']['name1'], 'value1')
813 self.assertEqual(cf['section2']['name2'], 'value2')
814 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100815 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100816 cf['section2'] = {'name22': 'value22'}
817 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
818 self.assertEqual(cf['section2']['name22'], 'value22')
819 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100820 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100821 cf['section3'] = {}
822 self.assertEqual(set(cf['section3'].keys()), {'named'})
823 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100824 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100825 cf[self.default_section] = {}
826 self.assertEqual(set(cf[self.default_section].keys()), set())
827 self.assertEqual(set(cf['section1'].keys()), {'name1'})
828 self.assertEqual(set(cf['section2'].keys()), {'name22'})
829 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100830 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100831
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800832 def test_invalid_multiline_value(self):
833 if self.allow_no_value:
834 self.skipTest('if no_value is allowed, ParsingError is not raised')
835
836 invalid = textwrap.dedent("""\
837 [DEFAULT]
838 test {0} test
839 invalid""".format(self.delimiters[0])
840 )
841 cf = self.newconfig()
842 with self.assertRaises(configparser.ParsingError):
843 cf.read_string(invalid)
844 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
845 self.assertEqual(cf['DEFAULT']['test'], 'test')
846
Fred Drake8ef67672000-09-27 22:45:25 +0000847
Ezio Melottidc1fa802013-01-11 06:30:57 +0200848class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000849 config_class = configparser.RawConfigParser
850 strict = True
851
852
Ezio Melottidc1fa802013-01-11 06:30:57 +0200853class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000854 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000855
856 def test_interpolation(self):
857 cf = self.get_interpolation_config()
858 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000859 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
860 eq(cf.get("Foo", "bar9"),
861 "something with lots of interpolation (9 steps)")
862 eq(cf.get("Foo", "bar10"),
863 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000864 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000865 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200866 self.assertEqual(e.args, ("bar11", "Foo",
867 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000868 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
869 self.assertEqual(e.args, ("bar11", "Foo",
870 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000871
Fred Drake54782192002-12-31 06:57:25 +0000872 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000873 cf = self.get_interpolation_config()
874 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000875 "Interpolation Error", "name")
876 self.assertEqual(e.reference, "reference")
877 self.assertEqual(e.section, "Interpolation Error")
878 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000879 if self.interpolation == configparser._UNSET:
880 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200881 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000882 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
883 self.assertEqual(e.args, ('name', 'Interpolation Error',
884 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000885
Fred Drake98e3b292002-10-25 20:42:44 +0000886 def test_items(self):
887 self.check_items_config([('default', '<default>'),
888 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000889 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000890 ('name', 'value'),
891 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000892
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000893 def test_safe_interpolation(self):
894 # See http://www.python.org/sf/511737
895 cf = self.fromstring("[section]\n"
896 "option1{eq}xxx\n"
897 "option2{eq}%(option1)s/xxx\n"
898 "ok{eq}%(option1)s/%%s\n"
899 "not_ok{eq}%(option2)s/%%s".format(
900 eq=self.delimiters[0]))
901 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
902 if self.interpolation == configparser._UNSET:
903 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
904 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
905 with self.assertRaises(TypeError):
906 cf.get("section", "not_ok")
907
908 def test_set_malformatted_interpolation(self):
909 cf = self.fromstring("[sect]\n"
910 "option1{eq}foo\n".format(eq=self.delimiters[0]))
911
912 self.assertEqual(cf.get('sect', "option1"), "foo")
913
914 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
915 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
916 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
917
918 self.assertEqual(cf.get('sect', "option1"), "foo")
919
920 # bug #5741: double percents are *not* malformed
921 cf.set("sect", "option2", "foo%%bar")
922 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
923
David Goodger1cbf2062004-10-03 15:55:09 +0000924 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000925 cf = self.fromstring("[sect]\n"
926 "option1{eq}foo\n".format(eq=self.delimiters[0]))
927 # Check that we get a TypeError when setting non-string values
928 # in an existing section:
929 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
930 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
931 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
932 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
933 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
934 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
935 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
936 self.assertRaises(TypeError, cf.add_section, 123)
937
938 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000939 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000940 self.assertRaises(ValueError, cf.add_section, self.default_section)
941
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200942
Ezio Melottidc1fa802013-01-11 06:30:57 +0200943class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200944 config_class = configparser.ConfigParser
945 interpolation = None
946 ini = textwrap.dedent("""
947 [numbers]
948 one = 1
949 two = %(one)s * 2
950 three = ${common:one} * 3
951
952 [hexen]
953 sixteen = ${numbers:two} * 8
954 """).strip()
955
956 def assertMatchesIni(self, cf):
957 self.assertEqual(cf['numbers']['one'], '1')
958 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
959 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
960 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
961
962 def test_no_interpolation(self):
963 cf = self.fromstring(self.ini)
964 self.assertMatchesIni(cf)
965
966 def test_empty_case(self):
967 cf = self.newconfig()
968 self.assertIsNone(cf.read_string(""))
969
970 def test_none_as_default_interpolation(self):
971 class CustomConfigParser(configparser.ConfigParser):
972 _DEFAULT_INTERPOLATION = None
973
974 cf = CustomConfigParser()
975 cf.read_string(self.ini)
976 self.assertMatchesIni(cf)
977
978
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000979class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
980 config_class = configparser.ConfigParser
981 interpolation = configparser.LegacyInterpolation()
982
983 def test_set_malformatted_interpolation(self):
984 cf = self.fromstring("[sect]\n"
985 "option1{eq}foo\n".format(eq=self.delimiters[0]))
986
987 self.assertEqual(cf.get('sect', "option1"), "foo")
988
989 cf.set("sect", "option1", "%foo")
990 self.assertEqual(cf.get('sect', "option1"), "%foo")
991 cf.set("sect", "option1", "foo%")
992 self.assertEqual(cf.get('sect', "option1"), "foo%")
993 cf.set("sect", "option1", "f%oo")
994 self.assertEqual(cf.get('sect', "option1"), "f%oo")
995
996 # bug #5741: double percents are *not* malformed
997 cf.set("sect", "option2", "foo%%bar")
998 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000999
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001000
Georg Brandl96a60ae2010-07-28 13:13:46 +00001001class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1002 delimiters = (':=', '$')
1003 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001004 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001005
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001006
Łukasz Langac264c092010-11-20 16:15:37 +00001007class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1008 default_section = 'general'
1009
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001010
Ezio Melottidc1fa802013-01-11 06:30:57 +02001011class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001012 config_class = configparser.ConfigParser
1013 wonderful_spam = ("I'm having spam spam spam spam "
1014 "spam spam spam beaked beans spam "
1015 "spam spam and spam!").replace(' ', '\t\n')
1016
1017 def setUp(self):
1018 cf = self.newconfig()
1019 for i in range(100):
1020 s = 'section{}'.format(i)
1021 cf.add_section(s)
1022 for j in range(10):
1023 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1024 with open(support.TESTFN, 'w') as f:
1025 cf.write(f)
1026
1027 def tearDown(self):
1028 os.unlink(support.TESTFN)
1029
1030 def test_dominating_multiline_values(self):
1031 # We're reading from file because this is where the code changed
1032 # during performance updates in Python 3.2
1033 cf_from_file = self.newconfig()
1034 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001035 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001036 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1037 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001038
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001039
Ezio Melottidc1fa802013-01-11 06:30:57 +02001040class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001041 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001042
1043 def test_interpolation(self):
1044 cf = self.get_interpolation_config()
1045 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001046 eq(cf.get("Foo", "bar"),
1047 "something %(with1)s interpolation (1 step)")
1048 eq(cf.get("Foo", "bar9"),
1049 "something %(with9)s lots of interpolation (9 steps)")
1050 eq(cf.get("Foo", "bar10"),
1051 "something %(with10)s lots of interpolation (10 steps)")
1052 eq(cf.get("Foo", "bar11"),
1053 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001054
Fred Drake98e3b292002-10-25 20:42:44 +00001055 def test_items(self):
1056 self.check_items_config([('default', '<default>'),
1057 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001058 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001059 ('name', '%(value)s'),
1060 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001061
David Goodger1cbf2062004-10-03 15:55:09 +00001062 def test_set_nonstring_types(self):
1063 cf = self.newconfig()
1064 cf.add_section('non-string')
1065 cf.set('non-string', 'int', 1)
1066 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1067 cf.set('non-string', 'dict', {'pi': 3.14159})
1068 self.assertEqual(cf.get('non-string', 'int'), 1)
1069 self.assertEqual(cf.get('non-string', 'list'),
1070 [0, 1, 1, 2, 3, 5, 8, 13])
1071 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001072 cf.add_section(123)
1073 cf.set(123, 'this is sick', True)
1074 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001075 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001076 # would not work for SortedDict; only checking for the most common
1077 # default dictionary (OrderedDict)
1078 cf.optionxform = lambda x: x
1079 cf.set('non-string', 1, 1)
1080 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001081
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001082
Georg Brandl96a60ae2010-07-28 13:13:46 +00001083class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1084 delimiters = (':=', '$')
1085 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001086 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001087
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001088
Ezio Melottidc1fa802013-01-11 06:30:57 +02001089class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001090 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001091 comment_prefixes = ('#', ';', '----')
1092 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001093 empty_lines_in_values = False
1094
1095 def test_reading(self):
1096 smbconf = support.findfile("cfgparser.2")
1097 # check when we pass a mix of readable and non-readable files:
1098 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001099 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001100 self.assertEqual(parsed_files, [smbconf])
1101 sections = ['global', 'homes', 'printers',
1102 'print$', 'pdf-generator', 'tmp', 'Agustin']
1103 self.assertEqual(cf.sections(), sections)
1104 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1105 self.assertEqual(cf.getint("global", "max log size"), 50)
1106 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1107 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001108
Ezio Melottidc1fa802013-01-11 06:30:57 +02001109class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001110 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001111 interpolation = configparser.ExtendedInterpolation()
1112 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001113 strict = True
1114
1115 def fromstring(self, string, defaults=None, optionxform=None):
1116 cf = self.newconfig(defaults)
1117 if optionxform:
1118 cf.optionxform = optionxform
1119 cf.read_string(string)
1120 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001121
1122 def test_extended_interpolation(self):
1123 cf = self.fromstring(textwrap.dedent("""
1124 [common]
1125 favourite Beatle = Paul
1126 favourite color = green
1127
1128 [tom]
1129 favourite band = ${favourite color} day
1130 favourite pope = John ${favourite Beatle} II
1131 sequel = ${favourite pope}I
1132
1133 [ambv]
1134 favourite Beatle = George
1135 son of Edward VII = ${favourite Beatle} V
1136 son of George V = ${son of Edward VII}I
1137
1138 [stanley]
1139 favourite Beatle = ${ambv:favourite Beatle}
1140 favourite pope = ${tom:favourite pope}
1141 favourite color = black
1142 favourite state of mind = paranoid
1143 favourite movie = soylent ${common:favourite color}
1144 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1145 """).strip())
1146
1147 eq = self.assertEqual
1148 eq(cf['common']['favourite Beatle'], 'Paul')
1149 eq(cf['common']['favourite color'], 'green')
1150 eq(cf['tom']['favourite Beatle'], 'Paul')
1151 eq(cf['tom']['favourite color'], 'green')
1152 eq(cf['tom']['favourite band'], 'green day')
1153 eq(cf['tom']['favourite pope'], 'John Paul II')
1154 eq(cf['tom']['sequel'], 'John Paul III')
1155 eq(cf['ambv']['favourite Beatle'], 'George')
1156 eq(cf['ambv']['favourite color'], 'green')
1157 eq(cf['ambv']['son of Edward VII'], 'George V')
1158 eq(cf['ambv']['son of George V'], 'George VI')
1159 eq(cf['stanley']['favourite Beatle'], 'George')
1160 eq(cf['stanley']['favourite color'], 'black')
1161 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1162 eq(cf['stanley']['favourite movie'], 'soylent green')
1163 eq(cf['stanley']['favourite pope'], 'John Paul II')
1164 eq(cf['stanley']['favourite song'],
1165 'black sabbath - paranoid')
1166
1167 def test_endless_loop(self):
1168 cf = self.fromstring(textwrap.dedent("""
1169 [one for you]
1170 ping = ${one for me:pong}
1171
1172 [one for me]
1173 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001174
1175 [selfish]
1176 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001177 """).strip())
1178
1179 with self.assertRaises(configparser.InterpolationDepthError):
1180 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001181 with self.assertRaises(configparser.InterpolationDepthError):
1182 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001183
Łukasz Langa71b37a52010-12-17 21:56:32 +00001184 def test_strange_options(self):
1185 cf = self.fromstring("""
1186 [dollars]
1187 $var = $$value
1188 $var2 = ${$var}
1189 ${sick} = cannot interpolate me
1190
1191 [interpolated]
1192 $other = ${dollars:$var}
1193 $trying = ${dollars:${sick}}
1194 """)
1195
1196 self.assertEqual(cf['dollars']['$var'], '$value')
1197 self.assertEqual(cf['interpolated']['$other'], '$value')
1198 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1199 exception_class = configparser.InterpolationMissingOptionError
1200 with self.assertRaises(exception_class) as cm:
1201 cf['interpolated']['$trying']
1202 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001203 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001204
Łukasz Langae698cd52011-04-28 10:58:57 +02001205 def test_case_sensitivity_basic(self):
1206 ini = textwrap.dedent("""
1207 [common]
1208 optionlower = value
1209 OptionUpper = Value
1210
1211 [Common]
1212 optionlower = a better ${common:optionlower}
1213 OptionUpper = A Better ${common:OptionUpper}
1214
1215 [random]
1216 foolower = ${common:optionlower} redefined
1217 FooUpper = ${Common:OptionUpper} Redefined
1218 """).strip()
1219
1220 cf = self.fromstring(ini)
1221 eq = self.assertEqual
1222 eq(cf['common']['optionlower'], 'value')
1223 eq(cf['common']['OptionUpper'], 'Value')
1224 eq(cf['Common']['optionlower'], 'a better value')
1225 eq(cf['Common']['OptionUpper'], 'A Better Value')
1226 eq(cf['random']['foolower'], 'value redefined')
1227 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1228
1229 def test_case_sensitivity_conflicts(self):
1230 ini = textwrap.dedent("""
1231 [common]
1232 option = value
1233 Option = Value
1234
1235 [Common]
1236 option = a better ${common:option}
1237 Option = A Better ${common:Option}
1238
1239 [random]
1240 foo = ${common:option} redefined
1241 Foo = ${Common:Option} Redefined
1242 """).strip()
1243 with self.assertRaises(configparser.DuplicateOptionError):
1244 cf = self.fromstring(ini)
1245
1246 # raw options
1247 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1248 eq = self.assertEqual
1249 eq(cf['common']['option'], 'value')
1250 eq(cf['common']['Option'], 'Value')
1251 eq(cf['Common']['option'], 'a better value')
1252 eq(cf['Common']['Option'], 'A Better Value')
1253 eq(cf['random']['foo'], 'value redefined')
1254 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001255
1256 def test_other_errors(self):
1257 cf = self.fromstring("""
1258 [interpolation fail]
1259 case1 = ${where's the brace
1260 case2 = ${does_not_exist}
1261 case3 = ${wrong_section:wrong_value}
1262 case4 = ${i:like:colon:characters}
1263 case5 = $100 for Fail No 5!
1264 """)
1265
1266 with self.assertRaises(configparser.InterpolationSyntaxError):
1267 cf['interpolation fail']['case1']
1268 with self.assertRaises(configparser.InterpolationMissingOptionError):
1269 cf['interpolation fail']['case2']
1270 with self.assertRaises(configparser.InterpolationMissingOptionError):
1271 cf['interpolation fail']['case3']
1272 with self.assertRaises(configparser.InterpolationSyntaxError):
1273 cf['interpolation fail']['case4']
1274 with self.assertRaises(configparser.InterpolationSyntaxError):
1275 cf['interpolation fail']['case5']
1276 with self.assertRaises(ValueError):
1277 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001278
1279
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001280class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001281 allow_no_value = True
1282
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001283
Ezio Melottidc1fa802013-01-11 06:30:57 +02001284class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001285 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001286 delimiters = {'='}
1287 comment_prefixes = {'#'}
1288 allow_no_value = True
1289
1290 def test_cfgparser_dot_3(self):
1291 tricky = support.findfile("cfgparser.3")
1292 cf = self.newconfig()
1293 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1294 self.assertEqual(cf.sections(), ['strange',
1295 'corruption',
1296 'yeah, sections can be '
1297 'indented as well',
1298 'another one!',
1299 'no values here',
1300 'tricky interpolation',
1301 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001302 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001303 vars={'interpolate': '-1'}), -1)
1304 with self.assertRaises(ValueError):
1305 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001306 cf.getint(self.default_section, 'go', raw=True,
1307 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001308 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1309 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1310 longname = 'yeah, sections can be indented as well'
1311 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001312 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001313 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1314 # `go` from DEFAULT
1315 with self.assertRaises(configparser.InterpolationMissingOptionError):
1316 cf.items('no values here')
1317 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1318 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1319 cf.get('tricky interpolation', 'go'))
1320 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1321
1322 def test_unicode_failure(self):
1323 tricky = support.findfile("cfgparser.3")
1324 cf = self.newconfig()
1325 with self.assertRaises(UnicodeDecodeError):
1326 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001327
Fred Drake88444412010-09-03 04:22:36 +00001328
1329class Issue7005TestCase(unittest.TestCase):
1330 """Test output when None is set() as a value and allow_no_value == False.
1331
1332 http://bugs.python.org/issue7005
1333
1334 """
1335
1336 expected_output = "[section]\noption = None\n\n"
1337
1338 def prepare(self, config_class):
1339 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001340 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001341 cp.add_section("section")
1342 cp.set("section", "option", None)
1343 sio = io.StringIO()
1344 cp.write(sio)
1345 return sio.getvalue()
1346
1347 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001348 cp = configparser.ConfigParser(allow_no_value=False)
1349 cp.add_section("section")
1350 with self.assertRaises(TypeError):
1351 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001352
1353 def test_none_as_value_stringified_raw(self):
1354 output = self.prepare(configparser.RawConfigParser)
1355 self.assertEqual(output, self.expected_output)
1356
1357
Thomas Wouters89f507f2006-12-13 04:49:30 +00001358class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001359 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001360
1361 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001362 cf = self.fromstring("[b]\n"
1363 "o4=1\n"
1364 "o3=2\n"
1365 "o2=3\n"
1366 "o1=4\n"
1367 "[a]\n"
1368 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001369 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001370 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001371 self.assertEqual(output.getvalue(),
1372 "[a]\n"
1373 "k = v\n\n"
1374 "[b]\n"
1375 "o1 = 4\n"
1376 "o2 = 3\n"
1377 "o3 = 2\n"
1378 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001379
Fred Drake03c44a32010-02-19 06:08:41 +00001380
Ezio Melottidc1fa802013-01-11 06:30:57 +02001381class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001382 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001383 comment_prefixes = '#;'
1384 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001385
1386 def test_comment_handling(self):
1387 config_string = textwrap.dedent("""\
1388 [Commented Bar]
1389 baz=qwe ; a comment
1390 foo: bar # not a comment!
1391 # but this is a comment
1392 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001393 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001394 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001395 """)
1396 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001397 self.assertEqual(cf.get('Commented Bar', 'foo'),
1398 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001399 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001400 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1401 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001402
Ezio Melottidc1fa802013-01-11 06:30:57 +02001403class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001404 config_class = configparser.ConfigParser
1405
1406 def fromstring(self, string, defaults=None):
1407 cf = self.newconfig(defaults)
1408 cf.read_string(string)
1409 cf_copy = self.newconfig()
1410 cf_copy.read_dict(cf)
1411 # we have to clean up option duplicates that appeared because of
1412 # the magic DEFAULTSECT behaviour.
1413 for section in cf_copy.values():
1414 if section.name == self.default_section:
1415 continue
1416 for default, value in cf[self.default_section].items():
1417 if section[default] == value:
1418 del section[default]
1419 return cf_copy
1420
Łukasz Langadaab1c82011-04-27 18:10:05 +02001421
1422class FakeFile:
1423 def __init__(self):
1424 file_path = support.findfile("cfgparser.1")
1425 with open(file_path) as f:
1426 self.lines = f.readlines()
1427 self.lines.reverse()
1428
1429 def readline(self):
1430 if len(self.lines):
1431 return self.lines.pop()
1432 return ''
1433
1434
1435def readline_generator(f):
1436 """As advised in Doc/library/configparser.rst."""
1437 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001438 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001439 yield line
1440 line = f.readline()
1441
1442
1443class ReadFileTestCase(unittest.TestCase):
1444 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001445 file_paths = [support.findfile("cfgparser.1")]
1446 try:
1447 file_paths.append(file_paths[0].encode('utf8'))
1448 except UnicodeEncodeError:
1449 pass # unfortunately we can't test bytes on this path
1450 for file_path in file_paths:
1451 parser = configparser.ConfigParser()
1452 with open(file_path) as f:
1453 parser.read_file(f)
1454 self.assertIn("Foo Bar", parser)
1455 self.assertIn("foo", parser["Foo Bar"])
1456 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001457
1458 def test_iterable(self):
1459 lines = textwrap.dedent("""
1460 [Foo Bar]
1461 foo=newbar""").strip().split('\n')
1462 parser = configparser.ConfigParser()
1463 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001464 self.assertIn("Foo Bar", parser)
1465 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001466 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1467
1468 def test_readline_generator(self):
1469 """Issue #11670."""
1470 parser = configparser.ConfigParser()
1471 with self.assertRaises(TypeError):
1472 parser.read_file(FakeFile())
1473 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001474 self.assertIn("Foo Bar", parser)
1475 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001476 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1477
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001478 def test_source_as_bytes(self):
1479 """Issue #18260."""
1480 lines = textwrap.dedent("""
1481 [badbad]
1482 [badbad]""").strip().split('\n')
1483 parser = configparser.ConfigParser()
1484 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1485 parser.read_file(lines, source=b"badbad")
1486 self.assertEqual(
1487 str(dse.exception),
1488 "While reading from b'badbad' [line 2]: section 'badbad' "
1489 "already exists"
1490 )
1491 lines = textwrap.dedent("""
1492 [badbad]
1493 bad = bad
1494 bad = bad""").strip().split('\n')
1495 parser = configparser.ConfigParser()
1496 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1497 parser.read_file(lines, source=b"badbad")
1498 self.assertEqual(
1499 str(dse.exception),
1500 "While reading from b'badbad' [line 3]: option 'bad' in section "
1501 "'badbad' already exists"
1502 )
1503 lines = textwrap.dedent("""
1504 [badbad]
1505 = bad""").strip().split('\n')
1506 parser = configparser.ConfigParser()
1507 with self.assertRaises(configparser.ParsingError) as dse:
1508 parser.read_file(lines, source=b"badbad")
1509 self.assertEqual(
1510 str(dse.exception),
1511 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1512 )
1513 lines = textwrap.dedent("""
1514 [badbad
1515 bad = bad""").strip().split('\n')
1516 parser = configparser.ConfigParser()
1517 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1518 parser.read_file(lines, source=b"badbad")
1519 self.assertEqual(
1520 str(dse.exception),
1521 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1522 "'[badbad'"
1523 )
1524
Łukasz Langadaab1c82011-04-27 18:10:05 +02001525
Łukasz Langa71b37a52010-12-17 21:56:32 +00001526class CoverageOneHundredTestCase(unittest.TestCase):
1527 """Covers edge cases in the codebase."""
1528
1529 def test_duplicate_option_error(self):
1530 error = configparser.DuplicateOptionError('section', 'option')
1531 self.assertEqual(error.section, 'section')
1532 self.assertEqual(error.option, 'option')
1533 self.assertEqual(error.source, None)
1534 self.assertEqual(error.lineno, None)
1535 self.assertEqual(error.args, ('section', 'option', None, None))
1536 self.assertEqual(str(error), "Option 'option' in section 'section' "
1537 "already exists")
1538
1539 def test_interpolation_depth_error(self):
1540 error = configparser.InterpolationDepthError('option', 'section',
1541 'rawval')
1542 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1543 self.assertEqual(error.option, 'option')
1544 self.assertEqual(error.section, 'section')
1545
1546 def test_parsing_error(self):
1547 with self.assertRaises(ValueError) as cm:
1548 configparser.ParsingError()
1549 self.assertEqual(str(cm.exception), "Required argument `source' not "
1550 "given.")
1551 with self.assertRaises(ValueError) as cm:
1552 configparser.ParsingError(source='source', filename='filename')
1553 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1554 "and `source'. Use `source'.")
1555 error = configparser.ParsingError(filename='source')
1556 self.assertEqual(error.source, 'source')
1557 with warnings.catch_warnings(record=True) as w:
1558 warnings.simplefilter("always", DeprecationWarning)
1559 self.assertEqual(error.filename, 'source')
1560 error.filename = 'filename'
1561 self.assertEqual(error.source, 'filename')
1562 for warning in w:
1563 self.assertTrue(warning.category is DeprecationWarning)
1564
1565 def test_interpolation_validation(self):
1566 parser = configparser.ConfigParser()
1567 parser.read_string("""
1568 [section]
1569 invalid_percent = %
1570 invalid_reference = %(()
1571 invalid_variable = %(does_not_exist)s
1572 """)
1573 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1574 parser['section']['invalid_percent']
1575 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1576 "'(', found: '%'")
1577 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1578 parser['section']['invalid_reference']
1579 self.assertEqual(str(cm.exception), "bad interpolation variable "
1580 "reference '%(()'")
1581
1582 def test_readfp_deprecation(self):
1583 sio = io.StringIO("""
1584 [section]
1585 option = value
1586 """)
1587 parser = configparser.ConfigParser()
1588 with warnings.catch_warnings(record=True) as w:
1589 warnings.simplefilter("always", DeprecationWarning)
1590 parser.readfp(sio, filename='StringIO')
1591 for warning in w:
1592 self.assertTrue(warning.category is DeprecationWarning)
1593 self.assertEqual(len(parser), 2)
1594 self.assertEqual(parser['section']['option'], 'value')
1595
1596 def test_safeconfigparser_deprecation(self):
1597 with warnings.catch_warnings(record=True) as w:
1598 warnings.simplefilter("always", DeprecationWarning)
1599 parser = configparser.SafeConfigParser()
1600 for warning in w:
1601 self.assertTrue(warning.category is DeprecationWarning)
1602
1603 def test_sectionproxy_repr(self):
1604 parser = configparser.ConfigParser()
1605 parser.read_string("""
1606 [section]
1607 key = value
1608 """)
1609 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001610
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001611 def test_inconsistent_converters_state(self):
1612 parser = configparser.ConfigParser()
1613 import decimal
1614 parser.converters['decimal'] = decimal.Decimal
1615 parser.read_string("""
1616 [s1]
1617 one = 1
1618 [s2]
1619 two = 2
1620 """)
1621 self.assertIn('decimal', parser.converters)
1622 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1623 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1624 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1625 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1626 del parser.getdecimal
1627 with self.assertRaises(AttributeError):
1628 parser.getdecimal('s1', 'one')
1629 self.assertIn('decimal', parser.converters)
1630 del parser.converters['decimal']
1631 self.assertNotIn('decimal', parser.converters)
1632 with self.assertRaises(AttributeError):
1633 parser.getdecimal('s1', 'one')
1634 with self.assertRaises(AttributeError):
1635 parser['s1'].getdecimal('one')
1636 with self.assertRaises(AttributeError):
1637 parser['s2'].getdecimal('two')
1638
Łukasz Langae7851952012-01-20 14:57:55 +01001639
1640class ExceptionPicklingTestCase(unittest.TestCase):
1641 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1642
1643 def test_error(self):
1644 import pickle
1645 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001646 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1647 pickled = pickle.dumps(e1, proto)
1648 e2 = pickle.loads(pickled)
1649 self.assertEqual(e1.message, e2.message)
1650 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001651
1652 def test_nosectionerror(self):
1653 import pickle
1654 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001655 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1656 pickled = pickle.dumps(e1, proto)
1657 e2 = pickle.loads(pickled)
1658 self.assertEqual(e1.message, e2.message)
1659 self.assertEqual(e1.args, e2.args)
1660 self.assertEqual(e1.section, e2.section)
1661 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001662
1663 def test_nooptionerror(self):
1664 import pickle
1665 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001666 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1667 pickled = pickle.dumps(e1, proto)
1668 e2 = pickle.loads(pickled)
1669 self.assertEqual(e1.message, e2.message)
1670 self.assertEqual(e1.args, e2.args)
1671 self.assertEqual(e1.section, e2.section)
1672 self.assertEqual(e1.option, e2.option)
1673 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001674
1675 def test_duplicatesectionerror(self):
1676 import pickle
1677 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001678 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1679 pickled = pickle.dumps(e1, proto)
1680 e2 = pickle.loads(pickled)
1681 self.assertEqual(e1.message, e2.message)
1682 self.assertEqual(e1.args, e2.args)
1683 self.assertEqual(e1.section, e2.section)
1684 self.assertEqual(e1.source, e2.source)
1685 self.assertEqual(e1.lineno, e2.lineno)
1686 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001687
1688 def test_duplicateoptionerror(self):
1689 import pickle
1690 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1691 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001692 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1693 pickled = pickle.dumps(e1, proto)
1694 e2 = pickle.loads(pickled)
1695 self.assertEqual(e1.message, e2.message)
1696 self.assertEqual(e1.args, e2.args)
1697 self.assertEqual(e1.section, e2.section)
1698 self.assertEqual(e1.option, e2.option)
1699 self.assertEqual(e1.source, e2.source)
1700 self.assertEqual(e1.lineno, e2.lineno)
1701 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001702
1703 def test_interpolationerror(self):
1704 import pickle
1705 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001706 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1707 pickled = pickle.dumps(e1, proto)
1708 e2 = pickle.loads(pickled)
1709 self.assertEqual(e1.message, e2.message)
1710 self.assertEqual(e1.args, e2.args)
1711 self.assertEqual(e1.section, e2.section)
1712 self.assertEqual(e1.option, e2.option)
1713 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001714
1715 def test_interpolationmissingoptionerror(self):
1716 import pickle
1717 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1718 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001719 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1720 pickled = pickle.dumps(e1, proto)
1721 e2 = pickle.loads(pickled)
1722 self.assertEqual(e1.message, e2.message)
1723 self.assertEqual(e1.args, e2.args)
1724 self.assertEqual(e1.section, e2.section)
1725 self.assertEqual(e1.option, e2.option)
1726 self.assertEqual(e1.reference, e2.reference)
1727 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001728
1729 def test_interpolationsyntaxerror(self):
1730 import pickle
1731 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001732 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1733 pickled = pickle.dumps(e1, proto)
1734 e2 = pickle.loads(pickled)
1735 self.assertEqual(e1.message, e2.message)
1736 self.assertEqual(e1.args, e2.args)
1737 self.assertEqual(e1.section, e2.section)
1738 self.assertEqual(e1.option, e2.option)
1739 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001740
1741 def test_interpolationdeptherror(self):
1742 import pickle
1743 e1 = configparser.InterpolationDepthError('option', 'section',
1744 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001745 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1746 pickled = pickle.dumps(e1, proto)
1747 e2 = pickle.loads(pickled)
1748 self.assertEqual(e1.message, e2.message)
1749 self.assertEqual(e1.args, e2.args)
1750 self.assertEqual(e1.section, e2.section)
1751 self.assertEqual(e1.option, e2.option)
1752 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001753
1754 def test_parsingerror(self):
1755 import pickle
1756 e1 = configparser.ParsingError('source')
1757 e1.append(1, 'line1')
1758 e1.append(2, 'line2')
1759 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001760 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1761 pickled = pickle.dumps(e1, proto)
1762 e2 = pickle.loads(pickled)
1763 self.assertEqual(e1.message, e2.message)
1764 self.assertEqual(e1.args, e2.args)
1765 self.assertEqual(e1.source, e2.source)
1766 self.assertEqual(e1.errors, e2.errors)
1767 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001768 e1 = configparser.ParsingError(filename='filename')
1769 e1.append(1, 'line1')
1770 e1.append(2, 'line2')
1771 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001772 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1773 pickled = pickle.dumps(e1, proto)
1774 e2 = pickle.loads(pickled)
1775 self.assertEqual(e1.message, e2.message)
1776 self.assertEqual(e1.args, e2.args)
1777 self.assertEqual(e1.source, e2.source)
1778 self.assertEqual(e1.errors, e2.errors)
1779 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001780
1781 def test_missingsectionheadererror(self):
1782 import pickle
1783 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001784 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1785 pickled = pickle.dumps(e1, proto)
1786 e2 = pickle.loads(pickled)
1787 self.assertEqual(e1.message, e2.message)
1788 self.assertEqual(e1.args, e2.args)
1789 self.assertEqual(e1.line, e2.line)
1790 self.assertEqual(e1.source, e2.source)
1791 self.assertEqual(e1.lineno, e2.lineno)
1792 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001793
1794
Łukasz Langacba24322012-07-07 18:54:08 +02001795class InlineCommentStrippingTestCase(unittest.TestCase):
1796 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1797 delimiter occurs earlier without preceding space.."""
1798
1799 def test_stripping(self):
1800 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1801 '//'))
1802 cfg.read_string("""
1803 [section]
1804 k1 = v1;still v1
1805 k2 = v2 ;a comment
1806 k3 = v3 ; also a comment
1807 k4 = v4;still v4 ;a comment
1808 k5 = v5;still v5 ; also a comment
1809 k6 = v6;still v6; and still v6 ;a comment
1810 k7 = v7;still v7; and still v7 ; also a comment
1811
1812 [multiprefix]
1813 k1 = v1;still v1 #a comment ; yeah, pretty much
1814 k2 = v2 // this already is a comment ; continued
1815 k3 = v3;#//still v3# and still v3 ; a comment
1816 """)
1817 s = cfg['section']
1818 self.assertEqual(s['k1'], 'v1;still v1')
1819 self.assertEqual(s['k2'], 'v2')
1820 self.assertEqual(s['k3'], 'v3')
1821 self.assertEqual(s['k4'], 'v4;still v4')
1822 self.assertEqual(s['k5'], 'v5;still v5')
1823 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1824 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1825 s = cfg['multiprefix']
1826 self.assertEqual(s['k1'], 'v1;still v1')
1827 self.assertEqual(s['k2'], 'v2')
1828 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1829
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001830
Łukasz Langa949053b2014-09-04 01:36:33 -07001831class ExceptionContextTestCase(unittest.TestCase):
1832 """ Test that implementation details doesn't leak
1833 through raising exceptions. """
1834
1835 def test_get_basic_interpolation(self):
1836 parser = configparser.ConfigParser()
1837 parser.read_string("""
1838 [Paths]
1839 home_dir: /Users
1840 my_dir: %(home_dir1)s/lumberjack
1841 my_pictures: %(my_dir)s/Pictures
1842 """)
1843 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1844 with cm:
1845 parser.get('Paths', 'my_dir')
1846 self.assertIs(cm.exception.__suppress_context__, True)
1847
1848 def test_get_extended_interpolation(self):
1849 parser = configparser.ConfigParser(
1850 interpolation=configparser.ExtendedInterpolation())
1851 parser.read_string("""
1852 [Paths]
1853 home_dir: /Users
1854 my_dir: ${home_dir1}/lumberjack
1855 my_pictures: ${my_dir}/Pictures
1856 """)
1857 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1858 with cm:
1859 parser.get('Paths', 'my_dir')
1860 self.assertIs(cm.exception.__suppress_context__, True)
1861
1862 def test_missing_options(self):
1863 parser = configparser.ConfigParser()
1864 parser.read_string("""
1865 [Paths]
1866 home_dir: /Users
1867 """)
1868 with self.assertRaises(configparser.NoSectionError) as cm:
1869 parser.options('test')
1870 self.assertIs(cm.exception.__suppress_context__, True)
1871
1872 def test_missing_section(self):
1873 config = configparser.ConfigParser()
1874 with self.assertRaises(configparser.NoSectionError) as cm:
1875 config.set('Section1', 'an_int', '15')
1876 self.assertIs(cm.exception.__suppress_context__, True)
1877
1878 def test_remove_option(self):
1879 config = configparser.ConfigParser()
1880 with self.assertRaises(configparser.NoSectionError) as cm:
1881 config.remove_option('Section1', 'an_int')
1882 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001883
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001884
1885class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1886 """Introduced in 3.5, issue #18159."""
1887
1888 config_class = configparser.ConfigParser
1889
1890 def newconfig(self, defaults=None):
1891 instance = super().newconfig(defaults=defaults)
1892 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1893 if e.strip()]
1894 return instance
1895
1896 def test_converters(self):
1897 cfg = self.newconfig()
1898 self.assertIn('boolean', cfg.converters)
1899 self.assertIn('list', cfg.converters)
1900 self.assertIsNone(cfg.converters['int'])
1901 self.assertIsNone(cfg.converters['float'])
1902 self.assertIsNone(cfg.converters['boolean'])
1903 self.assertIsNotNone(cfg.converters['list'])
1904 self.assertEqual(len(cfg.converters), 4)
1905 with self.assertRaises(ValueError):
1906 cfg.converters[''] = lambda v: v
1907 with self.assertRaises(ValueError):
1908 cfg.converters[None] = lambda v: v
1909 cfg.read_string("""
1910 [s]
1911 str = string
1912 int = 1
1913 float = 0.5
1914 list = a b c d e f g
1915 bool = yes
1916 """)
1917 s = cfg['s']
1918 self.assertEqual(s['str'], 'string')
1919 self.assertEqual(s['int'], '1')
1920 self.assertEqual(s['float'], '0.5')
1921 self.assertEqual(s['list'], 'a b c d e f g')
1922 self.assertEqual(s['bool'], 'yes')
1923 self.assertEqual(cfg.get('s', 'str'), 'string')
1924 self.assertEqual(cfg.get('s', 'int'), '1')
1925 self.assertEqual(cfg.get('s', 'float'), '0.5')
1926 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1927 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1928 self.assertEqual(cfg.get('s', 'str'), 'string')
1929 self.assertEqual(cfg.getint('s', 'int'), 1)
1930 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1931 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1932 'e', 'f', 'g'])
1933 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1934 self.assertEqual(s.get('str'), 'string')
1935 self.assertEqual(s.getint('int'), 1)
1936 self.assertEqual(s.getfloat('float'), 0.5)
1937 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1938 'e', 'f', 'g'])
1939 self.assertEqual(s.getboolean('bool'), True)
1940 with self.assertRaises(AttributeError):
1941 cfg.getdecimal('s', 'float')
1942 with self.assertRaises(AttributeError):
1943 s.getdecimal('float')
1944 import decimal
1945 cfg.converters['decimal'] = decimal.Decimal
1946 self.assertIn('decimal', cfg.converters)
1947 self.assertIsNotNone(cfg.converters['decimal'])
1948 self.assertEqual(len(cfg.converters), 5)
1949 dec0_5 = decimal.Decimal('0.5')
1950 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1951 self.assertEqual(s.getdecimal('float'), dec0_5)
1952 del cfg.converters['decimal']
1953 self.assertNotIn('decimal', cfg.converters)
1954 self.assertEqual(len(cfg.converters), 4)
1955 with self.assertRaises(AttributeError):
1956 cfg.getdecimal('s', 'float')
1957 with self.assertRaises(AttributeError):
1958 s.getdecimal('float')
1959 with self.assertRaises(KeyError):
1960 del cfg.converters['decimal']
1961 with self.assertRaises(KeyError):
1962 del cfg.converters['']
1963 with self.assertRaises(KeyError):
1964 del cfg.converters[None]
1965
1966
1967class BlatantOverrideConvertersTestCase(unittest.TestCase):
1968 """What if somebody overrode a getboolean()? We want to make sure that in
1969 this case the automatic converters do not kick in."""
1970
1971 config = """
1972 [one]
1973 one = false
1974 two = false
1975 three = long story short
1976
1977 [two]
1978 one = false
1979 two = false
1980 three = four
1981 """
1982
1983 def test_converters_at_init(self):
1984 cfg = configparser.ConfigParser(converters={'len': len})
1985 cfg.read_string(self.config)
1986 self._test_len(cfg)
1987 self.assertIsNotNone(cfg.converters['len'])
1988
1989 def test_inheritance(self):
1990 class StrangeConfigParser(configparser.ConfigParser):
1991 gettysburg = 'a historic borough in south central Pennsylvania'
1992
1993 def getboolean(self, section, option, *, raw=False, vars=None,
1994 fallback=configparser._UNSET):
1995 if section == option:
1996 return True
1997 return super().getboolean(section, option, raw=raw, vars=vars,
1998 fallback=fallback)
1999 def getlen(self, section, option, *, raw=False, vars=None,
2000 fallback=configparser._UNSET):
2001 return self._get_conv(section, option, len, raw=raw, vars=vars,
2002 fallback=fallback)
2003
2004 cfg = StrangeConfigParser()
2005 cfg.read_string(self.config)
2006 self._test_len(cfg)
2007 self.assertIsNone(cfg.converters['len'])
2008 self.assertTrue(cfg.getboolean('one', 'one'))
2009 self.assertTrue(cfg.getboolean('two', 'two'))
2010 self.assertFalse(cfg.getboolean('one', 'two'))
2011 self.assertFalse(cfg.getboolean('two', 'one'))
2012 cfg.converters['boolean'] = cfg._convert_to_boolean
2013 self.assertFalse(cfg.getboolean('one', 'one'))
2014 self.assertFalse(cfg.getboolean('two', 'two'))
2015 self.assertFalse(cfg.getboolean('one', 'two'))
2016 self.assertFalse(cfg.getboolean('two', 'one'))
2017
2018 def _test_len(self, cfg):
2019 self.assertEqual(len(cfg.converters), 4)
2020 self.assertIn('boolean', cfg.converters)
2021 self.assertIn('len', cfg.converters)
2022 self.assertNotIn('tysburg', cfg.converters)
2023 self.assertIsNone(cfg.converters['int'])
2024 self.assertIsNone(cfg.converters['float'])
2025 self.assertIsNone(cfg.converters['boolean'])
2026 self.assertEqual(cfg.getlen('one', 'one'), 5)
2027 self.assertEqual(cfg.getlen('one', 'two'), 5)
2028 self.assertEqual(cfg.getlen('one', 'three'), 16)
2029 self.assertEqual(cfg.getlen('two', 'one'), 5)
2030 self.assertEqual(cfg.getlen('two', 'two'), 5)
2031 self.assertEqual(cfg.getlen('two', 'three'), 4)
2032 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2033 with self.assertRaises(configparser.NoOptionError):
2034 cfg.getlen('two', 'four')
2035 self.assertEqual(cfg['one'].getlen('one'), 5)
2036 self.assertEqual(cfg['one'].getlen('two'), 5)
2037 self.assertEqual(cfg['one'].getlen('three'), 16)
2038 self.assertEqual(cfg['two'].getlen('one'), 5)
2039 self.assertEqual(cfg['two'].getlen('two'), 5)
2040 self.assertEqual(cfg['two'].getlen('three'), 4)
2041 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2042 self.assertEqual(cfg['two'].getlen('four'), None)
2043
2044 def test_instance_assignment(self):
2045 cfg = configparser.ConfigParser()
2046 cfg.getboolean = lambda section, option: True
2047 cfg.getlen = lambda section, option: len(cfg[section][option])
2048 cfg.read_string(self.config)
2049 self.assertEqual(len(cfg.converters), 3)
2050 self.assertIn('boolean', cfg.converters)
2051 self.assertNotIn('len', cfg.converters)
2052 self.assertIsNone(cfg.converters['int'])
2053 self.assertIsNone(cfg.converters['float'])
2054 self.assertIsNone(cfg.converters['boolean'])
2055 self.assertTrue(cfg.getboolean('one', 'one'))
2056 self.assertTrue(cfg.getboolean('two', 'two'))
2057 self.assertTrue(cfg.getboolean('one', 'two'))
2058 self.assertTrue(cfg.getboolean('two', 'one'))
2059 cfg.converters['boolean'] = cfg._convert_to_boolean
2060 self.assertFalse(cfg.getboolean('one', 'one'))
2061 self.assertFalse(cfg.getboolean('two', 'two'))
2062 self.assertFalse(cfg.getboolean('one', 'two'))
2063 self.assertFalse(cfg.getboolean('two', 'one'))
2064 self.assertEqual(cfg.getlen('one', 'one'), 5)
2065 self.assertEqual(cfg.getlen('one', 'two'), 5)
2066 self.assertEqual(cfg.getlen('one', 'three'), 16)
2067 self.assertEqual(cfg.getlen('two', 'one'), 5)
2068 self.assertEqual(cfg.getlen('two', 'two'), 5)
2069 self.assertEqual(cfg.getlen('two', 'three'), 4)
2070 # If a getter impl is assigned straight to the instance, it won't
2071 # be available on the section proxies.
2072 with self.assertRaises(AttributeError):
2073 self.assertEqual(cfg['one'].getlen('one'), 5)
2074 with self.assertRaises(AttributeError):
2075 self.assertEqual(cfg['two'].getlen('one'), 5)
2076
2077
Martin Panter2b9b70b2016-09-09 06:46:48 +00002078class MiscTestCase(unittest.TestCase):
2079 def test__all__(self):
2080 blacklist = {"Error"}
2081 support.check__all__(self, configparser, blacklist=blacklist)
2082
2083
Ezio Melottidc1fa802013-01-11 06:30:57 +02002084if __name__ == '__main__':
2085 unittest.main()