blob: 4d07203b9dac289732da580a88e10262266e7cf1 [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|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000918 ('name', 'value'),
919 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000920
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000921 def test_safe_interpolation(self):
922 # See http://www.python.org/sf/511737
923 cf = self.fromstring("[section]\n"
924 "option1{eq}xxx\n"
925 "option2{eq}%(option1)s/xxx\n"
926 "ok{eq}%(option1)s/%%s\n"
927 "not_ok{eq}%(option2)s/%%s".format(
928 eq=self.delimiters[0]))
929 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
930 if self.interpolation == configparser._UNSET:
931 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
932 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
933 with self.assertRaises(TypeError):
934 cf.get("section", "not_ok")
935
936 def test_set_malformatted_interpolation(self):
937 cf = self.fromstring("[sect]\n"
938 "option1{eq}foo\n".format(eq=self.delimiters[0]))
939
940 self.assertEqual(cf.get('sect', "option1"), "foo")
941
942 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
943 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
944 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
945
946 self.assertEqual(cf.get('sect', "option1"), "foo")
947
948 # bug #5741: double percents are *not* malformed
949 cf.set("sect", "option2", "foo%%bar")
950 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
951
David Goodger1cbf2062004-10-03 15:55:09 +0000952 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000953 cf = self.fromstring("[sect]\n"
954 "option1{eq}foo\n".format(eq=self.delimiters[0]))
955 # Check that we get a TypeError when setting non-string values
956 # in an existing section:
957 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
958 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
959 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
960 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
961 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
962 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
963 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
964 self.assertRaises(TypeError, cf.add_section, 123)
965
966 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000967 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000968 self.assertRaises(ValueError, cf.add_section, self.default_section)
969
Łukasz Langaa5fab172017-08-24 09:43:53 -0700970 def test_defaults_keyword(self):
971 """bpo-23835 fix for ConfigParser"""
972 cf = self.newconfig(defaults={1: 2.4})
973 self.assertEqual(cf[self.default_section]['1'], '2.4')
974 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
975 cf = self.newconfig(defaults={"A": 5.2})
976 self.assertEqual(cf[self.default_section]['a'], '5.2')
977 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
978
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200979
Ezio Melottidc1fa802013-01-11 06:30:57 +0200980class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200981 config_class = configparser.ConfigParser
982 interpolation = None
983 ini = textwrap.dedent("""
984 [numbers]
985 one = 1
986 two = %(one)s * 2
987 three = ${common:one} * 3
988
989 [hexen]
990 sixteen = ${numbers:two} * 8
991 """).strip()
992
993 def assertMatchesIni(self, cf):
994 self.assertEqual(cf['numbers']['one'], '1')
995 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
996 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
997 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
998
999 def test_no_interpolation(self):
1000 cf = self.fromstring(self.ini)
1001 self.assertMatchesIni(cf)
1002
1003 def test_empty_case(self):
1004 cf = self.newconfig()
1005 self.assertIsNone(cf.read_string(""))
1006
1007 def test_none_as_default_interpolation(self):
1008 class CustomConfigParser(configparser.ConfigParser):
1009 _DEFAULT_INTERPOLATION = None
1010
1011 cf = CustomConfigParser()
1012 cf.read_string(self.ini)
1013 self.assertMatchesIni(cf)
1014
1015
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001016class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
1017 config_class = configparser.ConfigParser
1018 interpolation = configparser.LegacyInterpolation()
1019
1020 def test_set_malformatted_interpolation(self):
1021 cf = self.fromstring("[sect]\n"
1022 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1023
1024 self.assertEqual(cf.get('sect', "option1"), "foo")
1025
1026 cf.set("sect", "option1", "%foo")
1027 self.assertEqual(cf.get('sect', "option1"), "%foo")
1028 cf.set("sect", "option1", "foo%")
1029 self.assertEqual(cf.get('sect', "option1"), "foo%")
1030 cf.set("sect", "option1", "f%oo")
1031 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1032
1033 # bug #5741: double percents are *not* malformed
1034 cf.set("sect", "option2", "foo%%bar")
1035 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001036
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001037
Georg Brandl96a60ae2010-07-28 13:13:46 +00001038class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1039 delimiters = (':=', '$')
1040 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001041 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001042
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001043
Łukasz Langac264c092010-11-20 16:15:37 +00001044class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1045 default_section = 'general'
1046
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001047
Ezio Melottidc1fa802013-01-11 06:30:57 +02001048class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001049 config_class = configparser.ConfigParser
1050 wonderful_spam = ("I'm having spam spam spam spam "
1051 "spam spam spam beaked beans spam "
1052 "spam spam and spam!").replace(' ', '\t\n')
1053
1054 def setUp(self):
1055 cf = self.newconfig()
1056 for i in range(100):
1057 s = 'section{}'.format(i)
1058 cf.add_section(s)
1059 for j in range(10):
1060 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1061 with open(support.TESTFN, 'w') as f:
1062 cf.write(f)
1063
1064 def tearDown(self):
1065 os.unlink(support.TESTFN)
1066
1067 def test_dominating_multiline_values(self):
1068 # We're reading from file because this is where the code changed
1069 # during performance updates in Python 3.2
1070 cf_from_file = self.newconfig()
1071 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001072 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001073 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1074 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001075
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001076
Ezio Melottidc1fa802013-01-11 06:30:57 +02001077class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001078 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001079
1080 def test_interpolation(self):
1081 cf = self.get_interpolation_config()
1082 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001083 eq(cf.get("Foo", "bar"),
1084 "something %(with1)s interpolation (1 step)")
1085 eq(cf.get("Foo", "bar9"),
1086 "something %(with9)s lots of interpolation (9 steps)")
1087 eq(cf.get("Foo", "bar10"),
1088 "something %(with10)s lots of interpolation (10 steps)")
1089 eq(cf.get("Foo", "bar11"),
1090 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001091
Fred Drake98e3b292002-10-25 20:42:44 +00001092 def test_items(self):
1093 self.check_items_config([('default', '<default>'),
1094 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001095 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001096 ('name', '%(value)s'),
1097 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001098
David Goodger1cbf2062004-10-03 15:55:09 +00001099 def test_set_nonstring_types(self):
1100 cf = self.newconfig()
1101 cf.add_section('non-string')
1102 cf.set('non-string', 'int', 1)
1103 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1104 cf.set('non-string', 'dict', {'pi': 3.14159})
1105 self.assertEqual(cf.get('non-string', 'int'), 1)
1106 self.assertEqual(cf.get('non-string', 'list'),
1107 [0, 1, 1, 2, 3, 5, 8, 13])
1108 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001109 cf.add_section(123)
1110 cf.set(123, 'this is sick', True)
1111 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001112 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001113 # would not work for SortedDict; only checking for the most common
1114 # default dictionary (OrderedDict)
1115 cf.optionxform = lambda x: x
1116 cf.set('non-string', 1, 1)
1117 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001118
Łukasz Langaa5fab172017-08-24 09:43:53 -07001119 def test_defaults_keyword(self):
1120 """bpo-23835 legacy behavior for RawConfigParser"""
1121 with self.assertRaises(AttributeError) as ctx:
1122 self.newconfig(defaults={1: 2.4})
1123 err = ctx.exception
1124 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1125 cf = self.newconfig(defaults={"A": 5.2})
1126 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1127
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001128
Georg Brandl96a60ae2010-07-28 13:13:46 +00001129class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1130 delimiters = (':=', '$')
1131 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001132 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001133
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001134
Ezio Melottidc1fa802013-01-11 06:30:57 +02001135class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001136 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001137 comment_prefixes = ('#', ';', '----')
1138 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001139 empty_lines_in_values = False
1140
1141 def test_reading(self):
1142 smbconf = support.findfile("cfgparser.2")
1143 # check when we pass a mix of readable and non-readable files:
1144 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001145 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001146 self.assertEqual(parsed_files, [smbconf])
1147 sections = ['global', 'homes', 'printers',
1148 'print$', 'pdf-generator', 'tmp', 'Agustin']
1149 self.assertEqual(cf.sections(), sections)
1150 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1151 self.assertEqual(cf.getint("global", "max log size"), 50)
1152 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1153 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001154
Ezio Melottidc1fa802013-01-11 06:30:57 +02001155class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001156 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001157 interpolation = configparser.ExtendedInterpolation()
1158 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001159 strict = True
1160
1161 def fromstring(self, string, defaults=None, optionxform=None):
1162 cf = self.newconfig(defaults)
1163 if optionxform:
1164 cf.optionxform = optionxform
1165 cf.read_string(string)
1166 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001167
1168 def test_extended_interpolation(self):
1169 cf = self.fromstring(textwrap.dedent("""
1170 [common]
1171 favourite Beatle = Paul
1172 favourite color = green
1173
1174 [tom]
1175 favourite band = ${favourite color} day
1176 favourite pope = John ${favourite Beatle} II
1177 sequel = ${favourite pope}I
1178
1179 [ambv]
1180 favourite Beatle = George
1181 son of Edward VII = ${favourite Beatle} V
1182 son of George V = ${son of Edward VII}I
1183
1184 [stanley]
1185 favourite Beatle = ${ambv:favourite Beatle}
1186 favourite pope = ${tom:favourite pope}
1187 favourite color = black
1188 favourite state of mind = paranoid
1189 favourite movie = soylent ${common:favourite color}
1190 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1191 """).strip())
1192
1193 eq = self.assertEqual
1194 eq(cf['common']['favourite Beatle'], 'Paul')
1195 eq(cf['common']['favourite color'], 'green')
1196 eq(cf['tom']['favourite Beatle'], 'Paul')
1197 eq(cf['tom']['favourite color'], 'green')
1198 eq(cf['tom']['favourite band'], 'green day')
1199 eq(cf['tom']['favourite pope'], 'John Paul II')
1200 eq(cf['tom']['sequel'], 'John Paul III')
1201 eq(cf['ambv']['favourite Beatle'], 'George')
1202 eq(cf['ambv']['favourite color'], 'green')
1203 eq(cf['ambv']['son of Edward VII'], 'George V')
1204 eq(cf['ambv']['son of George V'], 'George VI')
1205 eq(cf['stanley']['favourite Beatle'], 'George')
1206 eq(cf['stanley']['favourite color'], 'black')
1207 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1208 eq(cf['stanley']['favourite movie'], 'soylent green')
1209 eq(cf['stanley']['favourite pope'], 'John Paul II')
1210 eq(cf['stanley']['favourite song'],
1211 'black sabbath - paranoid')
1212
1213 def test_endless_loop(self):
1214 cf = self.fromstring(textwrap.dedent("""
1215 [one for you]
1216 ping = ${one for me:pong}
1217
1218 [one for me]
1219 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001220
1221 [selfish]
1222 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001223 """).strip())
1224
1225 with self.assertRaises(configparser.InterpolationDepthError):
1226 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001227 with self.assertRaises(configparser.InterpolationDepthError):
1228 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001229
Łukasz Langa71b37a52010-12-17 21:56:32 +00001230 def test_strange_options(self):
1231 cf = self.fromstring("""
1232 [dollars]
1233 $var = $$value
1234 $var2 = ${$var}
1235 ${sick} = cannot interpolate me
1236
1237 [interpolated]
1238 $other = ${dollars:$var}
1239 $trying = ${dollars:${sick}}
1240 """)
1241
1242 self.assertEqual(cf['dollars']['$var'], '$value')
1243 self.assertEqual(cf['interpolated']['$other'], '$value')
1244 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1245 exception_class = configparser.InterpolationMissingOptionError
1246 with self.assertRaises(exception_class) as cm:
1247 cf['interpolated']['$trying']
1248 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001249 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001250
Łukasz Langae698cd52011-04-28 10:58:57 +02001251 def test_case_sensitivity_basic(self):
1252 ini = textwrap.dedent("""
1253 [common]
1254 optionlower = value
1255 OptionUpper = Value
1256
1257 [Common]
1258 optionlower = a better ${common:optionlower}
1259 OptionUpper = A Better ${common:OptionUpper}
1260
1261 [random]
1262 foolower = ${common:optionlower} redefined
1263 FooUpper = ${Common:OptionUpper} Redefined
1264 """).strip()
1265
1266 cf = self.fromstring(ini)
1267 eq = self.assertEqual
1268 eq(cf['common']['optionlower'], 'value')
1269 eq(cf['common']['OptionUpper'], 'Value')
1270 eq(cf['Common']['optionlower'], 'a better value')
1271 eq(cf['Common']['OptionUpper'], 'A Better Value')
1272 eq(cf['random']['foolower'], 'value redefined')
1273 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1274
1275 def test_case_sensitivity_conflicts(self):
1276 ini = textwrap.dedent("""
1277 [common]
1278 option = value
1279 Option = Value
1280
1281 [Common]
1282 option = a better ${common:option}
1283 Option = A Better ${common:Option}
1284
1285 [random]
1286 foo = ${common:option} redefined
1287 Foo = ${Common:Option} Redefined
1288 """).strip()
1289 with self.assertRaises(configparser.DuplicateOptionError):
1290 cf = self.fromstring(ini)
1291
1292 # raw options
1293 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1294 eq = self.assertEqual
1295 eq(cf['common']['option'], 'value')
1296 eq(cf['common']['Option'], 'Value')
1297 eq(cf['Common']['option'], 'a better value')
1298 eq(cf['Common']['Option'], 'A Better Value')
1299 eq(cf['random']['foo'], 'value redefined')
1300 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001301
1302 def test_other_errors(self):
1303 cf = self.fromstring("""
1304 [interpolation fail]
1305 case1 = ${where's the brace
1306 case2 = ${does_not_exist}
1307 case3 = ${wrong_section:wrong_value}
1308 case4 = ${i:like:colon:characters}
1309 case5 = $100 for Fail No 5!
1310 """)
1311
1312 with self.assertRaises(configparser.InterpolationSyntaxError):
1313 cf['interpolation fail']['case1']
1314 with self.assertRaises(configparser.InterpolationMissingOptionError):
1315 cf['interpolation fail']['case2']
1316 with self.assertRaises(configparser.InterpolationMissingOptionError):
1317 cf['interpolation fail']['case3']
1318 with self.assertRaises(configparser.InterpolationSyntaxError):
1319 cf['interpolation fail']['case4']
1320 with self.assertRaises(configparser.InterpolationSyntaxError):
1321 cf['interpolation fail']['case5']
1322 with self.assertRaises(ValueError):
1323 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001324
1325
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001326class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001327 allow_no_value = True
1328
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001329
Ezio Melottidc1fa802013-01-11 06:30:57 +02001330class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001331 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001332 delimiters = {'='}
1333 comment_prefixes = {'#'}
1334 allow_no_value = True
1335
1336 def test_cfgparser_dot_3(self):
1337 tricky = support.findfile("cfgparser.3")
1338 cf = self.newconfig()
1339 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1340 self.assertEqual(cf.sections(), ['strange',
1341 'corruption',
1342 'yeah, sections can be '
1343 'indented as well',
1344 'another one!',
1345 'no values here',
1346 'tricky interpolation',
1347 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001348 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001349 vars={'interpolate': '-1'}), -1)
1350 with self.assertRaises(ValueError):
1351 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001352 cf.getint(self.default_section, 'go', raw=True,
1353 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001354 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1355 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1356 longname = 'yeah, sections can be indented as well'
1357 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001358 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001359 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1360 # `go` from DEFAULT
1361 with self.assertRaises(configparser.InterpolationMissingOptionError):
1362 cf.items('no values here')
1363 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1364 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1365 cf.get('tricky interpolation', 'go'))
1366 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1367
1368 def test_unicode_failure(self):
1369 tricky = support.findfile("cfgparser.3")
1370 cf = self.newconfig()
1371 with self.assertRaises(UnicodeDecodeError):
1372 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001373
Fred Drake88444412010-09-03 04:22:36 +00001374
1375class Issue7005TestCase(unittest.TestCase):
1376 """Test output when None is set() as a value and allow_no_value == False.
1377
1378 http://bugs.python.org/issue7005
1379
1380 """
1381
1382 expected_output = "[section]\noption = None\n\n"
1383
1384 def prepare(self, config_class):
1385 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001386 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001387 cp.add_section("section")
1388 cp.set("section", "option", None)
1389 sio = io.StringIO()
1390 cp.write(sio)
1391 return sio.getvalue()
1392
1393 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001394 cp = configparser.ConfigParser(allow_no_value=False)
1395 cp.add_section("section")
1396 with self.assertRaises(TypeError):
1397 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001398
1399 def test_none_as_value_stringified_raw(self):
1400 output = self.prepare(configparser.RawConfigParser)
1401 self.assertEqual(output, self.expected_output)
1402
1403
Thomas Wouters89f507f2006-12-13 04:49:30 +00001404class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001405 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001406
1407 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001408 cf = self.fromstring("[b]\n"
1409 "o4=1\n"
1410 "o3=2\n"
1411 "o2=3\n"
1412 "o1=4\n"
1413 "[a]\n"
1414 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001415 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001416 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001417 self.assertEqual(output.getvalue(),
1418 "[a]\n"
1419 "k = v\n\n"
1420 "[b]\n"
1421 "o1 = 4\n"
1422 "o2 = 3\n"
1423 "o3 = 2\n"
1424 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001425
Fred Drake03c44a32010-02-19 06:08:41 +00001426
Ezio Melottidc1fa802013-01-11 06:30:57 +02001427class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001428 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001429 comment_prefixes = '#;'
1430 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001431
1432 def test_comment_handling(self):
1433 config_string = textwrap.dedent("""\
1434 [Commented Bar]
1435 baz=qwe ; a comment
1436 foo: bar # not a comment!
1437 # but this is a comment
1438 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001439 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001440 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001441 """)
1442 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001443 self.assertEqual(cf.get('Commented Bar', 'foo'),
1444 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001445 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001446 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1447 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001448
Ezio Melottidc1fa802013-01-11 06:30:57 +02001449class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001450 config_class = configparser.ConfigParser
1451
1452 def fromstring(self, string, defaults=None):
1453 cf = self.newconfig(defaults)
1454 cf.read_string(string)
1455 cf_copy = self.newconfig()
1456 cf_copy.read_dict(cf)
1457 # we have to clean up option duplicates that appeared because of
1458 # the magic DEFAULTSECT behaviour.
1459 for section in cf_copy.values():
1460 if section.name == self.default_section:
1461 continue
1462 for default, value in cf[self.default_section].items():
1463 if section[default] == value:
1464 del section[default]
1465 return cf_copy
1466
Łukasz Langadaab1c82011-04-27 18:10:05 +02001467
1468class FakeFile:
1469 def __init__(self):
1470 file_path = support.findfile("cfgparser.1")
1471 with open(file_path) as f:
1472 self.lines = f.readlines()
1473 self.lines.reverse()
1474
1475 def readline(self):
1476 if len(self.lines):
1477 return self.lines.pop()
1478 return ''
1479
1480
1481def readline_generator(f):
1482 """As advised in Doc/library/configparser.rst."""
1483 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001484 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001485 yield line
1486 line = f.readline()
1487
1488
1489class ReadFileTestCase(unittest.TestCase):
1490 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001491 file_paths = [support.findfile("cfgparser.1")]
1492 try:
1493 file_paths.append(file_paths[0].encode('utf8'))
1494 except UnicodeEncodeError:
1495 pass # unfortunately we can't test bytes on this path
1496 for file_path in file_paths:
1497 parser = configparser.ConfigParser()
1498 with open(file_path) as f:
1499 parser.read_file(f)
1500 self.assertIn("Foo Bar", parser)
1501 self.assertIn("foo", parser["Foo Bar"])
1502 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001503
1504 def test_iterable(self):
1505 lines = textwrap.dedent("""
1506 [Foo Bar]
1507 foo=newbar""").strip().split('\n')
1508 parser = configparser.ConfigParser()
1509 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001510 self.assertIn("Foo Bar", parser)
1511 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001512 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1513
1514 def test_readline_generator(self):
1515 """Issue #11670."""
1516 parser = configparser.ConfigParser()
1517 with self.assertRaises(TypeError):
1518 parser.read_file(FakeFile())
1519 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001520 self.assertIn("Foo Bar", parser)
1521 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001522 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1523
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001524 def test_source_as_bytes(self):
1525 """Issue #18260."""
1526 lines = textwrap.dedent("""
1527 [badbad]
1528 [badbad]""").strip().split('\n')
1529 parser = configparser.ConfigParser()
1530 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1531 parser.read_file(lines, source=b"badbad")
1532 self.assertEqual(
1533 str(dse.exception),
1534 "While reading from b'badbad' [line 2]: section 'badbad' "
1535 "already exists"
1536 )
1537 lines = textwrap.dedent("""
1538 [badbad]
1539 bad = bad
1540 bad = bad""").strip().split('\n')
1541 parser = configparser.ConfigParser()
1542 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1543 parser.read_file(lines, source=b"badbad")
1544 self.assertEqual(
1545 str(dse.exception),
1546 "While reading from b'badbad' [line 3]: option 'bad' in section "
1547 "'badbad' already exists"
1548 )
1549 lines = textwrap.dedent("""
1550 [badbad]
1551 = bad""").strip().split('\n')
1552 parser = configparser.ConfigParser()
1553 with self.assertRaises(configparser.ParsingError) as dse:
1554 parser.read_file(lines, source=b"badbad")
1555 self.assertEqual(
1556 str(dse.exception),
1557 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1558 )
1559 lines = textwrap.dedent("""
1560 [badbad
1561 bad = bad""").strip().split('\n')
1562 parser = configparser.ConfigParser()
1563 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1564 parser.read_file(lines, source=b"badbad")
1565 self.assertEqual(
1566 str(dse.exception),
1567 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1568 "'[badbad'"
1569 )
1570
Łukasz Langadaab1c82011-04-27 18:10:05 +02001571
Łukasz Langa71b37a52010-12-17 21:56:32 +00001572class CoverageOneHundredTestCase(unittest.TestCase):
1573 """Covers edge cases in the codebase."""
1574
1575 def test_duplicate_option_error(self):
1576 error = configparser.DuplicateOptionError('section', 'option')
1577 self.assertEqual(error.section, 'section')
1578 self.assertEqual(error.option, 'option')
1579 self.assertEqual(error.source, None)
1580 self.assertEqual(error.lineno, None)
1581 self.assertEqual(error.args, ('section', 'option', None, None))
1582 self.assertEqual(str(error), "Option 'option' in section 'section' "
1583 "already exists")
1584
1585 def test_interpolation_depth_error(self):
1586 error = configparser.InterpolationDepthError('option', 'section',
1587 'rawval')
1588 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1589 self.assertEqual(error.option, 'option')
1590 self.assertEqual(error.section, 'section')
1591
1592 def test_parsing_error(self):
1593 with self.assertRaises(ValueError) as cm:
1594 configparser.ParsingError()
1595 self.assertEqual(str(cm.exception), "Required argument `source' not "
1596 "given.")
1597 with self.assertRaises(ValueError) as cm:
1598 configparser.ParsingError(source='source', filename='filename')
1599 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1600 "and `source'. Use `source'.")
1601 error = configparser.ParsingError(filename='source')
1602 self.assertEqual(error.source, 'source')
1603 with warnings.catch_warnings(record=True) as w:
1604 warnings.simplefilter("always", DeprecationWarning)
1605 self.assertEqual(error.filename, 'source')
1606 error.filename = 'filename'
1607 self.assertEqual(error.source, 'filename')
1608 for warning in w:
1609 self.assertTrue(warning.category is DeprecationWarning)
1610
1611 def test_interpolation_validation(self):
1612 parser = configparser.ConfigParser()
1613 parser.read_string("""
1614 [section]
1615 invalid_percent = %
1616 invalid_reference = %(()
1617 invalid_variable = %(does_not_exist)s
1618 """)
1619 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1620 parser['section']['invalid_percent']
1621 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1622 "'(', found: '%'")
1623 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1624 parser['section']['invalid_reference']
1625 self.assertEqual(str(cm.exception), "bad interpolation variable "
1626 "reference '%(()'")
1627
1628 def test_readfp_deprecation(self):
1629 sio = io.StringIO("""
1630 [section]
1631 option = value
1632 """)
1633 parser = configparser.ConfigParser()
1634 with warnings.catch_warnings(record=True) as w:
1635 warnings.simplefilter("always", DeprecationWarning)
1636 parser.readfp(sio, filename='StringIO')
1637 for warning in w:
1638 self.assertTrue(warning.category is DeprecationWarning)
1639 self.assertEqual(len(parser), 2)
1640 self.assertEqual(parser['section']['option'], 'value')
1641
1642 def test_safeconfigparser_deprecation(self):
1643 with warnings.catch_warnings(record=True) as w:
1644 warnings.simplefilter("always", DeprecationWarning)
1645 parser = configparser.SafeConfigParser()
1646 for warning in w:
1647 self.assertTrue(warning.category is DeprecationWarning)
1648
1649 def test_sectionproxy_repr(self):
1650 parser = configparser.ConfigParser()
1651 parser.read_string("""
1652 [section]
1653 key = value
1654 """)
1655 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001656
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001657 def test_inconsistent_converters_state(self):
1658 parser = configparser.ConfigParser()
1659 import decimal
1660 parser.converters['decimal'] = decimal.Decimal
1661 parser.read_string("""
1662 [s1]
1663 one = 1
1664 [s2]
1665 two = 2
1666 """)
1667 self.assertIn('decimal', parser.converters)
1668 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1669 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1670 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1671 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1672 del parser.getdecimal
1673 with self.assertRaises(AttributeError):
1674 parser.getdecimal('s1', 'one')
1675 self.assertIn('decimal', parser.converters)
1676 del parser.converters['decimal']
1677 self.assertNotIn('decimal', parser.converters)
1678 with self.assertRaises(AttributeError):
1679 parser.getdecimal('s1', 'one')
1680 with self.assertRaises(AttributeError):
1681 parser['s1'].getdecimal('one')
1682 with self.assertRaises(AttributeError):
1683 parser['s2'].getdecimal('two')
1684
Łukasz Langae7851952012-01-20 14:57:55 +01001685
1686class ExceptionPicklingTestCase(unittest.TestCase):
1687 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1688
1689 def test_error(self):
1690 import pickle
1691 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001692 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1693 pickled = pickle.dumps(e1, proto)
1694 e2 = pickle.loads(pickled)
1695 self.assertEqual(e1.message, e2.message)
1696 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001697
1698 def test_nosectionerror(self):
1699 import pickle
1700 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001701 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1702 pickled = pickle.dumps(e1, proto)
1703 e2 = pickle.loads(pickled)
1704 self.assertEqual(e1.message, e2.message)
1705 self.assertEqual(e1.args, e2.args)
1706 self.assertEqual(e1.section, e2.section)
1707 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001708
1709 def test_nooptionerror(self):
1710 import pickle
1711 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001712 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1713 pickled = pickle.dumps(e1, proto)
1714 e2 = pickle.loads(pickled)
1715 self.assertEqual(e1.message, e2.message)
1716 self.assertEqual(e1.args, e2.args)
1717 self.assertEqual(e1.section, e2.section)
1718 self.assertEqual(e1.option, e2.option)
1719 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001720
1721 def test_duplicatesectionerror(self):
1722 import pickle
1723 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001724 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1725 pickled = pickle.dumps(e1, proto)
1726 e2 = pickle.loads(pickled)
1727 self.assertEqual(e1.message, e2.message)
1728 self.assertEqual(e1.args, e2.args)
1729 self.assertEqual(e1.section, e2.section)
1730 self.assertEqual(e1.source, e2.source)
1731 self.assertEqual(e1.lineno, e2.lineno)
1732 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001733
1734 def test_duplicateoptionerror(self):
1735 import pickle
1736 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1737 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001738 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1739 pickled = pickle.dumps(e1, proto)
1740 e2 = pickle.loads(pickled)
1741 self.assertEqual(e1.message, e2.message)
1742 self.assertEqual(e1.args, e2.args)
1743 self.assertEqual(e1.section, e2.section)
1744 self.assertEqual(e1.option, e2.option)
1745 self.assertEqual(e1.source, e2.source)
1746 self.assertEqual(e1.lineno, e2.lineno)
1747 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001748
1749 def test_interpolationerror(self):
1750 import pickle
1751 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001752 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1753 pickled = pickle.dumps(e1, proto)
1754 e2 = pickle.loads(pickled)
1755 self.assertEqual(e1.message, e2.message)
1756 self.assertEqual(e1.args, e2.args)
1757 self.assertEqual(e1.section, e2.section)
1758 self.assertEqual(e1.option, e2.option)
1759 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001760
1761 def test_interpolationmissingoptionerror(self):
1762 import pickle
1763 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1764 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001765 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1766 pickled = pickle.dumps(e1, proto)
1767 e2 = pickle.loads(pickled)
1768 self.assertEqual(e1.message, e2.message)
1769 self.assertEqual(e1.args, e2.args)
1770 self.assertEqual(e1.section, e2.section)
1771 self.assertEqual(e1.option, e2.option)
1772 self.assertEqual(e1.reference, e2.reference)
1773 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001774
1775 def test_interpolationsyntaxerror(self):
1776 import pickle
1777 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001778 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1779 pickled = pickle.dumps(e1, proto)
1780 e2 = pickle.loads(pickled)
1781 self.assertEqual(e1.message, e2.message)
1782 self.assertEqual(e1.args, e2.args)
1783 self.assertEqual(e1.section, e2.section)
1784 self.assertEqual(e1.option, e2.option)
1785 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001786
1787 def test_interpolationdeptherror(self):
1788 import pickle
1789 e1 = configparser.InterpolationDepthError('option', 'section',
1790 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001791 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1792 pickled = pickle.dumps(e1, proto)
1793 e2 = pickle.loads(pickled)
1794 self.assertEqual(e1.message, e2.message)
1795 self.assertEqual(e1.args, e2.args)
1796 self.assertEqual(e1.section, e2.section)
1797 self.assertEqual(e1.option, e2.option)
1798 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001799
1800 def test_parsingerror(self):
1801 import pickle
1802 e1 = configparser.ParsingError('source')
1803 e1.append(1, 'line1')
1804 e1.append(2, 'line2')
1805 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001806 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1807 pickled = pickle.dumps(e1, proto)
1808 e2 = pickle.loads(pickled)
1809 self.assertEqual(e1.message, e2.message)
1810 self.assertEqual(e1.args, e2.args)
1811 self.assertEqual(e1.source, e2.source)
1812 self.assertEqual(e1.errors, e2.errors)
1813 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001814 e1 = configparser.ParsingError(filename='filename')
1815 e1.append(1, 'line1')
1816 e1.append(2, 'line2')
1817 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001818 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1819 pickled = pickle.dumps(e1, proto)
1820 e2 = pickle.loads(pickled)
1821 self.assertEqual(e1.message, e2.message)
1822 self.assertEqual(e1.args, e2.args)
1823 self.assertEqual(e1.source, e2.source)
1824 self.assertEqual(e1.errors, e2.errors)
1825 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001826
1827 def test_missingsectionheadererror(self):
1828 import pickle
1829 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001830 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1831 pickled = pickle.dumps(e1, proto)
1832 e2 = pickle.loads(pickled)
1833 self.assertEqual(e1.message, e2.message)
1834 self.assertEqual(e1.args, e2.args)
1835 self.assertEqual(e1.line, e2.line)
1836 self.assertEqual(e1.source, e2.source)
1837 self.assertEqual(e1.lineno, e2.lineno)
1838 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001839
1840
Łukasz Langacba24322012-07-07 18:54:08 +02001841class InlineCommentStrippingTestCase(unittest.TestCase):
1842 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1843 delimiter occurs earlier without preceding space.."""
1844
1845 def test_stripping(self):
1846 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1847 '//'))
1848 cfg.read_string("""
1849 [section]
1850 k1 = v1;still v1
1851 k2 = v2 ;a comment
1852 k3 = v3 ; also a comment
1853 k4 = v4;still v4 ;a comment
1854 k5 = v5;still v5 ; also a comment
1855 k6 = v6;still v6; and still v6 ;a comment
1856 k7 = v7;still v7; and still v7 ; also a comment
1857
1858 [multiprefix]
1859 k1 = v1;still v1 #a comment ; yeah, pretty much
1860 k2 = v2 // this already is a comment ; continued
1861 k3 = v3;#//still v3# and still v3 ; a comment
1862 """)
1863 s = cfg['section']
1864 self.assertEqual(s['k1'], 'v1;still v1')
1865 self.assertEqual(s['k2'], 'v2')
1866 self.assertEqual(s['k3'], 'v3')
1867 self.assertEqual(s['k4'], 'v4;still v4')
1868 self.assertEqual(s['k5'], 'v5;still v5')
1869 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1870 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1871 s = cfg['multiprefix']
1872 self.assertEqual(s['k1'], 'v1;still v1')
1873 self.assertEqual(s['k2'], 'v2')
1874 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1875
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001876
Łukasz Langa949053b2014-09-04 01:36:33 -07001877class ExceptionContextTestCase(unittest.TestCase):
1878 """ Test that implementation details doesn't leak
1879 through raising exceptions. """
1880
1881 def test_get_basic_interpolation(self):
1882 parser = configparser.ConfigParser()
1883 parser.read_string("""
1884 [Paths]
1885 home_dir: /Users
1886 my_dir: %(home_dir1)s/lumberjack
1887 my_pictures: %(my_dir)s/Pictures
1888 """)
1889 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1890 with cm:
1891 parser.get('Paths', 'my_dir')
1892 self.assertIs(cm.exception.__suppress_context__, True)
1893
1894 def test_get_extended_interpolation(self):
1895 parser = configparser.ConfigParser(
1896 interpolation=configparser.ExtendedInterpolation())
1897 parser.read_string("""
1898 [Paths]
1899 home_dir: /Users
1900 my_dir: ${home_dir1}/lumberjack
1901 my_pictures: ${my_dir}/Pictures
1902 """)
1903 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1904 with cm:
1905 parser.get('Paths', 'my_dir')
1906 self.assertIs(cm.exception.__suppress_context__, True)
1907
1908 def test_missing_options(self):
1909 parser = configparser.ConfigParser()
1910 parser.read_string("""
1911 [Paths]
1912 home_dir: /Users
1913 """)
1914 with self.assertRaises(configparser.NoSectionError) as cm:
1915 parser.options('test')
1916 self.assertIs(cm.exception.__suppress_context__, True)
1917
1918 def test_missing_section(self):
1919 config = configparser.ConfigParser()
1920 with self.assertRaises(configparser.NoSectionError) as cm:
1921 config.set('Section1', 'an_int', '15')
1922 self.assertIs(cm.exception.__suppress_context__, True)
1923
1924 def test_remove_option(self):
1925 config = configparser.ConfigParser()
1926 with self.assertRaises(configparser.NoSectionError) as cm:
1927 config.remove_option('Section1', 'an_int')
1928 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001929
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001930
1931class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1932 """Introduced in 3.5, issue #18159."""
1933
1934 config_class = configparser.ConfigParser
1935
1936 def newconfig(self, defaults=None):
1937 instance = super().newconfig(defaults=defaults)
1938 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1939 if e.strip()]
1940 return instance
1941
1942 def test_converters(self):
1943 cfg = self.newconfig()
1944 self.assertIn('boolean', cfg.converters)
1945 self.assertIn('list', cfg.converters)
1946 self.assertIsNone(cfg.converters['int'])
1947 self.assertIsNone(cfg.converters['float'])
1948 self.assertIsNone(cfg.converters['boolean'])
1949 self.assertIsNotNone(cfg.converters['list'])
1950 self.assertEqual(len(cfg.converters), 4)
1951 with self.assertRaises(ValueError):
1952 cfg.converters[''] = lambda v: v
1953 with self.assertRaises(ValueError):
1954 cfg.converters[None] = lambda v: v
1955 cfg.read_string("""
1956 [s]
1957 str = string
1958 int = 1
1959 float = 0.5
1960 list = a b c d e f g
1961 bool = yes
1962 """)
1963 s = cfg['s']
1964 self.assertEqual(s['str'], 'string')
1965 self.assertEqual(s['int'], '1')
1966 self.assertEqual(s['float'], '0.5')
1967 self.assertEqual(s['list'], 'a b c d e f g')
1968 self.assertEqual(s['bool'], 'yes')
1969 self.assertEqual(cfg.get('s', 'str'), 'string')
1970 self.assertEqual(cfg.get('s', 'int'), '1')
1971 self.assertEqual(cfg.get('s', 'float'), '0.5')
1972 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1973 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1974 self.assertEqual(cfg.get('s', 'str'), 'string')
1975 self.assertEqual(cfg.getint('s', 'int'), 1)
1976 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1977 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1978 'e', 'f', 'g'])
1979 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1980 self.assertEqual(s.get('str'), 'string')
1981 self.assertEqual(s.getint('int'), 1)
1982 self.assertEqual(s.getfloat('float'), 0.5)
1983 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1984 'e', 'f', 'g'])
1985 self.assertEqual(s.getboolean('bool'), True)
1986 with self.assertRaises(AttributeError):
1987 cfg.getdecimal('s', 'float')
1988 with self.assertRaises(AttributeError):
1989 s.getdecimal('float')
1990 import decimal
1991 cfg.converters['decimal'] = decimal.Decimal
1992 self.assertIn('decimal', cfg.converters)
1993 self.assertIsNotNone(cfg.converters['decimal'])
1994 self.assertEqual(len(cfg.converters), 5)
1995 dec0_5 = decimal.Decimal('0.5')
1996 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1997 self.assertEqual(s.getdecimal('float'), dec0_5)
1998 del cfg.converters['decimal']
1999 self.assertNotIn('decimal', cfg.converters)
2000 self.assertEqual(len(cfg.converters), 4)
2001 with self.assertRaises(AttributeError):
2002 cfg.getdecimal('s', 'float')
2003 with self.assertRaises(AttributeError):
2004 s.getdecimal('float')
2005 with self.assertRaises(KeyError):
2006 del cfg.converters['decimal']
2007 with self.assertRaises(KeyError):
2008 del cfg.converters['']
2009 with self.assertRaises(KeyError):
2010 del cfg.converters[None]
2011
2012
2013class BlatantOverrideConvertersTestCase(unittest.TestCase):
2014 """What if somebody overrode a getboolean()? We want to make sure that in
2015 this case the automatic converters do not kick in."""
2016
2017 config = """
2018 [one]
2019 one = false
2020 two = false
2021 three = long story short
2022
2023 [two]
2024 one = false
2025 two = false
2026 three = four
2027 """
2028
2029 def test_converters_at_init(self):
2030 cfg = configparser.ConfigParser(converters={'len': len})
2031 cfg.read_string(self.config)
2032 self._test_len(cfg)
2033 self.assertIsNotNone(cfg.converters['len'])
2034
2035 def test_inheritance(self):
2036 class StrangeConfigParser(configparser.ConfigParser):
2037 gettysburg = 'a historic borough in south central Pennsylvania'
2038
2039 def getboolean(self, section, option, *, raw=False, vars=None,
2040 fallback=configparser._UNSET):
2041 if section == option:
2042 return True
2043 return super().getboolean(section, option, raw=raw, vars=vars,
2044 fallback=fallback)
2045 def getlen(self, section, option, *, raw=False, vars=None,
2046 fallback=configparser._UNSET):
2047 return self._get_conv(section, option, len, raw=raw, vars=vars,
2048 fallback=fallback)
2049
2050 cfg = StrangeConfigParser()
2051 cfg.read_string(self.config)
2052 self._test_len(cfg)
2053 self.assertIsNone(cfg.converters['len'])
2054 self.assertTrue(cfg.getboolean('one', 'one'))
2055 self.assertTrue(cfg.getboolean('two', 'two'))
2056 self.assertFalse(cfg.getboolean('one', 'two'))
2057 self.assertFalse(cfg.getboolean('two', 'one'))
2058 cfg.converters['boolean'] = cfg._convert_to_boolean
2059 self.assertFalse(cfg.getboolean('one', 'one'))
2060 self.assertFalse(cfg.getboolean('two', 'two'))
2061 self.assertFalse(cfg.getboolean('one', 'two'))
2062 self.assertFalse(cfg.getboolean('two', 'one'))
2063
2064 def _test_len(self, cfg):
2065 self.assertEqual(len(cfg.converters), 4)
2066 self.assertIn('boolean', cfg.converters)
2067 self.assertIn('len', cfg.converters)
2068 self.assertNotIn('tysburg', cfg.converters)
2069 self.assertIsNone(cfg.converters['int'])
2070 self.assertIsNone(cfg.converters['float'])
2071 self.assertIsNone(cfg.converters['boolean'])
2072 self.assertEqual(cfg.getlen('one', 'one'), 5)
2073 self.assertEqual(cfg.getlen('one', 'two'), 5)
2074 self.assertEqual(cfg.getlen('one', 'three'), 16)
2075 self.assertEqual(cfg.getlen('two', 'one'), 5)
2076 self.assertEqual(cfg.getlen('two', 'two'), 5)
2077 self.assertEqual(cfg.getlen('two', 'three'), 4)
2078 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2079 with self.assertRaises(configparser.NoOptionError):
2080 cfg.getlen('two', 'four')
2081 self.assertEqual(cfg['one'].getlen('one'), 5)
2082 self.assertEqual(cfg['one'].getlen('two'), 5)
2083 self.assertEqual(cfg['one'].getlen('three'), 16)
2084 self.assertEqual(cfg['two'].getlen('one'), 5)
2085 self.assertEqual(cfg['two'].getlen('two'), 5)
2086 self.assertEqual(cfg['two'].getlen('three'), 4)
2087 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2088 self.assertEqual(cfg['two'].getlen('four'), None)
2089
2090 def test_instance_assignment(self):
2091 cfg = configparser.ConfigParser()
2092 cfg.getboolean = lambda section, option: True
2093 cfg.getlen = lambda section, option: len(cfg[section][option])
2094 cfg.read_string(self.config)
2095 self.assertEqual(len(cfg.converters), 3)
2096 self.assertIn('boolean', cfg.converters)
2097 self.assertNotIn('len', cfg.converters)
2098 self.assertIsNone(cfg.converters['int'])
2099 self.assertIsNone(cfg.converters['float'])
2100 self.assertIsNone(cfg.converters['boolean'])
2101 self.assertTrue(cfg.getboolean('one', 'one'))
2102 self.assertTrue(cfg.getboolean('two', 'two'))
2103 self.assertTrue(cfg.getboolean('one', 'two'))
2104 self.assertTrue(cfg.getboolean('two', 'one'))
2105 cfg.converters['boolean'] = cfg._convert_to_boolean
2106 self.assertFalse(cfg.getboolean('one', 'one'))
2107 self.assertFalse(cfg.getboolean('two', 'two'))
2108 self.assertFalse(cfg.getboolean('one', 'two'))
2109 self.assertFalse(cfg.getboolean('two', 'one'))
2110 self.assertEqual(cfg.getlen('one', 'one'), 5)
2111 self.assertEqual(cfg.getlen('one', 'two'), 5)
2112 self.assertEqual(cfg.getlen('one', 'three'), 16)
2113 self.assertEqual(cfg.getlen('two', 'one'), 5)
2114 self.assertEqual(cfg.getlen('two', 'two'), 5)
2115 self.assertEqual(cfg.getlen('two', 'three'), 4)
2116 # If a getter impl is assigned straight to the instance, it won't
2117 # be available on the section proxies.
2118 with self.assertRaises(AttributeError):
2119 self.assertEqual(cfg['one'].getlen('one'), 5)
2120 with self.assertRaises(AttributeError):
2121 self.assertEqual(cfg['two'].getlen('one'), 5)
2122
2123
Martin Panter2b9b70b2016-09-09 06:46:48 +00002124class MiscTestCase(unittest.TestCase):
2125 def test__all__(self):
2126 blacklist = {"Error"}
2127 support.check__all__(self, configparser, blacklist=blacklist)
2128
2129
Ezio Melottidc1fa802013-01-11 06:30:57 +02002130if __name__ == '__main__':
2131 unittest.main()