blob: d8969efc4db937d88cfb933f0be7960f047009a8 [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
David Ellis85b8d012017-03-03 17:14:27 +00005import pathlib
Georg Brandl96a60ae2010-07-28 13:13:46 +00006import textwrap
Łukasz Langa535c0772010-12-04 13:48:13 +00007import unittest
Łukasz Langa71b37a52010-12-17 21:56:32 +00008import warnings
Fred Drake8ef67672000-09-27 22:45:25 +00009
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Fred Drake3d5f7e82000-12-04 16:30:40 +000011
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080012
Raymond Hettingerf80680d2008-02-06 00:07:11 +000013class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000014
Thomas Wouters89f507f2006-12-13 04:49:30 +000015 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000016 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000017
18 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000019 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000020
Thomas Wouters89f507f2006-12-13 04:49:30 +000021 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000022 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000023
Łukasz Langae698cd52011-04-28 10:58:57 +020024 def iteritems(self):
25 return iter(self.items())
26
27 def iterkeys(self):
28 return iter(self.keys())
29
30 def itervalues(self):
31 return iter(self.values())
32
Thomas Wouters89f507f2006-12-13 04:49:30 +000033 __iter__ = iterkeys
Fred Drake3d5f7e82000-12-04 16:30:40 +000034
Fred Drake03c44a32010-02-19 06:08:41 +000035
Ezio Melottidc1fa802013-01-11 06:30:57 +020036class CfgParserTestCaseClass:
Fred Drake03c44a32010-02-19 06:08:41 +000037 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000038 delimiters = ('=', ':')
39 comment_prefixes = (';', '#')
Łukasz Langab25a7912010-12-17 01:32:29 +000040 inline_comment_prefixes = (';', '#')
Georg Brandl96a60ae2010-07-28 13:13:46 +000041 empty_lines_in_values = True
42 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000043 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000044 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000045 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000046
Fred Drakec6f28912002-10-25 19:40:49 +000047 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000048 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000049 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000050 allow_no_value=self.allow_no_value,
51 delimiters=self.delimiters,
52 comment_prefixes=self.comment_prefixes,
Łukasz Langab25a7912010-12-17 01:32:29 +000053 inline_comment_prefixes=self.inline_comment_prefixes,
Georg Brandl96a60ae2010-07-28 13:13:46 +000054 empty_lines_in_values=self.empty_lines_in_values,
55 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000056 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000057 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000058 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000059 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000060 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000061 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000062
Fred Drakec6f28912002-10-25 19:40:49 +000063 def fromstring(self, string, defaults=None):
64 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000065 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000066 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000067
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080068
Georg Brandl96a60ae2010-07-28 13:13:46 +000069class BasicTestCase(CfgParserTestCaseClass):
70
Fred Drakea4923622010-08-09 12:52:45 +000071 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000072 E = ['Commented Bar',
73 'Foo Bar',
74 'Internationalized Stuff',
75 'Long Line',
76 'Section\\with$weird%characters[\t',
77 'Spaces',
78 'Spacey Bar',
79 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000080 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000081 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000082
Fred Drake03c44a32010-02-19 06:08:41 +000083 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000084 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000085 E.sort()
Łukasz Langa71b37a52010-12-17 21:56:32 +000086 F = [('baz', 'qwe'), ('foo', 'bar3')]
Łukasz Langa26d513c2010-11-10 18:57:39 +000087
88 # API access
89 L = cf.sections()
90 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000091 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000092 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +000093 L = cf.items('Spacey Bar From The Beginning')
94 L.sort()
95 eq(L, F)
Fred Drake8ef67672000-09-27 22:45:25 +000096
Łukasz Langa26d513c2010-11-10 18:57:39 +000097 # mapping access
98 L = [section for section in cf]
99 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +0000100 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000101 E.sort()
102 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000103 L = cf['Spacey Bar From The Beginning'].items()
104 L = sorted(list(L))
105 eq(L, F)
106 L = cf.items()
107 L = sorted(list(L))
108 self.assertEqual(len(L), len(E))
109 for name, section in L:
110 eq(name, section.name)
111 eq(cf.defaults(), cf[self.default_section])
Łukasz Langa26d513c2010-11-10 18:57:39 +0000112
Fred Drakec6f28912002-10-25 19:40:49 +0000113 # The use of spaces in the section names serves as a
114 # regression test for SourceForge bug #583248:
115 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +0000116
117 # API access
118 eq(cf.get('Foo Bar', 'foo'), 'bar1')
119 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
120 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000121 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000122 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000123 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000124 eq(cf.get('Spaces', 'key with spaces'), 'value')
125 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000126 eq(cf.getint('Types', 'int'), 42)
127 eq(cf.get('Types', 'int'), "42")
128 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
129 eq(cf.get('Types', 'float'), "0.44")
130 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000131 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000132 if self.allow_no_value:
133 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000134
Łukasz Langa26d513c2010-11-10 18:57:39 +0000135 # test vars= and fallback=
136 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000137 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
138 with self.assertRaises(configparser.NoSectionError):
139 cf.get('No Such Foo Bar', 'foo')
140 with self.assertRaises(configparser.NoOptionError):
141 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000142 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
143 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
144 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
145 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
146 eq(cf.getint('Types', 'int', fallback=18), 42)
147 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
148 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000149 with self.assertRaises(configparser.NoOptionError):
150 cf.getint('Types', 'no-such-int')
Fred Drakecc645b92010-09-04 04:35:34 +0000151 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000152 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000153 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000154 fallback=0.0), 0.0)
155 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000156 with self.assertRaises(configparser.NoOptionError):
157 cf.getfloat('Types', 'no-such-float')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000158 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
159 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000160 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000161 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000162 with self.assertRaises(configparser.NoOptionError):
163 cf.getboolean('Types', 'no-such-boolean')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000164 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000165 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000166 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000167 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000168 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000169
Łukasz Langa26d513c2010-11-10 18:57:39 +0000170 # mapping access
171 eq(cf['Foo Bar']['foo'], 'bar1')
172 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000173 section = cf['Spacey Bar From The Beginning']
174 eq(section.name, 'Spacey Bar From The Beginning')
175 self.assertIs(section.parser, cf)
176 with self.assertRaises(AttributeError):
177 section.name = 'Name is read-only'
178 with self.assertRaises(AttributeError):
179 section.parser = 'Parser is read-only'
180 eq(section['foo'], 'bar3')
181 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000182 eq(cf['Commented Bar']['foo'], 'bar4')
183 eq(cf['Commented Bar']['baz'], 'qwe')
184 eq(cf['Spaces']['key with spaces'], 'value')
185 eq(cf['Spaces']['another with spaces'], 'splat!')
186 eq(cf['Long Line']['foo'],
187 'this line is much, much longer than my editor\nlikes it.')
188 if self.allow_no_value:
189 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000190 # test vars= and fallback=
191 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
192 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
193 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
194 with self.assertRaises(KeyError):
195 cf['No Such Foo Bar']['foo']
196 with self.assertRaises(KeyError):
197 cf['Foo Bar']['no-such-foo']
198 with self.assertRaises(KeyError):
199 cf['No Such Foo Bar'].get('foo', fallback='baz')
200 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
201 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
Łukasz Langa71b37a52010-12-17 21:56:32 +0000202 eq(cf['Foo Bar'].get('no-such-foo'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000203 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
204 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
205 with self.assertRaises(KeyError):
206 cf['No Such Spacey Bar'].get('foo', None)
207 eq(cf['Types'].getint('int', 18), 42)
208 eq(cf['Types'].getint('int', fallback=18), 42)
209 eq(cf['Types'].getint('no-such-int', 18), 18)
210 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
211 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
212 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000213 eq(cf['Types'].getint('no-such-int'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000214 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
215 self.assertAlmostEqual(cf['Types'].getfloat('float',
216 fallback=0.0), 0.44)
217 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
218 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
219 fallback=0.0), 0.0)
220 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
221 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000222 eq(cf['Types'].getfloat('no-such-float'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000223 eq(cf['Types'].getboolean('boolean', True), False)
224 eq(cf['Types'].getboolean('boolean', fallback=True), False)
225 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
226 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
227 "yes") # sic!
228 eq(cf['Types'].getboolean('no-such-boolean', True), True)
229 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000230 eq(cf['Types'].getboolean('no-such-boolean'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000231 if self.allow_no_value:
232 eq(cf['NoValue'].get('option-without-value', False), None)
233 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
234 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
235 eq(cf['NoValue'].get('no-such-option-without-value',
236 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000237
Łukasz Langa71b37a52010-12-17 21:56:32 +0000238 # Make sure the right things happen for remove_section() and
239 # remove_option(); added to include check for SourceForge bug #123324.
Łukasz Langa26d513c2010-11-10 18:57:39 +0000240
Łukasz Langa71b37a52010-12-17 21:56:32 +0000241 cf[self.default_section]['this_value'] = '1'
242 cf[self.default_section]['that_value'] = '2'
243
244 # API access
245 self.assertTrue(cf.remove_section('Spaces'))
246 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
247 self.assertFalse(cf.remove_section('Spaces'))
248 self.assertFalse(cf.remove_section(self.default_section))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000250 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000251 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000252 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000253 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000254 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000255 " that was removed")
Łukasz Langa71b37a52010-12-17 21:56:32 +0000256 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
257 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
258 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
259 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
260 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
Fred Drakec6f28912002-10-25 19:40:49 +0000261
Michael Foordbd6c0792010-07-25 23:09:25 +0000262 with self.assertRaises(configparser.NoSectionError) as cm:
263 cf.remove_option('No Such Section', 'foo')
264 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000265
266 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000267 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000268
Łukasz Langa26d513c2010-11-10 18:57:39 +0000269 # mapping access
Łukasz Langa71b37a52010-12-17 21:56:32 +0000270 del cf['Types']
271 self.assertFalse('Types' in cf)
272 with self.assertRaises(KeyError):
273 del cf['Types']
274 with self.assertRaises(ValueError):
275 del cf[self.default_section]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000276 del cf['Spacey Bar']['foo']
277 self.assertFalse('foo' in cf['Spacey Bar'])
278 with self.assertRaises(KeyError):
279 del cf['Spacey Bar']['foo']
Łukasz Langa71b37a52010-12-17 21:56:32 +0000280 self.assertTrue('that_value' in cf['Spacey Bar'])
281 with self.assertRaises(KeyError):
282 del cf['Spacey Bar']['that_value']
283 del cf[self.default_section]['that_value']
284 self.assertFalse('that_value' in cf['Spacey Bar'])
285 with self.assertRaises(KeyError):
286 del cf[self.default_section]['that_value']
Łukasz Langa26d513c2010-11-10 18:57:39 +0000287 with self.assertRaises(KeyError):
288 del cf['No Such Section']['foo']
289
Łukasz Langa71b37a52010-12-17 21:56:32 +0000290 # Don't add new asserts below in this method as most of the options
291 # and sections are now removed.
292
Fred Drakea4923622010-08-09 12:52:45 +0000293 def test_basic(self):
294 config_string = """\
295[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000296foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000297[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000298foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000299[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000300 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000301 baz {0[0]} qwe
302[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000303foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000304baz{0[0]}qwe {1[0]}another one
305[Long Line]
306foo{0[1]} this line is much, much longer than my editor
307 likes it.
308[Section\\with$weird%characters[\t]
309[Internationalized Stuff]
310foo[bg]{0[1]} Bulgarian
311foo{0[0]}Default
312foo[en]{0[0]}English
313foo[de]{0[0]}Deutsch
314[Spaces]
315key with spaces {0[1]} value
316another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000317[Types]
318int {0[1]} 42
319float {0[0]} 0.44
320boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000321123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000322""".format(self.delimiters, self.comment_prefixes)
323 if self.allow_no_value:
324 config_string += (
325 "[NoValue]\n"
326 "option-without-value\n"
327 )
328 cf = self.fromstring(config_string)
329 self.basic_test(cf)
330 if self.strict:
331 with self.assertRaises(configparser.DuplicateOptionError):
332 cf.read_string(textwrap.dedent("""\
333 [Duplicate Options Here]
334 option {0[0]} with a value
335 option {0[1]} with another value
336 """.format(self.delimiters)))
337 with self.assertRaises(configparser.DuplicateSectionError):
338 cf.read_string(textwrap.dedent("""\
339 [And Now For Something]
340 completely different {0[0]} True
341 [And Now For Something]
342 the larch {0[1]} 1
343 """.format(self.delimiters)))
344 else:
345 cf.read_string(textwrap.dedent("""\
346 [Duplicate Options Here]
347 option {0[0]} with a value
348 option {0[1]} with another value
349 """.format(self.delimiters)))
350
351 cf.read_string(textwrap.dedent("""\
352 [And Now For Something]
353 completely different {0[0]} True
354 [And Now For Something]
355 the larch {0[1]} 1
356 """.format(self.delimiters)))
357
358 def test_basic_from_dict(self):
359 config = {
360 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000361 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000362 },
363 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000364 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000365 },
366 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000367 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000368 "baz": "qwe",
369 },
370 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000371 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000372 "baz": "qwe",
373 },
374 "Long Line": {
375 "foo": "this line is much, much longer than my editor\nlikes "
376 "it.",
377 },
378 "Section\\with$weird%characters[\t": {
379 },
380 "Internationalized Stuff": {
381 "foo[bg]": "Bulgarian",
382 "foo": "Default",
383 "foo[en]": "English",
384 "foo[de]": "Deutsch",
385 },
386 "Spaces": {
387 "key with spaces": "value",
388 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000389 },
390 "Types": {
391 "int": 42,
392 "float": 0.44,
393 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000394 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000395 },
Fred Drakea4923622010-08-09 12:52:45 +0000396 }
397 if self.allow_no_value:
398 config.update({
399 "NoValue": {
400 "option-without-value": None,
401 }
402 })
403 cf = self.newconfig()
404 cf.read_dict(config)
405 self.basic_test(cf)
406 if self.strict:
Łukasz Langa71b37a52010-12-17 21:56:32 +0000407 with self.assertRaises(configparser.DuplicateSectionError):
408 cf.read_dict({
409 '1': {'key': 'value'},
410 1: {'key2': 'value2'},
411 })
Fred Drakea4923622010-08-09 12:52:45 +0000412 with self.assertRaises(configparser.DuplicateOptionError):
413 cf.read_dict({
414 "Duplicate Options Here": {
415 'option': 'with a value',
416 'OPTION': 'with another value',
417 },
418 })
419 else:
420 cf.read_dict({
Łukasz Langa71b37a52010-12-17 21:56:32 +0000421 'section': {'key': 'value'},
422 'SECTION': {'key2': 'value2'},
423 })
424 cf.read_dict({
Fred Drakea4923622010-08-09 12:52:45 +0000425 "Duplicate Options Here": {
426 'option': 'with a value',
427 'OPTION': 'with another value',
428 },
429 })
430
Fred Drakec6f28912002-10-25 19:40:49 +0000431 def test_case_sensitivity(self):
432 cf = self.newconfig()
433 cf.add_section("A")
434 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000435 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000436 L = cf.sections()
437 L.sort()
438 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000439 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000440 cf.set("a", "B", "value")
441 eq(cf.options("a"), ["b"])
442 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000443 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000444 with self.assertRaises(configparser.NoSectionError):
445 # section names are case-sensitive
446 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000447 self.assertTrue(cf.has_option("a", "b"))
Łukasz Langa71b37a52010-12-17 21:56:32 +0000448 self.assertFalse(cf.has_option("b", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000449 cf.set("A", "A-B", "A-B value")
450 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000451 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000452 cf.has_option("A", opt),
453 "has_option() returned false for option which should exist")
454 eq(cf.options("A"), ["a-b"])
455 eq(cf.options("a"), ["b"])
456 cf.remove_option("a", "B")
457 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000458
Fred Drakec6f28912002-10-25 19:40:49 +0000459 # SF bug #432369:
460 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000461 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000462 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000463 eq(cf.options("MySection"), ["option"])
464 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000465
Fred Drakec6f28912002-10-25 19:40:49 +0000466 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000467 cf = self.fromstring("[section]\n"
468 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000469 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000470 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000471
Fred Drake3c823aa2001-02-26 21:55:34 +0000472
Łukasz Langa26d513c2010-11-10 18:57:39 +0000473 def test_case_sensitivity_mapping_access(self):
474 cf = self.newconfig()
475 cf["A"] = {}
476 cf["a"] = {"B": "value"}
477 cf["B"] = {}
478 L = [section for section in cf]
479 L.sort()
480 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000481 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000482 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000483 eq(cf["a"].keys(), {"b"})
484 eq(cf["a"]["b"], "value",
485 "could not locate option, expecting case-insensitive option names")
486 with self.assertRaises(KeyError):
487 # section names are case-sensitive
488 cf["b"]["A"] = "value"
489 self.assertTrue("b" in cf["a"])
490 cf["A"]["A-B"] = "A-B value"
491 for opt in ("a-b", "A-b", "a-B", "A-B"):
492 self.assertTrue(
493 opt in cf["A"],
494 "has_option() returned false for option which should exist")
495 eq(cf["A"].keys(), {"a-b"})
496 eq(cf["a"].keys(), {"b"})
497 del cf["a"]["B"]
498 elem_eq(cf["a"].keys(), {})
499
500 # SF bug #432369:
501 cf = self.fromstring(
502 "[MySection]\nOption{} first line \n\tsecond line \n".format(
503 self.delimiters[0]))
504 eq(cf["MySection"].keys(), {"option"})
505 eq(cf["MySection"]["Option"], "first line\nsecond line")
506
507 # SF bug #561822:
508 cf = self.fromstring("[section]\n"
509 "nekey{}nevalue\n".format(self.delimiters[0]),
510 defaults={"key":"value"})
511 self.assertTrue("Key" in cf["section"])
512
David Goodger68a1abd2004-10-03 15:40:25 +0000513 def test_default_case_sensitivity(self):
514 cf = self.newconfig({"foo": "Bar"})
515 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000516 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000517 "could not locate option, expecting case-insensitive option names")
518 cf = self.newconfig({"Foo": "Bar"})
519 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000520 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000521 "could not locate option, expecting case-insensitive defaults")
522
Fred Drakec6f28912002-10-25 19:40:49 +0000523 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000524 cf = self.newconfig()
525 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000526 "[Foo]\n"
527 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000528 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000529 "[Foo]\n"
530 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000531 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000532 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000533 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000534 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000535 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000536 "[Foo]\n wrong-indent\n")
537 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000538 # read_file on a real file
539 tricky = support.findfile("cfgparser.3")
540 if self.delimiters[0] == '=':
541 error = configparser.ParsingError
542 expected = (tricky,)
543 else:
544 error = configparser.MissingSectionHeaderError
545 expected = (tricky, 1,
546 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000547 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000548 e = self.parse_error(cf, error, f)
549 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000550
Fred Drakea4923622010-08-09 12:52:45 +0000551 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000552 if hasattr(src, 'readline'):
553 sio = src
554 else:
555 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000556 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000557 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000558 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000559
Fred Drakec6f28912002-10-25 19:40:49 +0000560 def test_query_errors(self):
561 cf = self.newconfig()
562 self.assertEqual(cf.sections(), [],
563 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000564 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000565 "new ConfigParser should have no acknowledged "
566 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000567 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000568 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000569 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000570 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000571 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000572 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000573 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000574 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000575 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000576
Fred Drakea4923622010-08-09 12:52:45 +0000577 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000578 try:
Fred Drakea4923622010-08-09 12:52:45 +0000579 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000580 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000581 return e
582 else:
583 self.fail("expected exception type %s.%s"
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300584 % (exc.__module__, exc.__qualname__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000585
Fred Drakec6f28912002-10-25 19:40:49 +0000586 def test_boolean(self):
587 cf = self.fromstring(
588 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000589 "T1{equals}1\n"
590 "T2{equals}TRUE\n"
591 "T3{equals}True\n"
592 "T4{equals}oN\n"
593 "T5{equals}yes\n"
594 "F1{equals}0\n"
595 "F2{equals}FALSE\n"
596 "F3{equals}False\n"
597 "F4{equals}oFF\n"
598 "F5{equals}nO\n"
599 "E1{equals}2\n"
600 "E2{equals}foo\n"
601 "E3{equals}-1\n"
602 "E4{equals}0.1\n"
603 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000604 )
605 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000606 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
607 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000608 self.assertRaises(ValueError,
609 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000610
Fred Drakec6f28912002-10-25 19:40:49 +0000611 def test_weird_errors(self):
612 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000613 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000614 with self.assertRaises(configparser.DuplicateSectionError) as cm:
615 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000616 e = cm.exception
617 self.assertEqual(str(e), "Section 'Foo' already exists")
618 self.assertEqual(e.args, ("Foo", None, None))
619
620 if self.strict:
621 with self.assertRaises(configparser.DuplicateSectionError) as cm:
622 cf.read_string(textwrap.dedent("""\
623 [Foo]
624 will this be added{equals}True
625 [Bar]
626 what about this{equals}True
627 [Foo]
628 oops{equals}this won't
629 """.format(equals=self.delimiters[0])), source='<foo-bar>')
630 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200631 self.assertEqual(str(e), "While reading from '<foo-bar>' "
632 "[line 5]: section 'Foo' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000633 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
634
635 with self.assertRaises(configparser.DuplicateOptionError) as cm:
636 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
637 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200638 self.assertEqual(str(e), "While reading from '<dict>': option "
639 "'opt' in section 'Bar' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000640 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000641
642 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000643 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000644 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000645 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000646 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000647 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000648 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000649 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000650 "[Long Line - With Comments!]\n"
651 "test {0[1]} we {comment} can\n"
652 " also {comment} place\n"
653 " comments {comment} in\n"
654 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000655 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
656 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000657 )
Fred Drake03c44a32010-02-19 06:08:41 +0000658 if self.allow_no_value:
659 config_string += (
660 "[Valueless]\n"
661 "option-without-value\n"
662 )
663
664 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000665 for space_around_delimiters in (True, False):
666 output = io.StringIO()
667 cf.write(output, space_around_delimiters=space_around_delimiters)
668 delimiter = self.delimiters[0]
669 if space_around_delimiters:
670 delimiter = " {} ".format(delimiter)
671 expect_string = (
672 "[{default_section}]\n"
673 "foo{equals}another very\n"
674 "\tlong line\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000675 "\n"
Łukasz Langa71b37a52010-12-17 21:56:32 +0000676 "[Long Line]\n"
677 "foo{equals}this line is much, much longer than my editor\n"
678 "\tlikes it.\n"
679 "\n"
680 "[Long Line - With Comments!]\n"
681 "test{equals}we\n"
682 "\talso\n"
683 "\tcomments\n"
684 "\tmultiline\n"
685 "\n".format(equals=delimiter,
686 default_section=self.default_section)
Fred Drake03c44a32010-02-19 06:08:41 +0000687 )
Łukasz Langa71b37a52010-12-17 21:56:32 +0000688 if self.allow_no_value:
689 expect_string += (
690 "[Valueless]\n"
691 "option-without-value\n"
692 "\n"
693 )
694 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000695
Fred Drakeabc086f2004-05-18 03:29:52 +0000696 def test_set_string_types(self):
697 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000698 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000699 # Check that we don't get an exception when setting values in
700 # an existing section using strings:
701 class mystr(str):
702 pass
703 cf.set("sect", "option1", "splat")
704 cf.set("sect", "option1", mystr("splat"))
705 cf.set("sect", "option2", "splat")
706 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000707 cf.set("sect", "option1", "splat")
708 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000709
Fred Drake82903142004-05-18 04:24:02 +0000710 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000711 if self.delimiters[0] != '=':
Zachary Ware9fe6d862013-12-08 00:20:35 -0600712 self.skipTest('incompatible format')
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000713 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000714 # check when we pass a mix of readable and non-readable files:
715 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000716 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000717 self.assertEqual(parsed_files, [file1])
718 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
719 # check when we pass only a filename:
720 cf = self.newconfig()
721 parsed_files = cf.read(file1)
722 self.assertEqual(parsed_files, [file1])
723 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
David Ellis85b8d012017-03-03 17:14:27 +0000724 # check when we pass only a Path object:
725 cf = self.newconfig()
726 parsed_files = cf.read(pathlib.Path(file1))
727 self.assertEqual(parsed_files, [file1])
728 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
729 # check when we passed both a filename and a Path object:
730 cf = self.newconfig()
731 parsed_files = cf.read([pathlib.Path(file1), file1])
732 self.assertEqual(parsed_files, [file1, file1])
733 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
Fred Drake82903142004-05-18 04:24:02 +0000734 # check when we pass only missing files:
735 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000736 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000737 self.assertEqual(parsed_files, [])
738 # check when we pass no files:
739 cf = self.newconfig()
740 parsed_files = cf.read([])
741 self.assertEqual(parsed_files, [])
742
Fred Drakec6f28912002-10-25 19:40:49 +0000743 # shared by subclasses
744 def get_interpolation_config(self):
745 return self.fromstring(
746 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000747 "bar{equals}something %(with1)s interpolation (1 step)\n"
748 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
749 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
750 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
751 "with11{equals}%(with10)s\n"
752 "with10{equals}%(with9)s\n"
753 "with9{equals}%(with8)s\n"
754 "with8{equals}%(With7)s\n"
755 "with7{equals}%(WITH6)s\n"
756 "with6{equals}%(with5)s\n"
757 "With5{equals}%(with4)s\n"
758 "WITH4{equals}%(with3)s\n"
759 "with3{equals}%(with2)s\n"
760 "with2{equals}%(with1)s\n"
761 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000762 "\n"
763 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000764 "foo{equals}%(bar)s\n"
765 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000766 "\n"
767 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000768 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000769 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000770
Fred Drake98e3b292002-10-25 20:42:44 +0000771 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000772 cf = self.fromstring("""
773 [section]
774 name {0[0]} %(value)s
775 key{0[1]} |%(name)s|
776 getdefault{0[1]} |%(default)s|
777 """.format(self.delimiters), defaults={"default": "<default>"})
778 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000779 L.sort()
780 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000781 with self.assertRaises(configparser.NoSectionError):
782 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000783
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100784 def test_popitem(self):
785 cf = self.fromstring("""
786 [section1]
787 name1 {0[0]} value1
788 [section2]
789 name2 {0[0]} value2
790 [section3]
791 name3 {0[0]} value3
792 """.format(self.delimiters), defaults={"default": "<default>"})
793 self.assertEqual(cf.popitem()[0], 'section1')
794 self.assertEqual(cf.popitem()[0], 'section2')
795 self.assertEqual(cf.popitem()[0], 'section3')
796 with self.assertRaises(KeyError):
797 cf.popitem()
798
799 def test_clear(self):
800 cf = self.newconfig({"foo": "Bar"})
801 self.assertEqual(
802 cf.get(self.default_section, "Foo"), "Bar",
803 "could not locate option, expecting case-insensitive option names")
804 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
805 self.assertEqual(cf.sections(), ['zing'])
806 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
807 cf.clear()
808 self.assertEqual(set(cf.sections()), set())
809 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
810
Łukasz Langa02101942012-12-31 13:55:11 +0100811 def test_setitem(self):
812 cf = self.fromstring("""
813 [section1]
814 name1 {0[0]} value1
815 [section2]
816 name2 {0[0]} value2
817 [section3]
818 name3 {0[0]} value3
819 """.format(self.delimiters), defaults={"nameD": "valueD"})
820 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
821 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
822 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
823 self.assertEqual(cf['section1']['name1'], 'value1')
824 self.assertEqual(cf['section2']['name2'], 'value2')
825 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100826 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100827 cf['section2'] = {'name22': 'value22'}
828 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
829 self.assertEqual(cf['section2']['name22'], 'value22')
830 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100831 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100832 cf['section3'] = {}
833 self.assertEqual(set(cf['section3'].keys()), {'named'})
834 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100835 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100836 cf[self.default_section] = {}
837 self.assertEqual(set(cf[self.default_section].keys()), set())
838 self.assertEqual(set(cf['section1'].keys()), {'name1'})
839 self.assertEqual(set(cf['section2'].keys()), {'name22'})
840 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100841 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100842
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800843 def test_invalid_multiline_value(self):
844 if self.allow_no_value:
845 self.skipTest('if no_value is allowed, ParsingError is not raised')
846
847 invalid = textwrap.dedent("""\
848 [DEFAULT]
849 test {0} test
850 invalid""".format(self.delimiters[0])
851 )
852 cf = self.newconfig()
853 with self.assertRaises(configparser.ParsingError):
854 cf.read_string(invalid)
855 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
856 self.assertEqual(cf['DEFAULT']['test'], 'test')
857
Fred Drake8ef67672000-09-27 22:45:25 +0000858
Ezio Melottidc1fa802013-01-11 06:30:57 +0200859class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000860 config_class = configparser.RawConfigParser
861 strict = True
862
863
Ezio Melottidc1fa802013-01-11 06:30:57 +0200864class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000865 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000866
867 def test_interpolation(self):
868 cf = self.get_interpolation_config()
869 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000870 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
871 eq(cf.get("Foo", "bar9"),
872 "something with lots of interpolation (9 steps)")
873 eq(cf.get("Foo", "bar10"),
874 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000875 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000876 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200877 self.assertEqual(e.args, ("bar11", "Foo",
878 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000879 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
880 self.assertEqual(e.args, ("bar11", "Foo",
881 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000882
Fred Drake54782192002-12-31 06:57:25 +0000883 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000884 cf = self.get_interpolation_config()
885 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000886 "Interpolation Error", "name")
887 self.assertEqual(e.reference, "reference")
888 self.assertEqual(e.section, "Interpolation Error")
889 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000890 if self.interpolation == configparser._UNSET:
891 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200892 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000893 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
894 self.assertEqual(e.args, ('name', 'Interpolation Error',
895 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000896
Fred Drake98e3b292002-10-25 20:42:44 +0000897 def test_items(self):
898 self.check_items_config([('default', '<default>'),
899 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000900 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000901 ('name', 'value'),
902 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000903
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000904 def test_safe_interpolation(self):
905 # See http://www.python.org/sf/511737
906 cf = self.fromstring("[section]\n"
907 "option1{eq}xxx\n"
908 "option2{eq}%(option1)s/xxx\n"
909 "ok{eq}%(option1)s/%%s\n"
910 "not_ok{eq}%(option2)s/%%s".format(
911 eq=self.delimiters[0]))
912 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
913 if self.interpolation == configparser._UNSET:
914 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
915 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
916 with self.assertRaises(TypeError):
917 cf.get("section", "not_ok")
918
919 def test_set_malformatted_interpolation(self):
920 cf = self.fromstring("[sect]\n"
921 "option1{eq}foo\n".format(eq=self.delimiters[0]))
922
923 self.assertEqual(cf.get('sect', "option1"), "foo")
924
925 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
926 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
927 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
928
929 self.assertEqual(cf.get('sect', "option1"), "foo")
930
931 # bug #5741: double percents are *not* malformed
932 cf.set("sect", "option2", "foo%%bar")
933 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
934
David Goodger1cbf2062004-10-03 15:55:09 +0000935 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000936 cf = self.fromstring("[sect]\n"
937 "option1{eq}foo\n".format(eq=self.delimiters[0]))
938 # Check that we get a TypeError when setting non-string values
939 # in an existing section:
940 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
941 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
942 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
943 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
944 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
945 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
946 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
947 self.assertRaises(TypeError, cf.add_section, 123)
948
949 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000950 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000951 self.assertRaises(ValueError, cf.add_section, self.default_section)
952
Łukasz Langaa5fab172017-08-24 09:43:53 -0700953 def test_defaults_keyword(self):
954 """bpo-23835 fix for ConfigParser"""
955 cf = self.newconfig(defaults={1: 2.4})
956 self.assertEqual(cf[self.default_section]['1'], '2.4')
957 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
958 cf = self.newconfig(defaults={"A": 5.2})
959 self.assertEqual(cf[self.default_section]['a'], '5.2')
960 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
961
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200962
Ezio Melottidc1fa802013-01-11 06:30:57 +0200963class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200964 config_class = configparser.ConfigParser
965 interpolation = None
966 ini = textwrap.dedent("""
967 [numbers]
968 one = 1
969 two = %(one)s * 2
970 three = ${common:one} * 3
971
972 [hexen]
973 sixteen = ${numbers:two} * 8
974 """).strip()
975
976 def assertMatchesIni(self, cf):
977 self.assertEqual(cf['numbers']['one'], '1')
978 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
979 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
980 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
981
982 def test_no_interpolation(self):
983 cf = self.fromstring(self.ini)
984 self.assertMatchesIni(cf)
985
986 def test_empty_case(self):
987 cf = self.newconfig()
988 self.assertIsNone(cf.read_string(""))
989
990 def test_none_as_default_interpolation(self):
991 class CustomConfigParser(configparser.ConfigParser):
992 _DEFAULT_INTERPOLATION = None
993
994 cf = CustomConfigParser()
995 cf.read_string(self.ini)
996 self.assertMatchesIni(cf)
997
998
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000999class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
1000 config_class = configparser.ConfigParser
1001 interpolation = configparser.LegacyInterpolation()
1002
1003 def test_set_malformatted_interpolation(self):
1004 cf = self.fromstring("[sect]\n"
1005 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1006
1007 self.assertEqual(cf.get('sect', "option1"), "foo")
1008
1009 cf.set("sect", "option1", "%foo")
1010 self.assertEqual(cf.get('sect', "option1"), "%foo")
1011 cf.set("sect", "option1", "foo%")
1012 self.assertEqual(cf.get('sect', "option1"), "foo%")
1013 cf.set("sect", "option1", "f%oo")
1014 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1015
1016 # bug #5741: double percents are *not* malformed
1017 cf.set("sect", "option2", "foo%%bar")
1018 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001019
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001020
Georg Brandl96a60ae2010-07-28 13:13:46 +00001021class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1022 delimiters = (':=', '$')
1023 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001024 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001025
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001026
Łukasz Langac264c092010-11-20 16:15:37 +00001027class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1028 default_section = 'general'
1029
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001030
Ezio Melottidc1fa802013-01-11 06:30:57 +02001031class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001032 config_class = configparser.ConfigParser
1033 wonderful_spam = ("I'm having spam spam spam spam "
1034 "spam spam spam beaked beans spam "
1035 "spam spam and spam!").replace(' ', '\t\n')
1036
1037 def setUp(self):
1038 cf = self.newconfig()
1039 for i in range(100):
1040 s = 'section{}'.format(i)
1041 cf.add_section(s)
1042 for j in range(10):
1043 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1044 with open(support.TESTFN, 'w') as f:
1045 cf.write(f)
1046
1047 def tearDown(self):
1048 os.unlink(support.TESTFN)
1049
1050 def test_dominating_multiline_values(self):
1051 # We're reading from file because this is where the code changed
1052 # during performance updates in Python 3.2
1053 cf_from_file = self.newconfig()
1054 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001055 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001056 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1057 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001058
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001059
Ezio Melottidc1fa802013-01-11 06:30:57 +02001060class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001061 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001062
1063 def test_interpolation(self):
1064 cf = self.get_interpolation_config()
1065 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001066 eq(cf.get("Foo", "bar"),
1067 "something %(with1)s interpolation (1 step)")
1068 eq(cf.get("Foo", "bar9"),
1069 "something %(with9)s lots of interpolation (9 steps)")
1070 eq(cf.get("Foo", "bar10"),
1071 "something %(with10)s lots of interpolation (10 steps)")
1072 eq(cf.get("Foo", "bar11"),
1073 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001074
Fred Drake98e3b292002-10-25 20:42:44 +00001075 def test_items(self):
1076 self.check_items_config([('default', '<default>'),
1077 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001078 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001079 ('name', '%(value)s'),
1080 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001081
David Goodger1cbf2062004-10-03 15:55:09 +00001082 def test_set_nonstring_types(self):
1083 cf = self.newconfig()
1084 cf.add_section('non-string')
1085 cf.set('non-string', 'int', 1)
1086 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1087 cf.set('non-string', 'dict', {'pi': 3.14159})
1088 self.assertEqual(cf.get('non-string', 'int'), 1)
1089 self.assertEqual(cf.get('non-string', 'list'),
1090 [0, 1, 1, 2, 3, 5, 8, 13])
1091 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001092 cf.add_section(123)
1093 cf.set(123, 'this is sick', True)
1094 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001095 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001096 # would not work for SortedDict; only checking for the most common
1097 # default dictionary (OrderedDict)
1098 cf.optionxform = lambda x: x
1099 cf.set('non-string', 1, 1)
1100 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001101
Łukasz Langaa5fab172017-08-24 09:43:53 -07001102 def test_defaults_keyword(self):
1103 """bpo-23835 legacy behavior for RawConfigParser"""
1104 with self.assertRaises(AttributeError) as ctx:
1105 self.newconfig(defaults={1: 2.4})
1106 err = ctx.exception
1107 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1108 cf = self.newconfig(defaults={"A": 5.2})
1109 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1110
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001111
Georg Brandl96a60ae2010-07-28 13:13:46 +00001112class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1113 delimiters = (':=', '$')
1114 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001115 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001116
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001117
Ezio Melottidc1fa802013-01-11 06:30:57 +02001118class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001119 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001120 comment_prefixes = ('#', ';', '----')
1121 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001122 empty_lines_in_values = False
1123
1124 def test_reading(self):
1125 smbconf = support.findfile("cfgparser.2")
1126 # check when we pass a mix of readable and non-readable files:
1127 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001128 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001129 self.assertEqual(parsed_files, [smbconf])
1130 sections = ['global', 'homes', 'printers',
1131 'print$', 'pdf-generator', 'tmp', 'Agustin']
1132 self.assertEqual(cf.sections(), sections)
1133 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1134 self.assertEqual(cf.getint("global", "max log size"), 50)
1135 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1136 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001137
Ezio Melottidc1fa802013-01-11 06:30:57 +02001138class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001139 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001140 interpolation = configparser.ExtendedInterpolation()
1141 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001142 strict = True
1143
1144 def fromstring(self, string, defaults=None, optionxform=None):
1145 cf = self.newconfig(defaults)
1146 if optionxform:
1147 cf.optionxform = optionxform
1148 cf.read_string(string)
1149 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001150
1151 def test_extended_interpolation(self):
1152 cf = self.fromstring(textwrap.dedent("""
1153 [common]
1154 favourite Beatle = Paul
1155 favourite color = green
1156
1157 [tom]
1158 favourite band = ${favourite color} day
1159 favourite pope = John ${favourite Beatle} II
1160 sequel = ${favourite pope}I
1161
1162 [ambv]
1163 favourite Beatle = George
1164 son of Edward VII = ${favourite Beatle} V
1165 son of George V = ${son of Edward VII}I
1166
1167 [stanley]
1168 favourite Beatle = ${ambv:favourite Beatle}
1169 favourite pope = ${tom:favourite pope}
1170 favourite color = black
1171 favourite state of mind = paranoid
1172 favourite movie = soylent ${common:favourite color}
1173 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1174 """).strip())
1175
1176 eq = self.assertEqual
1177 eq(cf['common']['favourite Beatle'], 'Paul')
1178 eq(cf['common']['favourite color'], 'green')
1179 eq(cf['tom']['favourite Beatle'], 'Paul')
1180 eq(cf['tom']['favourite color'], 'green')
1181 eq(cf['tom']['favourite band'], 'green day')
1182 eq(cf['tom']['favourite pope'], 'John Paul II')
1183 eq(cf['tom']['sequel'], 'John Paul III')
1184 eq(cf['ambv']['favourite Beatle'], 'George')
1185 eq(cf['ambv']['favourite color'], 'green')
1186 eq(cf['ambv']['son of Edward VII'], 'George V')
1187 eq(cf['ambv']['son of George V'], 'George VI')
1188 eq(cf['stanley']['favourite Beatle'], 'George')
1189 eq(cf['stanley']['favourite color'], 'black')
1190 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1191 eq(cf['stanley']['favourite movie'], 'soylent green')
1192 eq(cf['stanley']['favourite pope'], 'John Paul II')
1193 eq(cf['stanley']['favourite song'],
1194 'black sabbath - paranoid')
1195
1196 def test_endless_loop(self):
1197 cf = self.fromstring(textwrap.dedent("""
1198 [one for you]
1199 ping = ${one for me:pong}
1200
1201 [one for me]
1202 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001203
1204 [selfish]
1205 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001206 """).strip())
1207
1208 with self.assertRaises(configparser.InterpolationDepthError):
1209 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001210 with self.assertRaises(configparser.InterpolationDepthError):
1211 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001212
Łukasz Langa71b37a52010-12-17 21:56:32 +00001213 def test_strange_options(self):
1214 cf = self.fromstring("""
1215 [dollars]
1216 $var = $$value
1217 $var2 = ${$var}
1218 ${sick} = cannot interpolate me
1219
1220 [interpolated]
1221 $other = ${dollars:$var}
1222 $trying = ${dollars:${sick}}
1223 """)
1224
1225 self.assertEqual(cf['dollars']['$var'], '$value')
1226 self.assertEqual(cf['interpolated']['$other'], '$value')
1227 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1228 exception_class = configparser.InterpolationMissingOptionError
1229 with self.assertRaises(exception_class) as cm:
1230 cf['interpolated']['$trying']
1231 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001232 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001233
Łukasz Langae698cd52011-04-28 10:58:57 +02001234 def test_case_sensitivity_basic(self):
1235 ini = textwrap.dedent("""
1236 [common]
1237 optionlower = value
1238 OptionUpper = Value
1239
1240 [Common]
1241 optionlower = a better ${common:optionlower}
1242 OptionUpper = A Better ${common:OptionUpper}
1243
1244 [random]
1245 foolower = ${common:optionlower} redefined
1246 FooUpper = ${Common:OptionUpper} Redefined
1247 """).strip()
1248
1249 cf = self.fromstring(ini)
1250 eq = self.assertEqual
1251 eq(cf['common']['optionlower'], 'value')
1252 eq(cf['common']['OptionUpper'], 'Value')
1253 eq(cf['Common']['optionlower'], 'a better value')
1254 eq(cf['Common']['OptionUpper'], 'A Better Value')
1255 eq(cf['random']['foolower'], 'value redefined')
1256 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1257
1258 def test_case_sensitivity_conflicts(self):
1259 ini = textwrap.dedent("""
1260 [common]
1261 option = value
1262 Option = Value
1263
1264 [Common]
1265 option = a better ${common:option}
1266 Option = A Better ${common:Option}
1267
1268 [random]
1269 foo = ${common:option} redefined
1270 Foo = ${Common:Option} Redefined
1271 """).strip()
1272 with self.assertRaises(configparser.DuplicateOptionError):
1273 cf = self.fromstring(ini)
1274
1275 # raw options
1276 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1277 eq = self.assertEqual
1278 eq(cf['common']['option'], 'value')
1279 eq(cf['common']['Option'], 'Value')
1280 eq(cf['Common']['option'], 'a better value')
1281 eq(cf['Common']['Option'], 'A Better Value')
1282 eq(cf['random']['foo'], 'value redefined')
1283 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001284
1285 def test_other_errors(self):
1286 cf = self.fromstring("""
1287 [interpolation fail]
1288 case1 = ${where's the brace
1289 case2 = ${does_not_exist}
1290 case3 = ${wrong_section:wrong_value}
1291 case4 = ${i:like:colon:characters}
1292 case5 = $100 for Fail No 5!
1293 """)
1294
1295 with self.assertRaises(configparser.InterpolationSyntaxError):
1296 cf['interpolation fail']['case1']
1297 with self.assertRaises(configparser.InterpolationMissingOptionError):
1298 cf['interpolation fail']['case2']
1299 with self.assertRaises(configparser.InterpolationMissingOptionError):
1300 cf['interpolation fail']['case3']
1301 with self.assertRaises(configparser.InterpolationSyntaxError):
1302 cf['interpolation fail']['case4']
1303 with self.assertRaises(configparser.InterpolationSyntaxError):
1304 cf['interpolation fail']['case5']
1305 with self.assertRaises(ValueError):
1306 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001307
1308
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001309class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001310 allow_no_value = True
1311
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001312
Ezio Melottidc1fa802013-01-11 06:30:57 +02001313class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001314 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001315 delimiters = {'='}
1316 comment_prefixes = {'#'}
1317 allow_no_value = True
1318
1319 def test_cfgparser_dot_3(self):
1320 tricky = support.findfile("cfgparser.3")
1321 cf = self.newconfig()
1322 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1323 self.assertEqual(cf.sections(), ['strange',
1324 'corruption',
1325 'yeah, sections can be '
1326 'indented as well',
1327 'another one!',
1328 'no values here',
1329 'tricky interpolation',
1330 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001331 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001332 vars={'interpolate': '-1'}), -1)
1333 with self.assertRaises(ValueError):
1334 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001335 cf.getint(self.default_section, 'go', raw=True,
1336 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001337 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1338 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1339 longname = 'yeah, sections can be indented as well'
1340 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001341 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001342 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1343 # `go` from DEFAULT
1344 with self.assertRaises(configparser.InterpolationMissingOptionError):
1345 cf.items('no values here')
1346 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1347 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1348 cf.get('tricky interpolation', 'go'))
1349 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1350
1351 def test_unicode_failure(self):
1352 tricky = support.findfile("cfgparser.3")
1353 cf = self.newconfig()
1354 with self.assertRaises(UnicodeDecodeError):
1355 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001356
Fred Drake88444412010-09-03 04:22:36 +00001357
1358class Issue7005TestCase(unittest.TestCase):
1359 """Test output when None is set() as a value and allow_no_value == False.
1360
1361 http://bugs.python.org/issue7005
1362
1363 """
1364
1365 expected_output = "[section]\noption = None\n\n"
1366
1367 def prepare(self, config_class):
1368 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001369 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001370 cp.add_section("section")
1371 cp.set("section", "option", None)
1372 sio = io.StringIO()
1373 cp.write(sio)
1374 return sio.getvalue()
1375
1376 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001377 cp = configparser.ConfigParser(allow_no_value=False)
1378 cp.add_section("section")
1379 with self.assertRaises(TypeError):
1380 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001381
1382 def test_none_as_value_stringified_raw(self):
1383 output = self.prepare(configparser.RawConfigParser)
1384 self.assertEqual(output, self.expected_output)
1385
1386
Thomas Wouters89f507f2006-12-13 04:49:30 +00001387class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001388 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001389
1390 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001391 cf = self.fromstring("[b]\n"
1392 "o4=1\n"
1393 "o3=2\n"
1394 "o2=3\n"
1395 "o1=4\n"
1396 "[a]\n"
1397 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001398 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001399 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001400 self.assertEqual(output.getvalue(),
1401 "[a]\n"
1402 "k = v\n\n"
1403 "[b]\n"
1404 "o1 = 4\n"
1405 "o2 = 3\n"
1406 "o3 = 2\n"
1407 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001408
Fred Drake03c44a32010-02-19 06:08:41 +00001409
Ezio Melottidc1fa802013-01-11 06:30:57 +02001410class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001411 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001412 comment_prefixes = '#;'
1413 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001414
1415 def test_comment_handling(self):
1416 config_string = textwrap.dedent("""\
1417 [Commented Bar]
1418 baz=qwe ; a comment
1419 foo: bar # not a comment!
1420 # but this is a comment
1421 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001422 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001423 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001424 """)
1425 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001426 self.assertEqual(cf.get('Commented Bar', 'foo'),
1427 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001428 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001429 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1430 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001431
Ezio Melottidc1fa802013-01-11 06:30:57 +02001432class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001433 config_class = configparser.ConfigParser
1434
1435 def fromstring(self, string, defaults=None):
1436 cf = self.newconfig(defaults)
1437 cf.read_string(string)
1438 cf_copy = self.newconfig()
1439 cf_copy.read_dict(cf)
1440 # we have to clean up option duplicates that appeared because of
1441 # the magic DEFAULTSECT behaviour.
1442 for section in cf_copy.values():
1443 if section.name == self.default_section:
1444 continue
1445 for default, value in cf[self.default_section].items():
1446 if section[default] == value:
1447 del section[default]
1448 return cf_copy
1449
Łukasz Langadaab1c82011-04-27 18:10:05 +02001450
1451class FakeFile:
1452 def __init__(self):
1453 file_path = support.findfile("cfgparser.1")
1454 with open(file_path) as f:
1455 self.lines = f.readlines()
1456 self.lines.reverse()
1457
1458 def readline(self):
1459 if len(self.lines):
1460 return self.lines.pop()
1461 return ''
1462
1463
1464def readline_generator(f):
1465 """As advised in Doc/library/configparser.rst."""
1466 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001467 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001468 yield line
1469 line = f.readline()
1470
1471
1472class ReadFileTestCase(unittest.TestCase):
1473 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001474 file_paths = [support.findfile("cfgparser.1")]
1475 try:
1476 file_paths.append(file_paths[0].encode('utf8'))
1477 except UnicodeEncodeError:
1478 pass # unfortunately we can't test bytes on this path
1479 for file_path in file_paths:
1480 parser = configparser.ConfigParser()
1481 with open(file_path) as f:
1482 parser.read_file(f)
1483 self.assertIn("Foo Bar", parser)
1484 self.assertIn("foo", parser["Foo Bar"])
1485 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001486
1487 def test_iterable(self):
1488 lines = textwrap.dedent("""
1489 [Foo Bar]
1490 foo=newbar""").strip().split('\n')
1491 parser = configparser.ConfigParser()
1492 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001493 self.assertIn("Foo Bar", parser)
1494 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001495 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1496
1497 def test_readline_generator(self):
1498 """Issue #11670."""
1499 parser = configparser.ConfigParser()
1500 with self.assertRaises(TypeError):
1501 parser.read_file(FakeFile())
1502 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001503 self.assertIn("Foo Bar", parser)
1504 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001505 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1506
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001507 def test_source_as_bytes(self):
1508 """Issue #18260."""
1509 lines = textwrap.dedent("""
1510 [badbad]
1511 [badbad]""").strip().split('\n')
1512 parser = configparser.ConfigParser()
1513 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1514 parser.read_file(lines, source=b"badbad")
1515 self.assertEqual(
1516 str(dse.exception),
1517 "While reading from b'badbad' [line 2]: section 'badbad' "
1518 "already exists"
1519 )
1520 lines = textwrap.dedent("""
1521 [badbad]
1522 bad = bad
1523 bad = bad""").strip().split('\n')
1524 parser = configparser.ConfigParser()
1525 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1526 parser.read_file(lines, source=b"badbad")
1527 self.assertEqual(
1528 str(dse.exception),
1529 "While reading from b'badbad' [line 3]: option 'bad' in section "
1530 "'badbad' already exists"
1531 )
1532 lines = textwrap.dedent("""
1533 [badbad]
1534 = bad""").strip().split('\n')
1535 parser = configparser.ConfigParser()
1536 with self.assertRaises(configparser.ParsingError) as dse:
1537 parser.read_file(lines, source=b"badbad")
1538 self.assertEqual(
1539 str(dse.exception),
1540 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1541 )
1542 lines = textwrap.dedent("""
1543 [badbad
1544 bad = bad""").strip().split('\n')
1545 parser = configparser.ConfigParser()
1546 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1547 parser.read_file(lines, source=b"badbad")
1548 self.assertEqual(
1549 str(dse.exception),
1550 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1551 "'[badbad'"
1552 )
1553
Łukasz Langadaab1c82011-04-27 18:10:05 +02001554
Łukasz Langa71b37a52010-12-17 21:56:32 +00001555class CoverageOneHundredTestCase(unittest.TestCase):
1556 """Covers edge cases in the codebase."""
1557
1558 def test_duplicate_option_error(self):
1559 error = configparser.DuplicateOptionError('section', 'option')
1560 self.assertEqual(error.section, 'section')
1561 self.assertEqual(error.option, 'option')
1562 self.assertEqual(error.source, None)
1563 self.assertEqual(error.lineno, None)
1564 self.assertEqual(error.args, ('section', 'option', None, None))
1565 self.assertEqual(str(error), "Option 'option' in section 'section' "
1566 "already exists")
1567
1568 def test_interpolation_depth_error(self):
1569 error = configparser.InterpolationDepthError('option', 'section',
1570 'rawval')
1571 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1572 self.assertEqual(error.option, 'option')
1573 self.assertEqual(error.section, 'section')
1574
1575 def test_parsing_error(self):
1576 with self.assertRaises(ValueError) as cm:
1577 configparser.ParsingError()
1578 self.assertEqual(str(cm.exception), "Required argument `source' not "
1579 "given.")
1580 with self.assertRaises(ValueError) as cm:
1581 configparser.ParsingError(source='source', filename='filename')
1582 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1583 "and `source'. Use `source'.")
1584 error = configparser.ParsingError(filename='source')
1585 self.assertEqual(error.source, 'source')
1586 with warnings.catch_warnings(record=True) as w:
1587 warnings.simplefilter("always", DeprecationWarning)
1588 self.assertEqual(error.filename, 'source')
1589 error.filename = 'filename'
1590 self.assertEqual(error.source, 'filename')
1591 for warning in w:
1592 self.assertTrue(warning.category is DeprecationWarning)
1593
1594 def test_interpolation_validation(self):
1595 parser = configparser.ConfigParser()
1596 parser.read_string("""
1597 [section]
1598 invalid_percent = %
1599 invalid_reference = %(()
1600 invalid_variable = %(does_not_exist)s
1601 """)
1602 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1603 parser['section']['invalid_percent']
1604 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1605 "'(', found: '%'")
1606 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1607 parser['section']['invalid_reference']
1608 self.assertEqual(str(cm.exception), "bad interpolation variable "
1609 "reference '%(()'")
1610
1611 def test_readfp_deprecation(self):
1612 sio = io.StringIO("""
1613 [section]
1614 option = value
1615 """)
1616 parser = configparser.ConfigParser()
1617 with warnings.catch_warnings(record=True) as w:
1618 warnings.simplefilter("always", DeprecationWarning)
1619 parser.readfp(sio, filename='StringIO')
1620 for warning in w:
1621 self.assertTrue(warning.category is DeprecationWarning)
1622 self.assertEqual(len(parser), 2)
1623 self.assertEqual(parser['section']['option'], 'value')
1624
1625 def test_safeconfigparser_deprecation(self):
1626 with warnings.catch_warnings(record=True) as w:
1627 warnings.simplefilter("always", DeprecationWarning)
1628 parser = configparser.SafeConfigParser()
1629 for warning in w:
1630 self.assertTrue(warning.category is DeprecationWarning)
1631
1632 def test_sectionproxy_repr(self):
1633 parser = configparser.ConfigParser()
1634 parser.read_string("""
1635 [section]
1636 key = value
1637 """)
1638 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001639
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001640 def test_inconsistent_converters_state(self):
1641 parser = configparser.ConfigParser()
1642 import decimal
1643 parser.converters['decimal'] = decimal.Decimal
1644 parser.read_string("""
1645 [s1]
1646 one = 1
1647 [s2]
1648 two = 2
1649 """)
1650 self.assertIn('decimal', parser.converters)
1651 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1652 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1653 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1654 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1655 del parser.getdecimal
1656 with self.assertRaises(AttributeError):
1657 parser.getdecimal('s1', 'one')
1658 self.assertIn('decimal', parser.converters)
1659 del parser.converters['decimal']
1660 self.assertNotIn('decimal', parser.converters)
1661 with self.assertRaises(AttributeError):
1662 parser.getdecimal('s1', 'one')
1663 with self.assertRaises(AttributeError):
1664 parser['s1'].getdecimal('one')
1665 with self.assertRaises(AttributeError):
1666 parser['s2'].getdecimal('two')
1667
Łukasz Langae7851952012-01-20 14:57:55 +01001668
1669class ExceptionPicklingTestCase(unittest.TestCase):
1670 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1671
1672 def test_error(self):
1673 import pickle
1674 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001675 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1676 pickled = pickle.dumps(e1, proto)
1677 e2 = pickle.loads(pickled)
1678 self.assertEqual(e1.message, e2.message)
1679 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001680
1681 def test_nosectionerror(self):
1682 import pickle
1683 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001684 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1685 pickled = pickle.dumps(e1, proto)
1686 e2 = pickle.loads(pickled)
1687 self.assertEqual(e1.message, e2.message)
1688 self.assertEqual(e1.args, e2.args)
1689 self.assertEqual(e1.section, e2.section)
1690 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001691
1692 def test_nooptionerror(self):
1693 import pickle
1694 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001695 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1696 pickled = pickle.dumps(e1, proto)
1697 e2 = pickle.loads(pickled)
1698 self.assertEqual(e1.message, e2.message)
1699 self.assertEqual(e1.args, e2.args)
1700 self.assertEqual(e1.section, e2.section)
1701 self.assertEqual(e1.option, e2.option)
1702 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001703
1704 def test_duplicatesectionerror(self):
1705 import pickle
1706 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001707 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1708 pickled = pickle.dumps(e1, proto)
1709 e2 = pickle.loads(pickled)
1710 self.assertEqual(e1.message, e2.message)
1711 self.assertEqual(e1.args, e2.args)
1712 self.assertEqual(e1.section, e2.section)
1713 self.assertEqual(e1.source, e2.source)
1714 self.assertEqual(e1.lineno, e2.lineno)
1715 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001716
1717 def test_duplicateoptionerror(self):
1718 import pickle
1719 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1720 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001721 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1722 pickled = pickle.dumps(e1, proto)
1723 e2 = pickle.loads(pickled)
1724 self.assertEqual(e1.message, e2.message)
1725 self.assertEqual(e1.args, e2.args)
1726 self.assertEqual(e1.section, e2.section)
1727 self.assertEqual(e1.option, e2.option)
1728 self.assertEqual(e1.source, e2.source)
1729 self.assertEqual(e1.lineno, e2.lineno)
1730 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001731
1732 def test_interpolationerror(self):
1733 import pickle
1734 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001735 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1736 pickled = pickle.dumps(e1, proto)
1737 e2 = pickle.loads(pickled)
1738 self.assertEqual(e1.message, e2.message)
1739 self.assertEqual(e1.args, e2.args)
1740 self.assertEqual(e1.section, e2.section)
1741 self.assertEqual(e1.option, e2.option)
1742 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001743
1744 def test_interpolationmissingoptionerror(self):
1745 import pickle
1746 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1747 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001748 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1749 pickled = pickle.dumps(e1, proto)
1750 e2 = pickle.loads(pickled)
1751 self.assertEqual(e1.message, e2.message)
1752 self.assertEqual(e1.args, e2.args)
1753 self.assertEqual(e1.section, e2.section)
1754 self.assertEqual(e1.option, e2.option)
1755 self.assertEqual(e1.reference, e2.reference)
1756 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001757
1758 def test_interpolationsyntaxerror(self):
1759 import pickle
1760 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001761 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1762 pickled = pickle.dumps(e1, proto)
1763 e2 = pickle.loads(pickled)
1764 self.assertEqual(e1.message, e2.message)
1765 self.assertEqual(e1.args, e2.args)
1766 self.assertEqual(e1.section, e2.section)
1767 self.assertEqual(e1.option, e2.option)
1768 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001769
1770 def test_interpolationdeptherror(self):
1771 import pickle
1772 e1 = configparser.InterpolationDepthError('option', 'section',
1773 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001774 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1775 pickled = pickle.dumps(e1, proto)
1776 e2 = pickle.loads(pickled)
1777 self.assertEqual(e1.message, e2.message)
1778 self.assertEqual(e1.args, e2.args)
1779 self.assertEqual(e1.section, e2.section)
1780 self.assertEqual(e1.option, e2.option)
1781 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001782
1783 def test_parsingerror(self):
1784 import pickle
1785 e1 = configparser.ParsingError('source')
1786 e1.append(1, 'line1')
1787 e1.append(2, 'line2')
1788 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001789 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1790 pickled = pickle.dumps(e1, proto)
1791 e2 = pickle.loads(pickled)
1792 self.assertEqual(e1.message, e2.message)
1793 self.assertEqual(e1.args, e2.args)
1794 self.assertEqual(e1.source, e2.source)
1795 self.assertEqual(e1.errors, e2.errors)
1796 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001797 e1 = configparser.ParsingError(filename='filename')
1798 e1.append(1, 'line1')
1799 e1.append(2, 'line2')
1800 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001801 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1802 pickled = pickle.dumps(e1, proto)
1803 e2 = pickle.loads(pickled)
1804 self.assertEqual(e1.message, e2.message)
1805 self.assertEqual(e1.args, e2.args)
1806 self.assertEqual(e1.source, e2.source)
1807 self.assertEqual(e1.errors, e2.errors)
1808 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001809
1810 def test_missingsectionheadererror(self):
1811 import pickle
1812 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001813 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1814 pickled = pickle.dumps(e1, proto)
1815 e2 = pickle.loads(pickled)
1816 self.assertEqual(e1.message, e2.message)
1817 self.assertEqual(e1.args, e2.args)
1818 self.assertEqual(e1.line, e2.line)
1819 self.assertEqual(e1.source, e2.source)
1820 self.assertEqual(e1.lineno, e2.lineno)
1821 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001822
1823
Łukasz Langacba24322012-07-07 18:54:08 +02001824class InlineCommentStrippingTestCase(unittest.TestCase):
1825 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1826 delimiter occurs earlier without preceding space.."""
1827
1828 def test_stripping(self):
1829 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1830 '//'))
1831 cfg.read_string("""
1832 [section]
1833 k1 = v1;still v1
1834 k2 = v2 ;a comment
1835 k3 = v3 ; also a comment
1836 k4 = v4;still v4 ;a comment
1837 k5 = v5;still v5 ; also a comment
1838 k6 = v6;still v6; and still v6 ;a comment
1839 k7 = v7;still v7; and still v7 ; also a comment
1840
1841 [multiprefix]
1842 k1 = v1;still v1 #a comment ; yeah, pretty much
1843 k2 = v2 // this already is a comment ; continued
1844 k3 = v3;#//still v3# and still v3 ; a comment
1845 """)
1846 s = cfg['section']
1847 self.assertEqual(s['k1'], 'v1;still v1')
1848 self.assertEqual(s['k2'], 'v2')
1849 self.assertEqual(s['k3'], 'v3')
1850 self.assertEqual(s['k4'], 'v4;still v4')
1851 self.assertEqual(s['k5'], 'v5;still v5')
1852 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1853 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1854 s = cfg['multiprefix']
1855 self.assertEqual(s['k1'], 'v1;still v1')
1856 self.assertEqual(s['k2'], 'v2')
1857 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1858
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001859
Łukasz Langa949053b2014-09-04 01:36:33 -07001860class ExceptionContextTestCase(unittest.TestCase):
1861 """ Test that implementation details doesn't leak
1862 through raising exceptions. """
1863
1864 def test_get_basic_interpolation(self):
1865 parser = configparser.ConfigParser()
1866 parser.read_string("""
1867 [Paths]
1868 home_dir: /Users
1869 my_dir: %(home_dir1)s/lumberjack
1870 my_pictures: %(my_dir)s/Pictures
1871 """)
1872 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1873 with cm:
1874 parser.get('Paths', 'my_dir')
1875 self.assertIs(cm.exception.__suppress_context__, True)
1876
1877 def test_get_extended_interpolation(self):
1878 parser = configparser.ConfigParser(
1879 interpolation=configparser.ExtendedInterpolation())
1880 parser.read_string("""
1881 [Paths]
1882 home_dir: /Users
1883 my_dir: ${home_dir1}/lumberjack
1884 my_pictures: ${my_dir}/Pictures
1885 """)
1886 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1887 with cm:
1888 parser.get('Paths', 'my_dir')
1889 self.assertIs(cm.exception.__suppress_context__, True)
1890
1891 def test_missing_options(self):
1892 parser = configparser.ConfigParser()
1893 parser.read_string("""
1894 [Paths]
1895 home_dir: /Users
1896 """)
1897 with self.assertRaises(configparser.NoSectionError) as cm:
1898 parser.options('test')
1899 self.assertIs(cm.exception.__suppress_context__, True)
1900
1901 def test_missing_section(self):
1902 config = configparser.ConfigParser()
1903 with self.assertRaises(configparser.NoSectionError) as cm:
1904 config.set('Section1', 'an_int', '15')
1905 self.assertIs(cm.exception.__suppress_context__, True)
1906
1907 def test_remove_option(self):
1908 config = configparser.ConfigParser()
1909 with self.assertRaises(configparser.NoSectionError) as cm:
1910 config.remove_option('Section1', 'an_int')
1911 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001912
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001913
1914class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1915 """Introduced in 3.5, issue #18159."""
1916
1917 config_class = configparser.ConfigParser
1918
1919 def newconfig(self, defaults=None):
1920 instance = super().newconfig(defaults=defaults)
1921 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1922 if e.strip()]
1923 return instance
1924
1925 def test_converters(self):
1926 cfg = self.newconfig()
1927 self.assertIn('boolean', cfg.converters)
1928 self.assertIn('list', cfg.converters)
1929 self.assertIsNone(cfg.converters['int'])
1930 self.assertIsNone(cfg.converters['float'])
1931 self.assertIsNone(cfg.converters['boolean'])
1932 self.assertIsNotNone(cfg.converters['list'])
1933 self.assertEqual(len(cfg.converters), 4)
1934 with self.assertRaises(ValueError):
1935 cfg.converters[''] = lambda v: v
1936 with self.assertRaises(ValueError):
1937 cfg.converters[None] = lambda v: v
1938 cfg.read_string("""
1939 [s]
1940 str = string
1941 int = 1
1942 float = 0.5
1943 list = a b c d e f g
1944 bool = yes
1945 """)
1946 s = cfg['s']
1947 self.assertEqual(s['str'], 'string')
1948 self.assertEqual(s['int'], '1')
1949 self.assertEqual(s['float'], '0.5')
1950 self.assertEqual(s['list'], 'a b c d e f g')
1951 self.assertEqual(s['bool'], 'yes')
1952 self.assertEqual(cfg.get('s', 'str'), 'string')
1953 self.assertEqual(cfg.get('s', 'int'), '1')
1954 self.assertEqual(cfg.get('s', 'float'), '0.5')
1955 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1956 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1957 self.assertEqual(cfg.get('s', 'str'), 'string')
1958 self.assertEqual(cfg.getint('s', 'int'), 1)
1959 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1960 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1961 'e', 'f', 'g'])
1962 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1963 self.assertEqual(s.get('str'), 'string')
1964 self.assertEqual(s.getint('int'), 1)
1965 self.assertEqual(s.getfloat('float'), 0.5)
1966 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1967 'e', 'f', 'g'])
1968 self.assertEqual(s.getboolean('bool'), True)
1969 with self.assertRaises(AttributeError):
1970 cfg.getdecimal('s', 'float')
1971 with self.assertRaises(AttributeError):
1972 s.getdecimal('float')
1973 import decimal
1974 cfg.converters['decimal'] = decimal.Decimal
1975 self.assertIn('decimal', cfg.converters)
1976 self.assertIsNotNone(cfg.converters['decimal'])
1977 self.assertEqual(len(cfg.converters), 5)
1978 dec0_5 = decimal.Decimal('0.5')
1979 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1980 self.assertEqual(s.getdecimal('float'), dec0_5)
1981 del cfg.converters['decimal']
1982 self.assertNotIn('decimal', cfg.converters)
1983 self.assertEqual(len(cfg.converters), 4)
1984 with self.assertRaises(AttributeError):
1985 cfg.getdecimal('s', 'float')
1986 with self.assertRaises(AttributeError):
1987 s.getdecimal('float')
1988 with self.assertRaises(KeyError):
1989 del cfg.converters['decimal']
1990 with self.assertRaises(KeyError):
1991 del cfg.converters['']
1992 with self.assertRaises(KeyError):
1993 del cfg.converters[None]
1994
1995
1996class BlatantOverrideConvertersTestCase(unittest.TestCase):
1997 """What if somebody overrode a getboolean()? We want to make sure that in
1998 this case the automatic converters do not kick in."""
1999
2000 config = """
2001 [one]
2002 one = false
2003 two = false
2004 three = long story short
2005
2006 [two]
2007 one = false
2008 two = false
2009 three = four
2010 """
2011
2012 def test_converters_at_init(self):
2013 cfg = configparser.ConfigParser(converters={'len': len})
2014 cfg.read_string(self.config)
2015 self._test_len(cfg)
2016 self.assertIsNotNone(cfg.converters['len'])
2017
2018 def test_inheritance(self):
2019 class StrangeConfigParser(configparser.ConfigParser):
2020 gettysburg = 'a historic borough in south central Pennsylvania'
2021
2022 def getboolean(self, section, option, *, raw=False, vars=None,
2023 fallback=configparser._UNSET):
2024 if section == option:
2025 return True
2026 return super().getboolean(section, option, raw=raw, vars=vars,
2027 fallback=fallback)
2028 def getlen(self, section, option, *, raw=False, vars=None,
2029 fallback=configparser._UNSET):
2030 return self._get_conv(section, option, len, raw=raw, vars=vars,
2031 fallback=fallback)
2032
2033 cfg = StrangeConfigParser()
2034 cfg.read_string(self.config)
2035 self._test_len(cfg)
2036 self.assertIsNone(cfg.converters['len'])
2037 self.assertTrue(cfg.getboolean('one', 'one'))
2038 self.assertTrue(cfg.getboolean('two', 'two'))
2039 self.assertFalse(cfg.getboolean('one', 'two'))
2040 self.assertFalse(cfg.getboolean('two', 'one'))
2041 cfg.converters['boolean'] = cfg._convert_to_boolean
2042 self.assertFalse(cfg.getboolean('one', 'one'))
2043 self.assertFalse(cfg.getboolean('two', 'two'))
2044 self.assertFalse(cfg.getboolean('one', 'two'))
2045 self.assertFalse(cfg.getboolean('two', 'one'))
2046
2047 def _test_len(self, cfg):
2048 self.assertEqual(len(cfg.converters), 4)
2049 self.assertIn('boolean', cfg.converters)
2050 self.assertIn('len', cfg.converters)
2051 self.assertNotIn('tysburg', cfg.converters)
2052 self.assertIsNone(cfg.converters['int'])
2053 self.assertIsNone(cfg.converters['float'])
2054 self.assertIsNone(cfg.converters['boolean'])
2055 self.assertEqual(cfg.getlen('one', 'one'), 5)
2056 self.assertEqual(cfg.getlen('one', 'two'), 5)
2057 self.assertEqual(cfg.getlen('one', 'three'), 16)
2058 self.assertEqual(cfg.getlen('two', 'one'), 5)
2059 self.assertEqual(cfg.getlen('two', 'two'), 5)
2060 self.assertEqual(cfg.getlen('two', 'three'), 4)
2061 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2062 with self.assertRaises(configparser.NoOptionError):
2063 cfg.getlen('two', 'four')
2064 self.assertEqual(cfg['one'].getlen('one'), 5)
2065 self.assertEqual(cfg['one'].getlen('two'), 5)
2066 self.assertEqual(cfg['one'].getlen('three'), 16)
2067 self.assertEqual(cfg['two'].getlen('one'), 5)
2068 self.assertEqual(cfg['two'].getlen('two'), 5)
2069 self.assertEqual(cfg['two'].getlen('three'), 4)
2070 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2071 self.assertEqual(cfg['two'].getlen('four'), None)
2072
2073 def test_instance_assignment(self):
2074 cfg = configparser.ConfigParser()
2075 cfg.getboolean = lambda section, option: True
2076 cfg.getlen = lambda section, option: len(cfg[section][option])
2077 cfg.read_string(self.config)
2078 self.assertEqual(len(cfg.converters), 3)
2079 self.assertIn('boolean', cfg.converters)
2080 self.assertNotIn('len', cfg.converters)
2081 self.assertIsNone(cfg.converters['int'])
2082 self.assertIsNone(cfg.converters['float'])
2083 self.assertIsNone(cfg.converters['boolean'])
2084 self.assertTrue(cfg.getboolean('one', 'one'))
2085 self.assertTrue(cfg.getboolean('two', 'two'))
2086 self.assertTrue(cfg.getboolean('one', 'two'))
2087 self.assertTrue(cfg.getboolean('two', 'one'))
2088 cfg.converters['boolean'] = cfg._convert_to_boolean
2089 self.assertFalse(cfg.getboolean('one', 'one'))
2090 self.assertFalse(cfg.getboolean('two', 'two'))
2091 self.assertFalse(cfg.getboolean('one', 'two'))
2092 self.assertFalse(cfg.getboolean('two', 'one'))
2093 self.assertEqual(cfg.getlen('one', 'one'), 5)
2094 self.assertEqual(cfg.getlen('one', 'two'), 5)
2095 self.assertEqual(cfg.getlen('one', 'three'), 16)
2096 self.assertEqual(cfg.getlen('two', 'one'), 5)
2097 self.assertEqual(cfg.getlen('two', 'two'), 5)
2098 self.assertEqual(cfg.getlen('two', 'three'), 4)
2099 # If a getter impl is assigned straight to the instance, it won't
2100 # be available on the section proxies.
2101 with self.assertRaises(AttributeError):
2102 self.assertEqual(cfg['one'].getlen('one'), 5)
2103 with self.assertRaises(AttributeError):
2104 self.assertEqual(cfg['two'].getlen('one'), 5)
2105
2106
Martin Panter2b9b70b2016-09-09 06:46:48 +00002107class MiscTestCase(unittest.TestCase):
2108 def test__all__(self):
2109 blacklist = {"Error"}
2110 support.check__all__(self, configparser, blacklist=blacklist)
2111
2112
Ezio Melottidc1fa802013-01-11 06:30:57 +02002113if __name__ == '__main__':
2114 unittest.main()