blob: 87b811f09b6ead75208c99691a632742325e9a23 [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
Vincent Michele3148532017-11-02 13:47:04 +0100743 def test_read_returns_file_list_with_bytestring_path(self):
744 if self.delimiters[0] != '=':
745 self.skipTest('incompatible format')
746 file1_bytestring = support.findfile("cfgparser.1").encode()
747 # check when passing an existing bytestring path
748 cf = self.newconfig()
749 parsed_files = cf.read(file1_bytestring)
750 self.assertEqual(parsed_files, [file1_bytestring])
751 # check when passing an non-existing bytestring path
752 cf = self.newconfig()
753 parsed_files = cf.read(b'nonexistent-file')
754 self.assertEqual(parsed_files, [])
755 # check when passing both an existing and non-existing bytestring path
756 cf = self.newconfig()
757 parsed_files = cf.read([file1_bytestring, b'nonexistent-file'])
758 self.assertEqual(parsed_files, [file1_bytestring])
759
Fred Drakec6f28912002-10-25 19:40:49 +0000760 # shared by subclasses
761 def get_interpolation_config(self):
762 return self.fromstring(
763 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000764 "bar{equals}something %(with1)s interpolation (1 step)\n"
765 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
766 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
767 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
768 "with11{equals}%(with10)s\n"
769 "with10{equals}%(with9)s\n"
770 "with9{equals}%(with8)s\n"
771 "with8{equals}%(With7)s\n"
772 "with7{equals}%(WITH6)s\n"
773 "with6{equals}%(with5)s\n"
774 "With5{equals}%(with4)s\n"
775 "WITH4{equals}%(with3)s\n"
776 "with3{equals}%(with2)s\n"
777 "with2{equals}%(with1)s\n"
778 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000779 "\n"
780 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000781 "foo{equals}%(bar)s\n"
782 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000783 "\n"
784 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000785 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000786 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000787
Fred Drake98e3b292002-10-25 20:42:44 +0000788 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000789 cf = self.fromstring("""
790 [section]
791 name {0[0]} %(value)s
792 key{0[1]} |%(name)s|
793 getdefault{0[1]} |%(default)s|
794 """.format(self.delimiters), defaults={"default": "<default>"})
795 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000796 L.sort()
797 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000798 with self.assertRaises(configparser.NoSectionError):
799 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000800
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100801 def test_popitem(self):
802 cf = self.fromstring("""
803 [section1]
804 name1 {0[0]} value1
805 [section2]
806 name2 {0[0]} value2
807 [section3]
808 name3 {0[0]} value3
809 """.format(self.delimiters), defaults={"default": "<default>"})
810 self.assertEqual(cf.popitem()[0], 'section1')
811 self.assertEqual(cf.popitem()[0], 'section2')
812 self.assertEqual(cf.popitem()[0], 'section3')
813 with self.assertRaises(KeyError):
814 cf.popitem()
815
816 def test_clear(self):
817 cf = self.newconfig({"foo": "Bar"})
818 self.assertEqual(
819 cf.get(self.default_section, "Foo"), "Bar",
820 "could not locate option, expecting case-insensitive option names")
821 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
822 self.assertEqual(cf.sections(), ['zing'])
823 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
824 cf.clear()
825 self.assertEqual(set(cf.sections()), set())
826 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
827
Łukasz Langa02101942012-12-31 13:55:11 +0100828 def test_setitem(self):
829 cf = self.fromstring("""
830 [section1]
831 name1 {0[0]} value1
832 [section2]
833 name2 {0[0]} value2
834 [section3]
835 name3 {0[0]} value3
836 """.format(self.delimiters), defaults={"nameD": "valueD"})
837 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
838 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
839 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
840 self.assertEqual(cf['section1']['name1'], 'value1')
841 self.assertEqual(cf['section2']['name2'], 'value2')
842 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100843 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100844 cf['section2'] = {'name22': 'value22'}
845 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
846 self.assertEqual(cf['section2']['name22'], 'value22')
847 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100848 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100849 cf['section3'] = {}
850 self.assertEqual(set(cf['section3'].keys()), {'named'})
851 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100852 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100853 cf[self.default_section] = {}
854 self.assertEqual(set(cf[self.default_section].keys()), set())
855 self.assertEqual(set(cf['section1'].keys()), {'name1'})
856 self.assertEqual(set(cf['section2'].keys()), {'name22'})
857 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100858 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100859
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800860 def test_invalid_multiline_value(self):
861 if self.allow_no_value:
862 self.skipTest('if no_value is allowed, ParsingError is not raised')
863
864 invalid = textwrap.dedent("""\
865 [DEFAULT]
866 test {0} test
867 invalid""".format(self.delimiters[0])
868 )
869 cf = self.newconfig()
870 with self.assertRaises(configparser.ParsingError):
871 cf.read_string(invalid)
872 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
873 self.assertEqual(cf['DEFAULT']['test'], 'test')
874
Fred Drake8ef67672000-09-27 22:45:25 +0000875
Ezio Melottidc1fa802013-01-11 06:30:57 +0200876class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000877 config_class = configparser.RawConfigParser
878 strict = True
879
880
Ezio Melottidc1fa802013-01-11 06:30:57 +0200881class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000882 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000883
884 def test_interpolation(self):
885 cf = self.get_interpolation_config()
886 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000887 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
888 eq(cf.get("Foo", "bar9"),
889 "something with lots of interpolation (9 steps)")
890 eq(cf.get("Foo", "bar10"),
891 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000892 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000893 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200894 self.assertEqual(e.args, ("bar11", "Foo",
895 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000896 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
897 self.assertEqual(e.args, ("bar11", "Foo",
898 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000899
Fred Drake54782192002-12-31 06:57:25 +0000900 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000901 cf = self.get_interpolation_config()
902 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000903 "Interpolation Error", "name")
904 self.assertEqual(e.reference, "reference")
905 self.assertEqual(e.section, "Interpolation Error")
906 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000907 if self.interpolation == configparser._UNSET:
908 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200909 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000910 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
911 self.assertEqual(e.args, ('name', 'Interpolation Error',
912 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000913
Fred Drake98e3b292002-10-25 20:42:44 +0000914 def test_items(self):
915 self.check_items_config([('default', '<default>'),
916 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000917 ('key', '|value|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +0100918 ('name', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000919
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000920 def test_safe_interpolation(self):
921 # See http://www.python.org/sf/511737
922 cf = self.fromstring("[section]\n"
923 "option1{eq}xxx\n"
924 "option2{eq}%(option1)s/xxx\n"
925 "ok{eq}%(option1)s/%%s\n"
926 "not_ok{eq}%(option2)s/%%s".format(
927 eq=self.delimiters[0]))
928 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
929 if self.interpolation == configparser._UNSET:
930 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
931 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
932 with self.assertRaises(TypeError):
933 cf.get("section", "not_ok")
934
935 def test_set_malformatted_interpolation(self):
936 cf = self.fromstring("[sect]\n"
937 "option1{eq}foo\n".format(eq=self.delimiters[0]))
938
939 self.assertEqual(cf.get('sect', "option1"), "foo")
940
941 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
942 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
943 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
944
945 self.assertEqual(cf.get('sect', "option1"), "foo")
946
947 # bug #5741: double percents are *not* malformed
948 cf.set("sect", "option2", "foo%%bar")
949 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
950
David Goodger1cbf2062004-10-03 15:55:09 +0000951 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000952 cf = self.fromstring("[sect]\n"
953 "option1{eq}foo\n".format(eq=self.delimiters[0]))
954 # Check that we get a TypeError when setting non-string values
955 # in an existing section:
956 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
957 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
958 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
959 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
960 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
961 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
962 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
963 self.assertRaises(TypeError, cf.add_section, 123)
964
965 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000966 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000967 self.assertRaises(ValueError, cf.add_section, self.default_section)
968
Łukasz Langaa5fab172017-08-24 09:43:53 -0700969 def test_defaults_keyword(self):
970 """bpo-23835 fix for ConfigParser"""
971 cf = self.newconfig(defaults={1: 2.4})
972 self.assertEqual(cf[self.default_section]['1'], '2.4')
973 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
974 cf = self.newconfig(defaults={"A": 5.2})
975 self.assertEqual(cf[self.default_section]['a'], '5.2')
976 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
977
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200978
Ezio Melottidc1fa802013-01-11 06:30:57 +0200979class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200980 config_class = configparser.ConfigParser
981 interpolation = None
982 ini = textwrap.dedent("""
983 [numbers]
984 one = 1
985 two = %(one)s * 2
986 three = ${common:one} * 3
987
988 [hexen]
989 sixteen = ${numbers:two} * 8
990 """).strip()
991
992 def assertMatchesIni(self, cf):
993 self.assertEqual(cf['numbers']['one'], '1')
994 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
995 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
996 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
997
998 def test_no_interpolation(self):
999 cf = self.fromstring(self.ini)
1000 self.assertMatchesIni(cf)
1001
1002 def test_empty_case(self):
1003 cf = self.newconfig()
1004 self.assertIsNone(cf.read_string(""))
1005
1006 def test_none_as_default_interpolation(self):
1007 class CustomConfigParser(configparser.ConfigParser):
1008 _DEFAULT_INTERPOLATION = None
1009
1010 cf = CustomConfigParser()
1011 cf.read_string(self.ini)
1012 self.assertMatchesIni(cf)
1013
1014
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001015class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
1016 config_class = configparser.ConfigParser
1017 interpolation = configparser.LegacyInterpolation()
1018
1019 def test_set_malformatted_interpolation(self):
1020 cf = self.fromstring("[sect]\n"
1021 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1022
1023 self.assertEqual(cf.get('sect', "option1"), "foo")
1024
1025 cf.set("sect", "option1", "%foo")
1026 self.assertEqual(cf.get('sect', "option1"), "%foo")
1027 cf.set("sect", "option1", "foo%")
1028 self.assertEqual(cf.get('sect', "option1"), "foo%")
1029 cf.set("sect", "option1", "f%oo")
1030 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1031
1032 # bug #5741: double percents are *not* malformed
1033 cf.set("sect", "option2", "foo%%bar")
1034 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001035
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001036
Georg Brandl96a60ae2010-07-28 13:13:46 +00001037class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1038 delimiters = (':=', '$')
1039 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001040 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001041
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001042
Łukasz Langac264c092010-11-20 16:15:37 +00001043class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1044 default_section = 'general'
1045
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001046
Ezio Melottidc1fa802013-01-11 06:30:57 +02001047class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001048 config_class = configparser.ConfigParser
1049 wonderful_spam = ("I'm having spam spam spam spam "
1050 "spam spam spam beaked beans spam "
1051 "spam spam and spam!").replace(' ', '\t\n')
1052
1053 def setUp(self):
1054 cf = self.newconfig()
1055 for i in range(100):
1056 s = 'section{}'.format(i)
1057 cf.add_section(s)
1058 for j in range(10):
1059 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1060 with open(support.TESTFN, 'w') as f:
1061 cf.write(f)
1062
1063 def tearDown(self):
1064 os.unlink(support.TESTFN)
1065
1066 def test_dominating_multiline_values(self):
1067 # We're reading from file because this is where the code changed
1068 # during performance updates in Python 3.2
1069 cf_from_file = self.newconfig()
1070 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001071 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001072 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1073 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001074
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001075
Ezio Melottidc1fa802013-01-11 06:30:57 +02001076class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001077 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001078
1079 def test_interpolation(self):
1080 cf = self.get_interpolation_config()
1081 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001082 eq(cf.get("Foo", "bar"),
1083 "something %(with1)s interpolation (1 step)")
1084 eq(cf.get("Foo", "bar9"),
1085 "something %(with9)s lots of interpolation (9 steps)")
1086 eq(cf.get("Foo", "bar10"),
1087 "something %(with10)s lots of interpolation (10 steps)")
1088 eq(cf.get("Foo", "bar11"),
1089 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001090
Fred Drake98e3b292002-10-25 20:42:44 +00001091 def test_items(self):
1092 self.check_items_config([('default', '<default>'),
1093 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001094 ('key', '|%(name)s|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +01001095 ('name', '%(value)s')])
Fred Drake98e3b292002-10-25 20:42:44 +00001096
David Goodger1cbf2062004-10-03 15:55:09 +00001097 def test_set_nonstring_types(self):
1098 cf = self.newconfig()
1099 cf.add_section('non-string')
1100 cf.set('non-string', 'int', 1)
1101 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1102 cf.set('non-string', 'dict', {'pi': 3.14159})
1103 self.assertEqual(cf.get('non-string', 'int'), 1)
1104 self.assertEqual(cf.get('non-string', 'list'),
1105 [0, 1, 1, 2, 3, 5, 8, 13])
1106 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001107 cf.add_section(123)
1108 cf.set(123, 'this is sick', True)
1109 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001110 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001111 # would not work for SortedDict; only checking for the most common
1112 # default dictionary (OrderedDict)
1113 cf.optionxform = lambda x: x
1114 cf.set('non-string', 1, 1)
1115 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001116
Łukasz Langaa5fab172017-08-24 09:43:53 -07001117 def test_defaults_keyword(self):
1118 """bpo-23835 legacy behavior for RawConfigParser"""
1119 with self.assertRaises(AttributeError) as ctx:
1120 self.newconfig(defaults={1: 2.4})
1121 err = ctx.exception
1122 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1123 cf = self.newconfig(defaults={"A": 5.2})
1124 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1125
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001126
Georg Brandl96a60ae2010-07-28 13:13:46 +00001127class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1128 delimiters = (':=', '$')
1129 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001130 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001131
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001132
Ezio Melottidc1fa802013-01-11 06:30:57 +02001133class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001134 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001135 comment_prefixes = ('#', ';', '----')
1136 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001137 empty_lines_in_values = False
1138
1139 def test_reading(self):
1140 smbconf = support.findfile("cfgparser.2")
1141 # check when we pass a mix of readable and non-readable files:
1142 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001143 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001144 self.assertEqual(parsed_files, [smbconf])
1145 sections = ['global', 'homes', 'printers',
1146 'print$', 'pdf-generator', 'tmp', 'Agustin']
1147 self.assertEqual(cf.sections(), sections)
1148 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1149 self.assertEqual(cf.getint("global", "max log size"), 50)
1150 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1151 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001152
Ezio Melottidc1fa802013-01-11 06:30:57 +02001153class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001154 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001155 interpolation = configparser.ExtendedInterpolation()
1156 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001157 strict = True
1158
1159 def fromstring(self, string, defaults=None, optionxform=None):
1160 cf = self.newconfig(defaults)
1161 if optionxform:
1162 cf.optionxform = optionxform
1163 cf.read_string(string)
1164 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001165
1166 def test_extended_interpolation(self):
1167 cf = self.fromstring(textwrap.dedent("""
1168 [common]
1169 favourite Beatle = Paul
1170 favourite color = green
1171
1172 [tom]
1173 favourite band = ${favourite color} day
1174 favourite pope = John ${favourite Beatle} II
1175 sequel = ${favourite pope}I
1176
1177 [ambv]
1178 favourite Beatle = George
1179 son of Edward VII = ${favourite Beatle} V
1180 son of George V = ${son of Edward VII}I
1181
1182 [stanley]
1183 favourite Beatle = ${ambv:favourite Beatle}
1184 favourite pope = ${tom:favourite pope}
1185 favourite color = black
1186 favourite state of mind = paranoid
1187 favourite movie = soylent ${common:favourite color}
1188 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1189 """).strip())
1190
1191 eq = self.assertEqual
1192 eq(cf['common']['favourite Beatle'], 'Paul')
1193 eq(cf['common']['favourite color'], 'green')
1194 eq(cf['tom']['favourite Beatle'], 'Paul')
1195 eq(cf['tom']['favourite color'], 'green')
1196 eq(cf['tom']['favourite band'], 'green day')
1197 eq(cf['tom']['favourite pope'], 'John Paul II')
1198 eq(cf['tom']['sequel'], 'John Paul III')
1199 eq(cf['ambv']['favourite Beatle'], 'George')
1200 eq(cf['ambv']['favourite color'], 'green')
1201 eq(cf['ambv']['son of Edward VII'], 'George V')
1202 eq(cf['ambv']['son of George V'], 'George VI')
1203 eq(cf['stanley']['favourite Beatle'], 'George')
1204 eq(cf['stanley']['favourite color'], 'black')
1205 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1206 eq(cf['stanley']['favourite movie'], 'soylent green')
1207 eq(cf['stanley']['favourite pope'], 'John Paul II')
1208 eq(cf['stanley']['favourite song'],
1209 'black sabbath - paranoid')
1210
1211 def test_endless_loop(self):
1212 cf = self.fromstring(textwrap.dedent("""
1213 [one for you]
1214 ping = ${one for me:pong}
1215
1216 [one for me]
1217 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001218
1219 [selfish]
1220 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001221 """).strip())
1222
1223 with self.assertRaises(configparser.InterpolationDepthError):
1224 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001225 with self.assertRaises(configparser.InterpolationDepthError):
1226 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001227
Łukasz Langa71b37a52010-12-17 21:56:32 +00001228 def test_strange_options(self):
1229 cf = self.fromstring("""
1230 [dollars]
1231 $var = $$value
1232 $var2 = ${$var}
1233 ${sick} = cannot interpolate me
1234
1235 [interpolated]
1236 $other = ${dollars:$var}
1237 $trying = ${dollars:${sick}}
1238 """)
1239
1240 self.assertEqual(cf['dollars']['$var'], '$value')
1241 self.assertEqual(cf['interpolated']['$other'], '$value')
1242 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1243 exception_class = configparser.InterpolationMissingOptionError
1244 with self.assertRaises(exception_class) as cm:
1245 cf['interpolated']['$trying']
1246 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001247 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001248
Łukasz Langae698cd52011-04-28 10:58:57 +02001249 def test_case_sensitivity_basic(self):
1250 ini = textwrap.dedent("""
1251 [common]
1252 optionlower = value
1253 OptionUpper = Value
1254
1255 [Common]
1256 optionlower = a better ${common:optionlower}
1257 OptionUpper = A Better ${common:OptionUpper}
1258
1259 [random]
1260 foolower = ${common:optionlower} redefined
1261 FooUpper = ${Common:OptionUpper} Redefined
1262 """).strip()
1263
1264 cf = self.fromstring(ini)
1265 eq = self.assertEqual
1266 eq(cf['common']['optionlower'], 'value')
1267 eq(cf['common']['OptionUpper'], 'Value')
1268 eq(cf['Common']['optionlower'], 'a better value')
1269 eq(cf['Common']['OptionUpper'], 'A Better Value')
1270 eq(cf['random']['foolower'], 'value redefined')
1271 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1272
1273 def test_case_sensitivity_conflicts(self):
1274 ini = textwrap.dedent("""
1275 [common]
1276 option = value
1277 Option = Value
1278
1279 [Common]
1280 option = a better ${common:option}
1281 Option = A Better ${common:Option}
1282
1283 [random]
1284 foo = ${common:option} redefined
1285 Foo = ${Common:Option} Redefined
1286 """).strip()
1287 with self.assertRaises(configparser.DuplicateOptionError):
1288 cf = self.fromstring(ini)
1289
1290 # raw options
1291 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1292 eq = self.assertEqual
1293 eq(cf['common']['option'], 'value')
1294 eq(cf['common']['Option'], 'Value')
1295 eq(cf['Common']['option'], 'a better value')
1296 eq(cf['Common']['Option'], 'A Better Value')
1297 eq(cf['random']['foo'], 'value redefined')
1298 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001299
1300 def test_other_errors(self):
1301 cf = self.fromstring("""
1302 [interpolation fail]
1303 case1 = ${where's the brace
1304 case2 = ${does_not_exist}
1305 case3 = ${wrong_section:wrong_value}
1306 case4 = ${i:like:colon:characters}
1307 case5 = $100 for Fail No 5!
1308 """)
1309
1310 with self.assertRaises(configparser.InterpolationSyntaxError):
1311 cf['interpolation fail']['case1']
1312 with self.assertRaises(configparser.InterpolationMissingOptionError):
1313 cf['interpolation fail']['case2']
1314 with self.assertRaises(configparser.InterpolationMissingOptionError):
1315 cf['interpolation fail']['case3']
1316 with self.assertRaises(configparser.InterpolationSyntaxError):
1317 cf['interpolation fail']['case4']
1318 with self.assertRaises(configparser.InterpolationSyntaxError):
1319 cf['interpolation fail']['case5']
1320 with self.assertRaises(ValueError):
1321 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001322
1323
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001324class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001325 allow_no_value = True
1326
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001327
Ezio Melottidc1fa802013-01-11 06:30:57 +02001328class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001329 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001330 delimiters = {'='}
1331 comment_prefixes = {'#'}
1332 allow_no_value = True
1333
1334 def test_cfgparser_dot_3(self):
1335 tricky = support.findfile("cfgparser.3")
1336 cf = self.newconfig()
1337 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1338 self.assertEqual(cf.sections(), ['strange',
1339 'corruption',
1340 'yeah, sections can be '
1341 'indented as well',
1342 'another one!',
1343 'no values here',
1344 'tricky interpolation',
1345 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001346 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001347 vars={'interpolate': '-1'}), -1)
1348 with self.assertRaises(ValueError):
1349 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001350 cf.getint(self.default_section, 'go', raw=True,
1351 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001352 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1353 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1354 longname = 'yeah, sections can be indented as well'
1355 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001356 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001357 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1358 # `go` from DEFAULT
1359 with self.assertRaises(configparser.InterpolationMissingOptionError):
1360 cf.items('no values here')
1361 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1362 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1363 cf.get('tricky interpolation', 'go'))
1364 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1365
1366 def test_unicode_failure(self):
1367 tricky = support.findfile("cfgparser.3")
1368 cf = self.newconfig()
1369 with self.assertRaises(UnicodeDecodeError):
1370 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001371
Fred Drake88444412010-09-03 04:22:36 +00001372
1373class Issue7005TestCase(unittest.TestCase):
1374 """Test output when None is set() as a value and allow_no_value == False.
1375
1376 http://bugs.python.org/issue7005
1377
1378 """
1379
1380 expected_output = "[section]\noption = None\n\n"
1381
1382 def prepare(self, config_class):
1383 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001384 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001385 cp.add_section("section")
1386 cp.set("section", "option", None)
1387 sio = io.StringIO()
1388 cp.write(sio)
1389 return sio.getvalue()
1390
1391 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001392 cp = configparser.ConfigParser(allow_no_value=False)
1393 cp.add_section("section")
1394 with self.assertRaises(TypeError):
1395 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001396
1397 def test_none_as_value_stringified_raw(self):
1398 output = self.prepare(configparser.RawConfigParser)
1399 self.assertEqual(output, self.expected_output)
1400
1401
Thomas Wouters89f507f2006-12-13 04:49:30 +00001402class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001403 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001404
1405 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001406 cf = self.fromstring("[b]\n"
1407 "o4=1\n"
1408 "o3=2\n"
1409 "o2=3\n"
1410 "o1=4\n"
1411 "[a]\n"
1412 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001413 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001414 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001415 self.assertEqual(output.getvalue(),
1416 "[a]\n"
1417 "k = v\n\n"
1418 "[b]\n"
1419 "o1 = 4\n"
1420 "o2 = 3\n"
1421 "o3 = 2\n"
1422 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001423
Fred Drake03c44a32010-02-19 06:08:41 +00001424
Ezio Melottidc1fa802013-01-11 06:30:57 +02001425class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001426 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001427 comment_prefixes = '#;'
1428 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001429
1430 def test_comment_handling(self):
1431 config_string = textwrap.dedent("""\
1432 [Commented Bar]
1433 baz=qwe ; a comment
1434 foo: bar # not a comment!
1435 # but this is a comment
1436 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001437 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001438 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001439 """)
1440 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001441 self.assertEqual(cf.get('Commented Bar', 'foo'),
1442 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001443 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001444 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1445 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001446
Ezio Melottidc1fa802013-01-11 06:30:57 +02001447class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001448 config_class = configparser.ConfigParser
1449
1450 def fromstring(self, string, defaults=None):
1451 cf = self.newconfig(defaults)
1452 cf.read_string(string)
1453 cf_copy = self.newconfig()
1454 cf_copy.read_dict(cf)
1455 # we have to clean up option duplicates that appeared because of
1456 # the magic DEFAULTSECT behaviour.
1457 for section in cf_copy.values():
1458 if section.name == self.default_section:
1459 continue
1460 for default, value in cf[self.default_section].items():
1461 if section[default] == value:
1462 del section[default]
1463 return cf_copy
1464
Łukasz Langadaab1c82011-04-27 18:10:05 +02001465
1466class FakeFile:
1467 def __init__(self):
1468 file_path = support.findfile("cfgparser.1")
1469 with open(file_path) as f:
1470 self.lines = f.readlines()
1471 self.lines.reverse()
1472
1473 def readline(self):
1474 if len(self.lines):
1475 return self.lines.pop()
1476 return ''
1477
1478
1479def readline_generator(f):
1480 """As advised in Doc/library/configparser.rst."""
1481 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001482 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001483 yield line
1484 line = f.readline()
1485
1486
1487class ReadFileTestCase(unittest.TestCase):
1488 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001489 file_paths = [support.findfile("cfgparser.1")]
1490 try:
1491 file_paths.append(file_paths[0].encode('utf8'))
1492 except UnicodeEncodeError:
1493 pass # unfortunately we can't test bytes on this path
1494 for file_path in file_paths:
1495 parser = configparser.ConfigParser()
1496 with open(file_path) as f:
1497 parser.read_file(f)
1498 self.assertIn("Foo Bar", parser)
1499 self.assertIn("foo", parser["Foo Bar"])
1500 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001501
1502 def test_iterable(self):
1503 lines = textwrap.dedent("""
1504 [Foo Bar]
1505 foo=newbar""").strip().split('\n')
1506 parser = configparser.ConfigParser()
1507 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001508 self.assertIn("Foo Bar", parser)
1509 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001510 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1511
1512 def test_readline_generator(self):
1513 """Issue #11670."""
1514 parser = configparser.ConfigParser()
1515 with self.assertRaises(TypeError):
1516 parser.read_file(FakeFile())
1517 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001518 self.assertIn("Foo Bar", parser)
1519 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001520 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1521
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001522 def test_source_as_bytes(self):
1523 """Issue #18260."""
1524 lines = textwrap.dedent("""
1525 [badbad]
1526 [badbad]""").strip().split('\n')
1527 parser = configparser.ConfigParser()
1528 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1529 parser.read_file(lines, source=b"badbad")
1530 self.assertEqual(
1531 str(dse.exception),
1532 "While reading from b'badbad' [line 2]: section 'badbad' "
1533 "already exists"
1534 )
1535 lines = textwrap.dedent("""
1536 [badbad]
1537 bad = bad
1538 bad = bad""").strip().split('\n')
1539 parser = configparser.ConfigParser()
1540 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1541 parser.read_file(lines, source=b"badbad")
1542 self.assertEqual(
1543 str(dse.exception),
1544 "While reading from b'badbad' [line 3]: option 'bad' in section "
1545 "'badbad' already exists"
1546 )
1547 lines = textwrap.dedent("""
1548 [badbad]
1549 = bad""").strip().split('\n')
1550 parser = configparser.ConfigParser()
1551 with self.assertRaises(configparser.ParsingError) as dse:
1552 parser.read_file(lines, source=b"badbad")
1553 self.assertEqual(
1554 str(dse.exception),
1555 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1556 )
1557 lines = textwrap.dedent("""
1558 [badbad
1559 bad = bad""").strip().split('\n')
1560 parser = configparser.ConfigParser()
1561 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1562 parser.read_file(lines, source=b"badbad")
1563 self.assertEqual(
1564 str(dse.exception),
1565 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1566 "'[badbad'"
1567 )
1568
Łukasz Langadaab1c82011-04-27 18:10:05 +02001569
Łukasz Langa71b37a52010-12-17 21:56:32 +00001570class CoverageOneHundredTestCase(unittest.TestCase):
1571 """Covers edge cases in the codebase."""
1572
1573 def test_duplicate_option_error(self):
1574 error = configparser.DuplicateOptionError('section', 'option')
1575 self.assertEqual(error.section, 'section')
1576 self.assertEqual(error.option, 'option')
1577 self.assertEqual(error.source, None)
1578 self.assertEqual(error.lineno, None)
1579 self.assertEqual(error.args, ('section', 'option', None, None))
1580 self.assertEqual(str(error), "Option 'option' in section 'section' "
1581 "already exists")
1582
1583 def test_interpolation_depth_error(self):
1584 error = configparser.InterpolationDepthError('option', 'section',
1585 'rawval')
1586 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1587 self.assertEqual(error.option, 'option')
1588 self.assertEqual(error.section, 'section')
1589
1590 def test_parsing_error(self):
1591 with self.assertRaises(ValueError) as cm:
1592 configparser.ParsingError()
1593 self.assertEqual(str(cm.exception), "Required argument `source' not "
1594 "given.")
1595 with self.assertRaises(ValueError) as cm:
1596 configparser.ParsingError(source='source', filename='filename')
1597 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1598 "and `source'. Use `source'.")
1599 error = configparser.ParsingError(filename='source')
1600 self.assertEqual(error.source, 'source')
1601 with warnings.catch_warnings(record=True) as w:
1602 warnings.simplefilter("always", DeprecationWarning)
1603 self.assertEqual(error.filename, 'source')
1604 error.filename = 'filename'
1605 self.assertEqual(error.source, 'filename')
1606 for warning in w:
1607 self.assertTrue(warning.category is DeprecationWarning)
1608
1609 def test_interpolation_validation(self):
1610 parser = configparser.ConfigParser()
1611 parser.read_string("""
1612 [section]
1613 invalid_percent = %
1614 invalid_reference = %(()
1615 invalid_variable = %(does_not_exist)s
1616 """)
1617 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1618 parser['section']['invalid_percent']
1619 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1620 "'(', found: '%'")
1621 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1622 parser['section']['invalid_reference']
1623 self.assertEqual(str(cm.exception), "bad interpolation variable "
1624 "reference '%(()'")
1625
1626 def test_readfp_deprecation(self):
1627 sio = io.StringIO("""
1628 [section]
1629 option = value
1630 """)
1631 parser = configparser.ConfigParser()
1632 with warnings.catch_warnings(record=True) as w:
1633 warnings.simplefilter("always", DeprecationWarning)
1634 parser.readfp(sio, filename='StringIO')
1635 for warning in w:
1636 self.assertTrue(warning.category is DeprecationWarning)
1637 self.assertEqual(len(parser), 2)
1638 self.assertEqual(parser['section']['option'], 'value')
1639
1640 def test_safeconfigparser_deprecation(self):
1641 with warnings.catch_warnings(record=True) as w:
1642 warnings.simplefilter("always", DeprecationWarning)
1643 parser = configparser.SafeConfigParser()
1644 for warning in w:
1645 self.assertTrue(warning.category is DeprecationWarning)
1646
1647 def test_sectionproxy_repr(self):
1648 parser = configparser.ConfigParser()
1649 parser.read_string("""
1650 [section]
1651 key = value
1652 """)
1653 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001654
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001655 def test_inconsistent_converters_state(self):
1656 parser = configparser.ConfigParser()
1657 import decimal
1658 parser.converters['decimal'] = decimal.Decimal
1659 parser.read_string("""
1660 [s1]
1661 one = 1
1662 [s2]
1663 two = 2
1664 """)
1665 self.assertIn('decimal', parser.converters)
1666 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1667 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1668 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1669 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1670 del parser.getdecimal
1671 with self.assertRaises(AttributeError):
1672 parser.getdecimal('s1', 'one')
1673 self.assertIn('decimal', parser.converters)
1674 del parser.converters['decimal']
1675 self.assertNotIn('decimal', parser.converters)
1676 with self.assertRaises(AttributeError):
1677 parser.getdecimal('s1', 'one')
1678 with self.assertRaises(AttributeError):
1679 parser['s1'].getdecimal('one')
1680 with self.assertRaises(AttributeError):
1681 parser['s2'].getdecimal('two')
1682
Łukasz Langae7851952012-01-20 14:57:55 +01001683
1684class ExceptionPicklingTestCase(unittest.TestCase):
1685 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1686
1687 def test_error(self):
1688 import pickle
1689 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001690 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1691 pickled = pickle.dumps(e1, proto)
1692 e2 = pickle.loads(pickled)
1693 self.assertEqual(e1.message, e2.message)
1694 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001695
1696 def test_nosectionerror(self):
1697 import pickle
1698 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001699 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1700 pickled = pickle.dumps(e1, proto)
1701 e2 = pickle.loads(pickled)
1702 self.assertEqual(e1.message, e2.message)
1703 self.assertEqual(e1.args, e2.args)
1704 self.assertEqual(e1.section, e2.section)
1705 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001706
1707 def test_nooptionerror(self):
1708 import pickle
1709 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001710 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1711 pickled = pickle.dumps(e1, proto)
1712 e2 = pickle.loads(pickled)
1713 self.assertEqual(e1.message, e2.message)
1714 self.assertEqual(e1.args, e2.args)
1715 self.assertEqual(e1.section, e2.section)
1716 self.assertEqual(e1.option, e2.option)
1717 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001718
1719 def test_duplicatesectionerror(self):
1720 import pickle
1721 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001722 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1723 pickled = pickle.dumps(e1, proto)
1724 e2 = pickle.loads(pickled)
1725 self.assertEqual(e1.message, e2.message)
1726 self.assertEqual(e1.args, e2.args)
1727 self.assertEqual(e1.section, e2.section)
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_duplicateoptionerror(self):
1733 import pickle
1734 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1735 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001736 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1737 pickled = pickle.dumps(e1, proto)
1738 e2 = pickle.loads(pickled)
1739 self.assertEqual(e1.message, e2.message)
1740 self.assertEqual(e1.args, e2.args)
1741 self.assertEqual(e1.section, e2.section)
1742 self.assertEqual(e1.option, e2.option)
1743 self.assertEqual(e1.source, e2.source)
1744 self.assertEqual(e1.lineno, e2.lineno)
1745 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001746
1747 def test_interpolationerror(self):
1748 import pickle
1749 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001750 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1751 pickled = pickle.dumps(e1, proto)
1752 e2 = pickle.loads(pickled)
1753 self.assertEqual(e1.message, e2.message)
1754 self.assertEqual(e1.args, e2.args)
1755 self.assertEqual(e1.section, e2.section)
1756 self.assertEqual(e1.option, e2.option)
1757 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001758
1759 def test_interpolationmissingoptionerror(self):
1760 import pickle
1761 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1762 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001763 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1764 pickled = pickle.dumps(e1, proto)
1765 e2 = pickle.loads(pickled)
1766 self.assertEqual(e1.message, e2.message)
1767 self.assertEqual(e1.args, e2.args)
1768 self.assertEqual(e1.section, e2.section)
1769 self.assertEqual(e1.option, e2.option)
1770 self.assertEqual(e1.reference, e2.reference)
1771 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001772
1773 def test_interpolationsyntaxerror(self):
1774 import pickle
1775 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001776 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1777 pickled = pickle.dumps(e1, proto)
1778 e2 = pickle.loads(pickled)
1779 self.assertEqual(e1.message, e2.message)
1780 self.assertEqual(e1.args, e2.args)
1781 self.assertEqual(e1.section, e2.section)
1782 self.assertEqual(e1.option, e2.option)
1783 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001784
1785 def test_interpolationdeptherror(self):
1786 import pickle
1787 e1 = configparser.InterpolationDepthError('option', 'section',
1788 'rawval')
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.section, e2.section)
1795 self.assertEqual(e1.option, e2.option)
1796 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001797
1798 def test_parsingerror(self):
1799 import pickle
1800 e1 = configparser.ParsingError('source')
1801 e1.append(1, 'line1')
1802 e1.append(2, 'line2')
1803 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001804 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1805 pickled = pickle.dumps(e1, proto)
1806 e2 = pickle.loads(pickled)
1807 self.assertEqual(e1.message, e2.message)
1808 self.assertEqual(e1.args, e2.args)
1809 self.assertEqual(e1.source, e2.source)
1810 self.assertEqual(e1.errors, e2.errors)
1811 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001812 e1 = configparser.ParsingError(filename='filename')
1813 e1.append(1, 'line1')
1814 e1.append(2, 'line2')
1815 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001816 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1817 pickled = pickle.dumps(e1, proto)
1818 e2 = pickle.loads(pickled)
1819 self.assertEqual(e1.message, e2.message)
1820 self.assertEqual(e1.args, e2.args)
1821 self.assertEqual(e1.source, e2.source)
1822 self.assertEqual(e1.errors, e2.errors)
1823 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001824
1825 def test_missingsectionheadererror(self):
1826 import pickle
1827 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001828 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1829 pickled = pickle.dumps(e1, proto)
1830 e2 = pickle.loads(pickled)
1831 self.assertEqual(e1.message, e2.message)
1832 self.assertEqual(e1.args, e2.args)
1833 self.assertEqual(e1.line, e2.line)
1834 self.assertEqual(e1.source, e2.source)
1835 self.assertEqual(e1.lineno, e2.lineno)
1836 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001837
1838
Łukasz Langacba24322012-07-07 18:54:08 +02001839class InlineCommentStrippingTestCase(unittest.TestCase):
1840 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1841 delimiter occurs earlier without preceding space.."""
1842
1843 def test_stripping(self):
1844 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1845 '//'))
1846 cfg.read_string("""
1847 [section]
1848 k1 = v1;still v1
1849 k2 = v2 ;a comment
1850 k3 = v3 ; also a comment
1851 k4 = v4;still v4 ;a comment
1852 k5 = v5;still v5 ; also a comment
1853 k6 = v6;still v6; and still v6 ;a comment
1854 k7 = v7;still v7; and still v7 ; also a comment
1855
1856 [multiprefix]
1857 k1 = v1;still v1 #a comment ; yeah, pretty much
1858 k2 = v2 // this already is a comment ; continued
1859 k3 = v3;#//still v3# and still v3 ; a comment
1860 """)
1861 s = cfg['section']
1862 self.assertEqual(s['k1'], 'v1;still v1')
1863 self.assertEqual(s['k2'], 'v2')
1864 self.assertEqual(s['k3'], 'v3')
1865 self.assertEqual(s['k4'], 'v4;still v4')
1866 self.assertEqual(s['k5'], 'v5;still v5')
1867 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1868 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1869 s = cfg['multiprefix']
1870 self.assertEqual(s['k1'], 'v1;still v1')
1871 self.assertEqual(s['k2'], 'v2')
1872 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1873
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001874
Łukasz Langa949053b2014-09-04 01:36:33 -07001875class ExceptionContextTestCase(unittest.TestCase):
1876 """ Test that implementation details doesn't leak
1877 through raising exceptions. """
1878
1879 def test_get_basic_interpolation(self):
1880 parser = configparser.ConfigParser()
1881 parser.read_string("""
1882 [Paths]
1883 home_dir: /Users
1884 my_dir: %(home_dir1)s/lumberjack
1885 my_pictures: %(my_dir)s/Pictures
1886 """)
1887 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1888 with cm:
1889 parser.get('Paths', 'my_dir')
1890 self.assertIs(cm.exception.__suppress_context__, True)
1891
1892 def test_get_extended_interpolation(self):
1893 parser = configparser.ConfigParser(
1894 interpolation=configparser.ExtendedInterpolation())
1895 parser.read_string("""
1896 [Paths]
1897 home_dir: /Users
1898 my_dir: ${home_dir1}/lumberjack
1899 my_pictures: ${my_dir}/Pictures
1900 """)
1901 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1902 with cm:
1903 parser.get('Paths', 'my_dir')
1904 self.assertIs(cm.exception.__suppress_context__, True)
1905
1906 def test_missing_options(self):
1907 parser = configparser.ConfigParser()
1908 parser.read_string("""
1909 [Paths]
1910 home_dir: /Users
1911 """)
1912 with self.assertRaises(configparser.NoSectionError) as cm:
1913 parser.options('test')
1914 self.assertIs(cm.exception.__suppress_context__, True)
1915
1916 def test_missing_section(self):
1917 config = configparser.ConfigParser()
1918 with self.assertRaises(configparser.NoSectionError) as cm:
1919 config.set('Section1', 'an_int', '15')
1920 self.assertIs(cm.exception.__suppress_context__, True)
1921
1922 def test_remove_option(self):
1923 config = configparser.ConfigParser()
1924 with self.assertRaises(configparser.NoSectionError) as cm:
1925 config.remove_option('Section1', 'an_int')
1926 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001927
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001928
1929class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1930 """Introduced in 3.5, issue #18159."""
1931
1932 config_class = configparser.ConfigParser
1933
1934 def newconfig(self, defaults=None):
1935 instance = super().newconfig(defaults=defaults)
1936 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1937 if e.strip()]
1938 return instance
1939
1940 def test_converters(self):
1941 cfg = self.newconfig()
1942 self.assertIn('boolean', cfg.converters)
1943 self.assertIn('list', cfg.converters)
1944 self.assertIsNone(cfg.converters['int'])
1945 self.assertIsNone(cfg.converters['float'])
1946 self.assertIsNone(cfg.converters['boolean'])
1947 self.assertIsNotNone(cfg.converters['list'])
1948 self.assertEqual(len(cfg.converters), 4)
1949 with self.assertRaises(ValueError):
1950 cfg.converters[''] = lambda v: v
1951 with self.assertRaises(ValueError):
1952 cfg.converters[None] = lambda v: v
1953 cfg.read_string("""
1954 [s]
1955 str = string
1956 int = 1
1957 float = 0.5
1958 list = a b c d e f g
1959 bool = yes
1960 """)
1961 s = cfg['s']
1962 self.assertEqual(s['str'], 'string')
1963 self.assertEqual(s['int'], '1')
1964 self.assertEqual(s['float'], '0.5')
1965 self.assertEqual(s['list'], 'a b c d e f g')
1966 self.assertEqual(s['bool'], 'yes')
1967 self.assertEqual(cfg.get('s', 'str'), 'string')
1968 self.assertEqual(cfg.get('s', 'int'), '1')
1969 self.assertEqual(cfg.get('s', 'float'), '0.5')
1970 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1971 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1972 self.assertEqual(cfg.get('s', 'str'), 'string')
1973 self.assertEqual(cfg.getint('s', 'int'), 1)
1974 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1975 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1976 'e', 'f', 'g'])
1977 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1978 self.assertEqual(s.get('str'), 'string')
1979 self.assertEqual(s.getint('int'), 1)
1980 self.assertEqual(s.getfloat('float'), 0.5)
1981 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1982 'e', 'f', 'g'])
1983 self.assertEqual(s.getboolean('bool'), True)
1984 with self.assertRaises(AttributeError):
1985 cfg.getdecimal('s', 'float')
1986 with self.assertRaises(AttributeError):
1987 s.getdecimal('float')
1988 import decimal
1989 cfg.converters['decimal'] = decimal.Decimal
1990 self.assertIn('decimal', cfg.converters)
1991 self.assertIsNotNone(cfg.converters['decimal'])
1992 self.assertEqual(len(cfg.converters), 5)
1993 dec0_5 = decimal.Decimal('0.5')
1994 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1995 self.assertEqual(s.getdecimal('float'), dec0_5)
1996 del cfg.converters['decimal']
1997 self.assertNotIn('decimal', cfg.converters)
1998 self.assertEqual(len(cfg.converters), 4)
1999 with self.assertRaises(AttributeError):
2000 cfg.getdecimal('s', 'float')
2001 with self.assertRaises(AttributeError):
2002 s.getdecimal('float')
2003 with self.assertRaises(KeyError):
2004 del cfg.converters['decimal']
2005 with self.assertRaises(KeyError):
2006 del cfg.converters['']
2007 with self.assertRaises(KeyError):
2008 del cfg.converters[None]
2009
2010
2011class BlatantOverrideConvertersTestCase(unittest.TestCase):
2012 """What if somebody overrode a getboolean()? We want to make sure that in
2013 this case the automatic converters do not kick in."""
2014
2015 config = """
2016 [one]
2017 one = false
2018 two = false
2019 three = long story short
2020
2021 [two]
2022 one = false
2023 two = false
2024 three = four
2025 """
2026
2027 def test_converters_at_init(self):
2028 cfg = configparser.ConfigParser(converters={'len': len})
2029 cfg.read_string(self.config)
2030 self._test_len(cfg)
2031 self.assertIsNotNone(cfg.converters['len'])
2032
2033 def test_inheritance(self):
2034 class StrangeConfigParser(configparser.ConfigParser):
2035 gettysburg = 'a historic borough in south central Pennsylvania'
2036
2037 def getboolean(self, section, option, *, raw=False, vars=None,
2038 fallback=configparser._UNSET):
2039 if section == option:
2040 return True
2041 return super().getboolean(section, option, raw=raw, vars=vars,
2042 fallback=fallback)
2043 def getlen(self, section, option, *, raw=False, vars=None,
2044 fallback=configparser._UNSET):
2045 return self._get_conv(section, option, len, raw=raw, vars=vars,
2046 fallback=fallback)
2047
2048 cfg = StrangeConfigParser()
2049 cfg.read_string(self.config)
2050 self._test_len(cfg)
2051 self.assertIsNone(cfg.converters['len'])
2052 self.assertTrue(cfg.getboolean('one', 'one'))
2053 self.assertTrue(cfg.getboolean('two', 'two'))
2054 self.assertFalse(cfg.getboolean('one', 'two'))
2055 self.assertFalse(cfg.getboolean('two', 'one'))
2056 cfg.converters['boolean'] = cfg._convert_to_boolean
2057 self.assertFalse(cfg.getboolean('one', 'one'))
2058 self.assertFalse(cfg.getboolean('two', 'two'))
2059 self.assertFalse(cfg.getboolean('one', 'two'))
2060 self.assertFalse(cfg.getboolean('two', 'one'))
2061
2062 def _test_len(self, cfg):
2063 self.assertEqual(len(cfg.converters), 4)
2064 self.assertIn('boolean', cfg.converters)
2065 self.assertIn('len', cfg.converters)
2066 self.assertNotIn('tysburg', cfg.converters)
2067 self.assertIsNone(cfg.converters['int'])
2068 self.assertIsNone(cfg.converters['float'])
2069 self.assertIsNone(cfg.converters['boolean'])
2070 self.assertEqual(cfg.getlen('one', 'one'), 5)
2071 self.assertEqual(cfg.getlen('one', 'two'), 5)
2072 self.assertEqual(cfg.getlen('one', 'three'), 16)
2073 self.assertEqual(cfg.getlen('two', 'one'), 5)
2074 self.assertEqual(cfg.getlen('two', 'two'), 5)
2075 self.assertEqual(cfg.getlen('two', 'three'), 4)
2076 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2077 with self.assertRaises(configparser.NoOptionError):
2078 cfg.getlen('two', 'four')
2079 self.assertEqual(cfg['one'].getlen('one'), 5)
2080 self.assertEqual(cfg['one'].getlen('two'), 5)
2081 self.assertEqual(cfg['one'].getlen('three'), 16)
2082 self.assertEqual(cfg['two'].getlen('one'), 5)
2083 self.assertEqual(cfg['two'].getlen('two'), 5)
2084 self.assertEqual(cfg['two'].getlen('three'), 4)
2085 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2086 self.assertEqual(cfg['two'].getlen('four'), None)
2087
2088 def test_instance_assignment(self):
2089 cfg = configparser.ConfigParser()
2090 cfg.getboolean = lambda section, option: True
2091 cfg.getlen = lambda section, option: len(cfg[section][option])
2092 cfg.read_string(self.config)
2093 self.assertEqual(len(cfg.converters), 3)
2094 self.assertIn('boolean', cfg.converters)
2095 self.assertNotIn('len', cfg.converters)
2096 self.assertIsNone(cfg.converters['int'])
2097 self.assertIsNone(cfg.converters['float'])
2098 self.assertIsNone(cfg.converters['boolean'])
2099 self.assertTrue(cfg.getboolean('one', 'one'))
2100 self.assertTrue(cfg.getboolean('two', 'two'))
2101 self.assertTrue(cfg.getboolean('one', 'two'))
2102 self.assertTrue(cfg.getboolean('two', 'one'))
2103 cfg.converters['boolean'] = cfg._convert_to_boolean
2104 self.assertFalse(cfg.getboolean('one', 'one'))
2105 self.assertFalse(cfg.getboolean('two', 'two'))
2106 self.assertFalse(cfg.getboolean('one', 'two'))
2107 self.assertFalse(cfg.getboolean('two', 'one'))
2108 self.assertEqual(cfg.getlen('one', 'one'), 5)
2109 self.assertEqual(cfg.getlen('one', 'two'), 5)
2110 self.assertEqual(cfg.getlen('one', 'three'), 16)
2111 self.assertEqual(cfg.getlen('two', 'one'), 5)
2112 self.assertEqual(cfg.getlen('two', 'two'), 5)
2113 self.assertEqual(cfg.getlen('two', 'three'), 4)
2114 # If a getter impl is assigned straight to the instance, it won't
2115 # be available on the section proxies.
2116 with self.assertRaises(AttributeError):
2117 self.assertEqual(cfg['one'].getlen('one'), 5)
2118 with self.assertRaises(AttributeError):
2119 self.assertEqual(cfg['two'].getlen('one'), 5)
2120
2121
Martin Panter2b9b70b2016-09-09 06:46:48 +00002122class MiscTestCase(unittest.TestCase):
2123 def test__all__(self):
2124 blacklist = {"Error"}
2125 support.check__all__(self, configparser, blacklist=blacklist)
2126
2127
Ezio Melottidc1fa802013-01-11 06:30:57 +02002128if __name__ == '__main__':
2129 unittest.main()