blob: 9373a62072ef47f5df515e871354ad7b56e5cdd8 [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
Hai Shia7f5d932020-08-04 00:41:24 +080011from test.support import os_helper
Fred Drake3d5f7e82000-12-04 16:30:40 +000012
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080013
Raymond Hettingerf80680d2008-02-06 00:07:11 +000014class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000015
Thomas Wouters89f507f2006-12-13 04:49:30 +000016 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000017 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000018
19 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000020 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000021
Thomas Wouters89f507f2006-12-13 04:49:30 +000022 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000023 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000024
Łukasz Langae698cd52011-04-28 10:58:57 +020025 def iteritems(self):
26 return iter(self.items())
27
28 def iterkeys(self):
29 return iter(self.keys())
30
31 def itervalues(self):
32 return iter(self.values())
33
Thomas Wouters89f507f2006-12-13 04:49:30 +000034 __iter__ = iterkeys
Fred Drake3d5f7e82000-12-04 16:30:40 +000035
Fred Drake03c44a32010-02-19 06:08:41 +000036
Ezio Melottidc1fa802013-01-11 06:30:57 +020037class CfgParserTestCaseClass:
Fred Drake03c44a32010-02-19 06:08:41 +000038 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000039 delimiters = ('=', ':')
40 comment_prefixes = (';', '#')
Łukasz Langab25a7912010-12-17 01:32:29 +000041 inline_comment_prefixes = (';', '#')
Georg Brandl96a60ae2010-07-28 13:13:46 +000042 empty_lines_in_values = True
43 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000044 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000045 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000046 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000047
Fred Drakec6f28912002-10-25 19:40:49 +000048 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000049 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000050 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000051 allow_no_value=self.allow_no_value,
52 delimiters=self.delimiters,
53 comment_prefixes=self.comment_prefixes,
Łukasz Langab25a7912010-12-17 01:32:29 +000054 inline_comment_prefixes=self.inline_comment_prefixes,
Georg Brandl96a60ae2010-07-28 13:13:46 +000055 empty_lines_in_values=self.empty_lines_in_values,
56 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000057 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000058 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000059 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000060 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000061 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000062 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000063
Fred Drakec6f28912002-10-25 19:40:49 +000064 def fromstring(self, string, defaults=None):
65 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000066 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000067 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000068
Łukasz Langa47a9a4b2016-11-26 14:00:39 -080069
Georg Brandl96a60ae2010-07-28 13:13:46 +000070class BasicTestCase(CfgParserTestCaseClass):
71
Fred Drakea4923622010-08-09 12:52:45 +000072 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000073 E = ['Commented Bar',
74 'Foo Bar',
75 'Internationalized Stuff',
76 'Long Line',
77 'Section\\with$weird%characters[\t',
78 'Spaces',
79 'Spacey Bar',
80 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000081 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000082 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000083
Fred Drake03c44a32010-02-19 06:08:41 +000084 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000085 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000086 E.sort()
Łukasz Langa71b37a52010-12-17 21:56:32 +000087 F = [('baz', 'qwe'), ('foo', 'bar3')]
Łukasz Langa26d513c2010-11-10 18:57:39 +000088
89 # API access
90 L = cf.sections()
91 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000092 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000093 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +000094 L = cf.items('Spacey Bar From The Beginning')
95 L.sort()
96 eq(L, F)
Fred Drake8ef67672000-09-27 22:45:25 +000097
Łukasz Langa26d513c2010-11-10 18:57:39 +000098 # mapping access
99 L = [section for section in cf]
100 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +0000101 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000102 E.sort()
103 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000104 L = cf['Spacey Bar From The Beginning'].items()
105 L = sorted(list(L))
106 eq(L, F)
107 L = cf.items()
108 L = sorted(list(L))
109 self.assertEqual(len(L), len(E))
110 for name, section in L:
111 eq(name, section.name)
112 eq(cf.defaults(), cf[self.default_section])
Łukasz Langa26d513c2010-11-10 18:57:39 +0000113
Fred Drakec6f28912002-10-25 19:40:49 +0000114 # The use of spaces in the section names serves as a
115 # regression test for SourceForge bug #583248:
116 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +0000117
118 # API access
119 eq(cf.get('Foo Bar', 'foo'), 'bar1')
120 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
121 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000122 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000123 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000124 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000125 eq(cf.get('Spaces', 'key with spaces'), 'value')
126 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000127 eq(cf.getint('Types', 'int'), 42)
128 eq(cf.get('Types', 'int'), "42")
129 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
130 eq(cf.get('Types', 'float'), "0.44")
131 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000132 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000133 if self.allow_no_value:
134 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000135
Łukasz Langa26d513c2010-11-10 18:57:39 +0000136 # test vars= and fallback=
137 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000138 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
139 with self.assertRaises(configparser.NoSectionError):
140 cf.get('No Such Foo Bar', 'foo')
141 with self.assertRaises(configparser.NoOptionError):
142 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000143 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
144 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
145 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
146 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
147 eq(cf.getint('Types', 'int', fallback=18), 42)
148 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
149 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000150 with self.assertRaises(configparser.NoOptionError):
151 cf.getint('Types', 'no-such-int')
Fred Drakecc645b92010-09-04 04:35:34 +0000152 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000153 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000154 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000155 fallback=0.0), 0.0)
156 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000157 with self.assertRaises(configparser.NoOptionError):
158 cf.getfloat('Types', 'no-such-float')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000159 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
160 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000161 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000162 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000163 with self.assertRaises(configparser.NoOptionError):
164 cf.getboolean('Types', 'no-such-boolean')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000165 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000166 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000167 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000168 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000169 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000170
Łukasz Langa26d513c2010-11-10 18:57:39 +0000171 # mapping access
172 eq(cf['Foo Bar']['foo'], 'bar1')
173 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000174 section = cf['Spacey Bar From The Beginning']
175 eq(section.name, 'Spacey Bar From The Beginning')
176 self.assertIs(section.parser, cf)
177 with self.assertRaises(AttributeError):
178 section.name = 'Name is read-only'
179 with self.assertRaises(AttributeError):
180 section.parser = 'Parser is read-only'
181 eq(section['foo'], 'bar3')
182 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000183 eq(cf['Commented Bar']['foo'], 'bar4')
184 eq(cf['Commented Bar']['baz'], 'qwe')
185 eq(cf['Spaces']['key with spaces'], 'value')
186 eq(cf['Spaces']['another with spaces'], 'splat!')
187 eq(cf['Long Line']['foo'],
188 'this line is much, much longer than my editor\nlikes it.')
189 if self.allow_no_value:
190 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000191 # test vars= and fallback=
192 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
193 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
194 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
195 with self.assertRaises(KeyError):
196 cf['No Such Foo Bar']['foo']
197 with self.assertRaises(KeyError):
198 cf['Foo Bar']['no-such-foo']
199 with self.assertRaises(KeyError):
200 cf['No Such Foo Bar'].get('foo', fallback='baz')
201 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
202 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
Łukasz Langa71b37a52010-12-17 21:56:32 +0000203 eq(cf['Foo Bar'].get('no-such-foo'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000204 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
205 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
206 with self.assertRaises(KeyError):
207 cf['No Such Spacey Bar'].get('foo', None)
208 eq(cf['Types'].getint('int', 18), 42)
209 eq(cf['Types'].getint('int', fallback=18), 42)
210 eq(cf['Types'].getint('no-such-int', 18), 18)
211 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
212 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
213 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000214 eq(cf['Types'].getint('no-such-int'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000215 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
216 self.assertAlmostEqual(cf['Types'].getfloat('float',
217 fallback=0.0), 0.44)
218 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
219 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
220 fallback=0.0), 0.0)
221 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
222 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000223 eq(cf['Types'].getfloat('no-such-float'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000224 eq(cf['Types'].getboolean('boolean', True), False)
225 eq(cf['Types'].getboolean('boolean', fallback=True), False)
226 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
227 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
228 "yes") # sic!
229 eq(cf['Types'].getboolean('no-such-boolean', True), True)
230 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000231 eq(cf['Types'].getboolean('no-such-boolean'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000232 if self.allow_no_value:
233 eq(cf['NoValue'].get('option-without-value', False), None)
234 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
235 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
236 eq(cf['NoValue'].get('no-such-option-without-value',
237 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000238
Łukasz Langa71b37a52010-12-17 21:56:32 +0000239 # Make sure the right things happen for remove_section() and
240 # remove_option(); added to include check for SourceForge bug #123324.
Łukasz Langa26d513c2010-11-10 18:57:39 +0000241
Łukasz Langa71b37a52010-12-17 21:56:32 +0000242 cf[self.default_section]['this_value'] = '1'
243 cf[self.default_section]['that_value'] = '2'
244
245 # API access
246 self.assertTrue(cf.remove_section('Spaces'))
247 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
248 self.assertFalse(cf.remove_section('Spaces'))
249 self.assertFalse(cf.remove_section(self.default_section))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000250 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000251 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000252 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000253 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000254 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000255 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000256 " that was removed")
Łukasz Langa71b37a52010-12-17 21:56:32 +0000257 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
258 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
259 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
260 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
261 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
Fred Drakec6f28912002-10-25 19:40:49 +0000262
Michael Foordbd6c0792010-07-25 23:09:25 +0000263 with self.assertRaises(configparser.NoSectionError) as cm:
264 cf.remove_option('No Such Section', 'foo')
265 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000266
267 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000268 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000269
Łukasz Langa26d513c2010-11-10 18:57:39 +0000270 # mapping access
Łukasz Langa71b37a52010-12-17 21:56:32 +0000271 del cf['Types']
272 self.assertFalse('Types' in cf)
273 with self.assertRaises(KeyError):
274 del cf['Types']
275 with self.assertRaises(ValueError):
276 del cf[self.default_section]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000277 del cf['Spacey Bar']['foo']
278 self.assertFalse('foo' in cf['Spacey Bar'])
279 with self.assertRaises(KeyError):
280 del cf['Spacey Bar']['foo']
Łukasz Langa71b37a52010-12-17 21:56:32 +0000281 self.assertTrue('that_value' in cf['Spacey Bar'])
282 with self.assertRaises(KeyError):
283 del cf['Spacey Bar']['that_value']
284 del cf[self.default_section]['that_value']
285 self.assertFalse('that_value' in cf['Spacey Bar'])
286 with self.assertRaises(KeyError):
287 del cf[self.default_section]['that_value']
Łukasz Langa26d513c2010-11-10 18:57:39 +0000288 with self.assertRaises(KeyError):
289 del cf['No Such Section']['foo']
290
Łukasz Langa71b37a52010-12-17 21:56:32 +0000291 # Don't add new asserts below in this method as most of the options
292 # and sections are now removed.
293
Fred Drakea4923622010-08-09 12:52:45 +0000294 def test_basic(self):
295 config_string = """\
296[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000297foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000298[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000299foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000300[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000301 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000302 baz {0[0]} qwe
303[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000304foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000305baz{0[0]}qwe {1[0]}another one
306[Long Line]
307foo{0[1]} this line is much, much longer than my editor
308 likes it.
309[Section\\with$weird%characters[\t]
310[Internationalized Stuff]
311foo[bg]{0[1]} Bulgarian
312foo{0[0]}Default
313foo[en]{0[0]}English
314foo[de]{0[0]}Deutsch
315[Spaces]
316key with spaces {0[1]} value
317another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000318[Types]
319int {0[1]} 42
320float {0[0]} 0.44
321boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000322123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000323""".format(self.delimiters, self.comment_prefixes)
324 if self.allow_no_value:
325 config_string += (
326 "[NoValue]\n"
327 "option-without-value\n"
328 )
329 cf = self.fromstring(config_string)
330 self.basic_test(cf)
331 if self.strict:
332 with self.assertRaises(configparser.DuplicateOptionError):
333 cf.read_string(textwrap.dedent("""\
334 [Duplicate Options Here]
335 option {0[0]} with a value
336 option {0[1]} with another value
337 """.format(self.delimiters)))
338 with self.assertRaises(configparser.DuplicateSectionError):
339 cf.read_string(textwrap.dedent("""\
340 [And Now For Something]
341 completely different {0[0]} True
342 [And Now For Something]
343 the larch {0[1]} 1
344 """.format(self.delimiters)))
345 else:
346 cf.read_string(textwrap.dedent("""\
347 [Duplicate Options Here]
348 option {0[0]} with a value
349 option {0[1]} with another value
350 """.format(self.delimiters)))
351
352 cf.read_string(textwrap.dedent("""\
353 [And Now For Something]
354 completely different {0[0]} True
355 [And Now For Something]
356 the larch {0[1]} 1
357 """.format(self.delimiters)))
358
359 def test_basic_from_dict(self):
360 config = {
361 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000362 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000363 },
364 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000365 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000366 },
367 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000368 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000369 "baz": "qwe",
370 },
371 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000372 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000373 "baz": "qwe",
374 },
375 "Long Line": {
376 "foo": "this line is much, much longer than my editor\nlikes "
377 "it.",
378 },
379 "Section\\with$weird%characters[\t": {
380 },
381 "Internationalized Stuff": {
382 "foo[bg]": "Bulgarian",
383 "foo": "Default",
384 "foo[en]": "English",
385 "foo[de]": "Deutsch",
386 },
387 "Spaces": {
388 "key with spaces": "value",
389 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000390 },
391 "Types": {
392 "int": 42,
393 "float": 0.44,
394 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000395 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000396 },
Fred Drakea4923622010-08-09 12:52:45 +0000397 }
398 if self.allow_no_value:
399 config.update({
400 "NoValue": {
401 "option-without-value": None,
402 }
403 })
404 cf = self.newconfig()
405 cf.read_dict(config)
406 self.basic_test(cf)
407 if self.strict:
Łukasz Langa71b37a52010-12-17 21:56:32 +0000408 with self.assertRaises(configparser.DuplicateSectionError):
409 cf.read_dict({
410 '1': {'key': 'value'},
411 1: {'key2': 'value2'},
412 })
Fred Drakea4923622010-08-09 12:52:45 +0000413 with self.assertRaises(configparser.DuplicateOptionError):
414 cf.read_dict({
415 "Duplicate Options Here": {
416 'option': 'with a value',
417 'OPTION': 'with another value',
418 },
419 })
420 else:
421 cf.read_dict({
Łukasz Langa71b37a52010-12-17 21:56:32 +0000422 'section': {'key': 'value'},
423 'SECTION': {'key2': 'value2'},
424 })
425 cf.read_dict({
Fred Drakea4923622010-08-09 12:52:45 +0000426 "Duplicate Options Here": {
427 'option': 'with a value',
428 'OPTION': 'with another value',
429 },
430 })
431
Fred Drakec6f28912002-10-25 19:40:49 +0000432 def test_case_sensitivity(self):
433 cf = self.newconfig()
434 cf.add_section("A")
435 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000436 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000437 L = cf.sections()
438 L.sort()
439 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000440 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000441 cf.set("a", "B", "value")
442 eq(cf.options("a"), ["b"])
443 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000444 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000445 with self.assertRaises(configparser.NoSectionError):
446 # section names are case-sensitive
447 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000448 self.assertTrue(cf.has_option("a", "b"))
Łukasz Langa71b37a52010-12-17 21:56:32 +0000449 self.assertFalse(cf.has_option("b", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000450 cf.set("A", "A-B", "A-B value")
451 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000452 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000453 cf.has_option("A", opt),
454 "has_option() returned false for option which should exist")
455 eq(cf.options("A"), ["a-b"])
456 eq(cf.options("a"), ["b"])
457 cf.remove_option("a", "B")
458 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000459
Fred Drakec6f28912002-10-25 19:40:49 +0000460 # SF bug #432369:
461 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000462 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000463 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000464 eq(cf.options("MySection"), ["option"])
465 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000466
Fred Drakec6f28912002-10-25 19:40:49 +0000467 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000468 cf = self.fromstring("[section]\n"
469 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000470 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000471 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000472
Fred Drake3c823aa2001-02-26 21:55:34 +0000473
Łukasz Langa26d513c2010-11-10 18:57:39 +0000474 def test_case_sensitivity_mapping_access(self):
475 cf = self.newconfig()
476 cf["A"] = {}
477 cf["a"] = {"B": "value"}
478 cf["B"] = {}
479 L = [section for section in cf]
480 L.sort()
481 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000482 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000483 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000484 eq(cf["a"].keys(), {"b"})
485 eq(cf["a"]["b"], "value",
486 "could not locate option, expecting case-insensitive option names")
487 with self.assertRaises(KeyError):
488 # section names are case-sensitive
489 cf["b"]["A"] = "value"
490 self.assertTrue("b" in cf["a"])
491 cf["A"]["A-B"] = "A-B value"
492 for opt in ("a-b", "A-b", "a-B", "A-B"):
493 self.assertTrue(
494 opt in cf["A"],
495 "has_option() returned false for option which should exist")
496 eq(cf["A"].keys(), {"a-b"})
497 eq(cf["a"].keys(), {"b"})
498 del cf["a"]["B"]
499 elem_eq(cf["a"].keys(), {})
500
501 # SF bug #432369:
502 cf = self.fromstring(
503 "[MySection]\nOption{} first line \n\tsecond line \n".format(
504 self.delimiters[0]))
505 eq(cf["MySection"].keys(), {"option"})
506 eq(cf["MySection"]["Option"], "first line\nsecond line")
507
508 # SF bug #561822:
509 cf = self.fromstring("[section]\n"
510 "nekey{}nevalue\n".format(self.delimiters[0]),
511 defaults={"key":"value"})
512 self.assertTrue("Key" in cf["section"])
513
David Goodger68a1abd2004-10-03 15:40:25 +0000514 def test_default_case_sensitivity(self):
515 cf = self.newconfig({"foo": "Bar"})
516 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000517 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000518 "could not locate option, expecting case-insensitive option names")
519 cf = self.newconfig({"Foo": "Bar"})
520 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000521 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000522 "could not locate option, expecting case-insensitive defaults")
523
Fred Drakec6f28912002-10-25 19:40:49 +0000524 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000525 cf = self.newconfig()
526 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000527 "[Foo]\n"
528 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000529 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000530 "[Foo]\n"
531 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000532 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000533 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000534 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000535 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000536 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000537 "[Foo]\n wrong-indent\n")
538 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000539 # read_file on a real file
540 tricky = support.findfile("cfgparser.3")
541 if self.delimiters[0] == '=':
542 error = configparser.ParsingError
543 expected = (tricky,)
544 else:
545 error = configparser.MissingSectionHeaderError
546 expected = (tricky, 1,
547 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000548 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000549 e = self.parse_error(cf, error, f)
550 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000551
Fred Drakea4923622010-08-09 12:52:45 +0000552 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000553 if hasattr(src, 'readline'):
554 sio = src
555 else:
556 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000557 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000558 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000559 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000560
Fred Drakec6f28912002-10-25 19:40:49 +0000561 def test_query_errors(self):
562 cf = self.newconfig()
563 self.assertEqual(cf.sections(), [],
564 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000565 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000566 "new ConfigParser should have no acknowledged "
567 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000568 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000569 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000570 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000571 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000572 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000573 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000574 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000575 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000576 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000577
Fred Drakea4923622010-08-09 12:52:45 +0000578 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000579 try:
Fred Drakea4923622010-08-09 12:52:45 +0000580 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000581 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000582 return e
583 else:
584 self.fail("expected exception type %s.%s"
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300585 % (exc.__module__, exc.__qualname__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000586
Fred Drakec6f28912002-10-25 19:40:49 +0000587 def test_boolean(self):
588 cf = self.fromstring(
589 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000590 "T1{equals}1\n"
591 "T2{equals}TRUE\n"
592 "T3{equals}True\n"
593 "T4{equals}oN\n"
594 "T5{equals}yes\n"
595 "F1{equals}0\n"
596 "F2{equals}FALSE\n"
597 "F3{equals}False\n"
598 "F4{equals}oFF\n"
599 "F5{equals}nO\n"
600 "E1{equals}2\n"
601 "E2{equals}foo\n"
602 "E3{equals}-1\n"
603 "E4{equals}0.1\n"
604 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000605 )
606 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000607 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
608 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000609 self.assertRaises(ValueError,
610 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000611
Fred Drakec6f28912002-10-25 19:40:49 +0000612 def test_weird_errors(self):
613 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000614 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000615 with self.assertRaises(configparser.DuplicateSectionError) as cm:
616 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000617 e = cm.exception
618 self.assertEqual(str(e), "Section 'Foo' already exists")
619 self.assertEqual(e.args, ("Foo", None, None))
620
621 if self.strict:
622 with self.assertRaises(configparser.DuplicateSectionError) as cm:
623 cf.read_string(textwrap.dedent("""\
624 [Foo]
625 will this be added{equals}True
626 [Bar]
627 what about this{equals}True
628 [Foo]
629 oops{equals}this won't
630 """.format(equals=self.delimiters[0])), source='<foo-bar>')
631 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200632 self.assertEqual(str(e), "While reading from '<foo-bar>' "
633 "[line 5]: section 'Foo' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000634 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
635
636 with self.assertRaises(configparser.DuplicateOptionError) as cm:
637 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
638 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200639 self.assertEqual(str(e), "While reading from '<dict>': option "
640 "'opt' in section 'Bar' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000641 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000642
643 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000644 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000645 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000646 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000647 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000648 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000649 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000650 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000651 "[Long Line - With Comments!]\n"
652 "test {0[1]} we {comment} can\n"
653 " also {comment} place\n"
654 " comments {comment} in\n"
655 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000656 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
657 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000658 )
Fred Drake03c44a32010-02-19 06:08:41 +0000659 if self.allow_no_value:
660 config_string += (
661 "[Valueless]\n"
662 "option-without-value\n"
663 )
664
665 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000666 for space_around_delimiters in (True, False):
667 output = io.StringIO()
668 cf.write(output, space_around_delimiters=space_around_delimiters)
669 delimiter = self.delimiters[0]
670 if space_around_delimiters:
671 delimiter = " {} ".format(delimiter)
672 expect_string = (
673 "[{default_section}]\n"
674 "foo{equals}another very\n"
675 "\tlong line\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000676 "\n"
Łukasz Langa71b37a52010-12-17 21:56:32 +0000677 "[Long Line]\n"
678 "foo{equals}this line is much, much longer than my editor\n"
679 "\tlikes it.\n"
680 "\n"
681 "[Long Line - With Comments!]\n"
682 "test{equals}we\n"
683 "\talso\n"
684 "\tcomments\n"
685 "\tmultiline\n"
686 "\n".format(equals=delimiter,
687 default_section=self.default_section)
Fred Drake03c44a32010-02-19 06:08:41 +0000688 )
Łukasz Langa71b37a52010-12-17 21:56:32 +0000689 if self.allow_no_value:
690 expect_string += (
691 "[Valueless]\n"
692 "option-without-value\n"
693 "\n"
694 )
695 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000696
Fred Drakeabc086f2004-05-18 03:29:52 +0000697 def test_set_string_types(self):
698 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000699 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000700 # Check that we don't get an exception when setting values in
701 # an existing section using strings:
702 class mystr(str):
703 pass
704 cf.set("sect", "option1", "splat")
705 cf.set("sect", "option1", mystr("splat"))
706 cf.set("sect", "option2", "splat")
707 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000708 cf.set("sect", "option1", "splat")
709 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000710
Fred Drake82903142004-05-18 04:24:02 +0000711 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000712 if self.delimiters[0] != '=':
Zachary Ware9fe6d862013-12-08 00:20:35 -0600713 self.skipTest('incompatible format')
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000714 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000715 # check when we pass a mix of readable and non-readable files:
716 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900717 parsed_files = cf.read([file1, "nonexistent-file"], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000718 self.assertEqual(parsed_files, [file1])
719 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
720 # check when we pass only a filename:
721 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900722 parsed_files = cf.read(file1, encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000723 self.assertEqual(parsed_files, [file1])
724 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
David Ellis85b8d012017-03-03 17:14:27 +0000725 # check when we pass only a Path object:
726 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900727 parsed_files = cf.read(pathlib.Path(file1), encoding="utf-8")
David Ellis85b8d012017-03-03 17:14:27 +0000728 self.assertEqual(parsed_files, [file1])
729 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
730 # check when we passed both a filename and a Path object:
731 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900732 parsed_files = cf.read([pathlib.Path(file1), file1], encoding="utf-8")
David Ellis85b8d012017-03-03 17:14:27 +0000733 self.assertEqual(parsed_files, [file1, file1])
734 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
Fred Drake82903142004-05-18 04:24:02 +0000735 # check when we pass only missing files:
736 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900737 parsed_files = cf.read(["nonexistent-file"], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000738 self.assertEqual(parsed_files, [])
739 # check when we pass no files:
740 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900741 parsed_files = cf.read([], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000742 self.assertEqual(parsed_files, [])
743
Vincent Michele3148532017-11-02 13:47:04 +0100744 def test_read_returns_file_list_with_bytestring_path(self):
745 if self.delimiters[0] != '=':
746 self.skipTest('incompatible format')
747 file1_bytestring = support.findfile("cfgparser.1").encode()
748 # check when passing an existing bytestring path
749 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900750 parsed_files = cf.read(file1_bytestring, encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100751 self.assertEqual(parsed_files, [file1_bytestring])
752 # check when passing an non-existing bytestring path
753 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900754 parsed_files = cf.read(b'nonexistent-file', encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100755 self.assertEqual(parsed_files, [])
756 # check when passing both an existing and non-existing bytestring path
757 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900758 parsed_files = cf.read([file1_bytestring, b'nonexistent-file'], encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100759 self.assertEqual(parsed_files, [file1_bytestring])
760
Fred Drakec6f28912002-10-25 19:40:49 +0000761 # shared by subclasses
762 def get_interpolation_config(self):
763 return self.fromstring(
764 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000765 "bar{equals}something %(with1)s interpolation (1 step)\n"
766 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
767 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
768 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
769 "with11{equals}%(with10)s\n"
770 "with10{equals}%(with9)s\n"
771 "with9{equals}%(with8)s\n"
772 "with8{equals}%(With7)s\n"
773 "with7{equals}%(WITH6)s\n"
774 "with6{equals}%(with5)s\n"
775 "With5{equals}%(with4)s\n"
776 "WITH4{equals}%(with3)s\n"
777 "with3{equals}%(with2)s\n"
778 "with2{equals}%(with1)s\n"
779 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000780 "\n"
781 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000782 "foo{equals}%(bar)s\n"
783 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000784 "\n"
785 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000786 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000787 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000788
Fred Drake98e3b292002-10-25 20:42:44 +0000789 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000790 cf = self.fromstring("""
791 [section]
792 name {0[0]} %(value)s
793 key{0[1]} |%(name)s|
794 getdefault{0[1]} |%(default)s|
795 """.format(self.delimiters), defaults={"default": "<default>"})
796 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000797 L.sort()
798 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000799 with self.assertRaises(configparser.NoSectionError):
800 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000801
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100802 def test_popitem(self):
803 cf = self.fromstring("""
804 [section1]
805 name1 {0[0]} value1
806 [section2]
807 name2 {0[0]} value2
808 [section3]
809 name3 {0[0]} value3
810 """.format(self.delimiters), defaults={"default": "<default>"})
811 self.assertEqual(cf.popitem()[0], 'section1')
812 self.assertEqual(cf.popitem()[0], 'section2')
813 self.assertEqual(cf.popitem()[0], 'section3')
814 with self.assertRaises(KeyError):
815 cf.popitem()
816
817 def test_clear(self):
818 cf = self.newconfig({"foo": "Bar"})
819 self.assertEqual(
820 cf.get(self.default_section, "Foo"), "Bar",
821 "could not locate option, expecting case-insensitive option names")
822 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
823 self.assertEqual(cf.sections(), ['zing'])
824 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
825 cf.clear()
826 self.assertEqual(set(cf.sections()), set())
827 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
828
Łukasz Langa02101942012-12-31 13:55:11 +0100829 def test_setitem(self):
830 cf = self.fromstring("""
831 [section1]
832 name1 {0[0]} value1
833 [section2]
834 name2 {0[0]} value2
835 [section3]
836 name3 {0[0]} value3
837 """.format(self.delimiters), defaults={"nameD": "valueD"})
838 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
839 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
840 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
841 self.assertEqual(cf['section1']['name1'], 'value1')
842 self.assertEqual(cf['section2']['name2'], 'value2')
843 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100844 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100845 cf['section2'] = {'name22': 'value22'}
846 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
847 self.assertEqual(cf['section2']['name22'], 'value22')
848 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100849 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100850 cf['section3'] = {}
851 self.assertEqual(set(cf['section3'].keys()), {'named'})
852 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100853 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Cheryl Sabella33cd0582018-06-12 16:37:51 -0400854 # For bpo-32108, assigning default_section to itself.
855 cf[self.default_section] = cf[self.default_section]
856 self.assertNotEqual(set(cf[self.default_section].keys()), set())
Łukasz Langa02101942012-12-31 13:55:11 +0100857 cf[self.default_section] = {}
858 self.assertEqual(set(cf[self.default_section].keys()), set())
859 self.assertEqual(set(cf['section1'].keys()), {'name1'})
860 self.assertEqual(set(cf['section2'].keys()), {'name22'})
861 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100862 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Cheryl Sabella33cd0582018-06-12 16:37:51 -0400863 # For bpo-32108, assigning section to itself.
864 cf['section2'] = cf['section2']
865 self.assertEqual(set(cf['section2'].keys()), {'name22'})
Łukasz Langa02101942012-12-31 13:55:11 +0100866
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800867 def test_invalid_multiline_value(self):
868 if self.allow_no_value:
869 self.skipTest('if no_value is allowed, ParsingError is not raised')
870
871 invalid = textwrap.dedent("""\
872 [DEFAULT]
873 test {0} test
874 invalid""".format(self.delimiters[0])
875 )
876 cf = self.newconfig()
877 with self.assertRaises(configparser.ParsingError):
878 cf.read_string(invalid)
879 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
880 self.assertEqual(cf['DEFAULT']['test'], 'test')
881
Fred Drake8ef67672000-09-27 22:45:25 +0000882
Ezio Melottidc1fa802013-01-11 06:30:57 +0200883class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000884 config_class = configparser.RawConfigParser
885 strict = True
886
887
Ezio Melottidc1fa802013-01-11 06:30:57 +0200888class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000889 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000890
891 def test_interpolation(self):
892 cf = self.get_interpolation_config()
893 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000894 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
895 eq(cf.get("Foo", "bar9"),
896 "something with lots of interpolation (9 steps)")
897 eq(cf.get("Foo", "bar10"),
898 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000899 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000900 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200901 self.assertEqual(e.args, ("bar11", "Foo",
902 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000903 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
904 self.assertEqual(e.args, ("bar11", "Foo",
905 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000906
Fred Drake54782192002-12-31 06:57:25 +0000907 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000908 cf = self.get_interpolation_config()
909 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000910 "Interpolation Error", "name")
911 self.assertEqual(e.reference, "reference")
912 self.assertEqual(e.section, "Interpolation Error")
913 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000914 if self.interpolation == configparser._UNSET:
915 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200916 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000917 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
918 self.assertEqual(e.args, ('name', 'Interpolation Error',
919 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000920
Fred Drake98e3b292002-10-25 20:42:44 +0000921 def test_items(self):
922 self.check_items_config([('default', '<default>'),
923 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000924 ('key', '|value|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +0100925 ('name', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000926
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000927 def test_safe_interpolation(self):
928 # See http://www.python.org/sf/511737
929 cf = self.fromstring("[section]\n"
930 "option1{eq}xxx\n"
931 "option2{eq}%(option1)s/xxx\n"
932 "ok{eq}%(option1)s/%%s\n"
933 "not_ok{eq}%(option2)s/%%s".format(
934 eq=self.delimiters[0]))
935 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
936 if self.interpolation == configparser._UNSET:
937 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
938 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
939 with self.assertRaises(TypeError):
940 cf.get("section", "not_ok")
941
942 def test_set_malformatted_interpolation(self):
943 cf = self.fromstring("[sect]\n"
944 "option1{eq}foo\n".format(eq=self.delimiters[0]))
945
946 self.assertEqual(cf.get('sect', "option1"), "foo")
947
948 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
949 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
950 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
951
952 self.assertEqual(cf.get('sect', "option1"), "foo")
953
954 # bug #5741: double percents are *not* malformed
955 cf.set("sect", "option2", "foo%%bar")
956 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
957
David Goodger1cbf2062004-10-03 15:55:09 +0000958 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000959 cf = self.fromstring("[sect]\n"
960 "option1{eq}foo\n".format(eq=self.delimiters[0]))
961 # Check that we get a TypeError when setting non-string values
962 # in an existing section:
963 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
964 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
965 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
966 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
967 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
968 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
969 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
970 self.assertRaises(TypeError, cf.add_section, 123)
971
972 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000973 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000974 self.assertRaises(ValueError, cf.add_section, self.default_section)
975
Łukasz Langaa5fab172017-08-24 09:43:53 -0700976 def test_defaults_keyword(self):
977 """bpo-23835 fix for ConfigParser"""
978 cf = self.newconfig(defaults={1: 2.4})
979 self.assertEqual(cf[self.default_section]['1'], '2.4')
980 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
981 cf = self.newconfig(defaults={"A": 5.2})
982 self.assertEqual(cf[self.default_section]['a'], '5.2')
983 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
984
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200985
Ezio Melottidc1fa802013-01-11 06:30:57 +0200986class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200987 config_class = configparser.ConfigParser
988 interpolation = None
989 ini = textwrap.dedent("""
990 [numbers]
991 one = 1
992 two = %(one)s * 2
993 three = ${common:one} * 3
994
995 [hexen]
996 sixteen = ${numbers:two} * 8
997 """).strip()
998
999 def assertMatchesIni(self, cf):
1000 self.assertEqual(cf['numbers']['one'], '1')
1001 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
1002 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
1003 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
1004
1005 def test_no_interpolation(self):
1006 cf = self.fromstring(self.ini)
1007 self.assertMatchesIni(cf)
1008
1009 def test_empty_case(self):
1010 cf = self.newconfig()
1011 self.assertIsNone(cf.read_string(""))
1012
1013 def test_none_as_default_interpolation(self):
1014 class CustomConfigParser(configparser.ConfigParser):
1015 _DEFAULT_INTERPOLATION = None
1016
1017 cf = CustomConfigParser()
1018 cf.read_string(self.ini)
1019 self.assertMatchesIni(cf)
1020
1021
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001022class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
1023 config_class = configparser.ConfigParser
1024 interpolation = configparser.LegacyInterpolation()
1025
1026 def test_set_malformatted_interpolation(self):
1027 cf = self.fromstring("[sect]\n"
1028 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1029
1030 self.assertEqual(cf.get('sect', "option1"), "foo")
1031
1032 cf.set("sect", "option1", "%foo")
1033 self.assertEqual(cf.get('sect', "option1"), "%foo")
1034 cf.set("sect", "option1", "foo%")
1035 self.assertEqual(cf.get('sect', "option1"), "foo%")
1036 cf.set("sect", "option1", "f%oo")
1037 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1038
1039 # bug #5741: double percents are *not* malformed
1040 cf.set("sect", "option2", "foo%%bar")
1041 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001042
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001043
Georg Brandl96a60ae2010-07-28 13:13:46 +00001044class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1045 delimiters = (':=', '$')
1046 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001047 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001048
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001049
Łukasz Langac264c092010-11-20 16:15:37 +00001050class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1051 default_section = 'general'
1052
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001053
Ezio Melottidc1fa802013-01-11 06:30:57 +02001054class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001055 config_class = configparser.ConfigParser
1056 wonderful_spam = ("I'm having spam spam spam spam "
1057 "spam spam spam beaked beans spam "
1058 "spam spam and spam!").replace(' ', '\t\n')
1059
1060 def setUp(self):
1061 cf = self.newconfig()
1062 for i in range(100):
1063 s = 'section{}'.format(i)
1064 cf.add_section(s)
1065 for j in range(10):
1066 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
Inada Naoki35715d12021-04-04 09:01:23 +09001067 with open(os_helper.TESTFN, 'w', encoding="utf-8") as f:
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001068 cf.write(f)
1069
1070 def tearDown(self):
Hai Shia7f5d932020-08-04 00:41:24 +08001071 os.unlink(os_helper.TESTFN)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001072
1073 def test_dominating_multiline_values(self):
1074 # We're reading from file because this is where the code changed
1075 # during performance updates in Python 3.2
1076 cf_from_file = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +09001077 with open(os_helper.TESTFN, encoding="utf-8") as f:
Fred Drakea4923622010-08-09 12:52:45 +00001078 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001079 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1080 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001081
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001082
Ezio Melottidc1fa802013-01-11 06:30:57 +02001083class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001084 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001085
1086 def test_interpolation(self):
1087 cf = self.get_interpolation_config()
1088 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001089 eq(cf.get("Foo", "bar"),
1090 "something %(with1)s interpolation (1 step)")
1091 eq(cf.get("Foo", "bar9"),
1092 "something %(with9)s lots of interpolation (9 steps)")
1093 eq(cf.get("Foo", "bar10"),
1094 "something %(with10)s lots of interpolation (10 steps)")
1095 eq(cf.get("Foo", "bar11"),
1096 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001097
Fred Drake98e3b292002-10-25 20:42:44 +00001098 def test_items(self):
1099 self.check_items_config([('default', '<default>'),
1100 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001101 ('key', '|%(name)s|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +01001102 ('name', '%(value)s')])
Fred Drake98e3b292002-10-25 20:42:44 +00001103
David Goodger1cbf2062004-10-03 15:55:09 +00001104 def test_set_nonstring_types(self):
1105 cf = self.newconfig()
1106 cf.add_section('non-string')
1107 cf.set('non-string', 'int', 1)
1108 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1109 cf.set('non-string', 'dict', {'pi': 3.14159})
1110 self.assertEqual(cf.get('non-string', 'int'), 1)
1111 self.assertEqual(cf.get('non-string', 'list'),
1112 [0, 1, 1, 2, 3, 5, 8, 13])
1113 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001114 cf.add_section(123)
1115 cf.set(123, 'this is sick', True)
1116 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001117 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001118 # would not work for SortedDict; only checking for the most common
John Reese3a5b0d82018-06-05 16:31:33 -07001119 # default dictionary (dict)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001120 cf.optionxform = lambda x: x
1121 cf.set('non-string', 1, 1)
1122 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001123
Łukasz Langaa5fab172017-08-24 09:43:53 -07001124 def test_defaults_keyword(self):
1125 """bpo-23835 legacy behavior for RawConfigParser"""
1126 with self.assertRaises(AttributeError) as ctx:
1127 self.newconfig(defaults={1: 2.4})
1128 err = ctx.exception
1129 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1130 cf = self.newconfig(defaults={"A": 5.2})
1131 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1132
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001133
Georg Brandl96a60ae2010-07-28 13:13:46 +00001134class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1135 delimiters = (':=', '$')
1136 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001137 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001138
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001139
Ezio Melottidc1fa802013-01-11 06:30:57 +02001140class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001141 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001142 comment_prefixes = ('#', ';', '----')
1143 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001144 empty_lines_in_values = False
1145
1146 def test_reading(self):
1147 smbconf = support.findfile("cfgparser.2")
1148 # check when we pass a mix of readable and non-readable files:
1149 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001150 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001151 self.assertEqual(parsed_files, [smbconf])
1152 sections = ['global', 'homes', 'printers',
1153 'print$', 'pdf-generator', 'tmp', 'Agustin']
1154 self.assertEqual(cf.sections(), sections)
1155 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1156 self.assertEqual(cf.getint("global", "max log size"), 50)
1157 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1158 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001159
Ezio Melottidc1fa802013-01-11 06:30:57 +02001160class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001161 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001162 interpolation = configparser.ExtendedInterpolation()
1163 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001164 strict = True
1165
1166 def fromstring(self, string, defaults=None, optionxform=None):
1167 cf = self.newconfig(defaults)
1168 if optionxform:
1169 cf.optionxform = optionxform
1170 cf.read_string(string)
1171 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001172
1173 def test_extended_interpolation(self):
1174 cf = self.fromstring(textwrap.dedent("""
1175 [common]
1176 favourite Beatle = Paul
1177 favourite color = green
1178
1179 [tom]
1180 favourite band = ${favourite color} day
1181 favourite pope = John ${favourite Beatle} II
1182 sequel = ${favourite pope}I
1183
1184 [ambv]
1185 favourite Beatle = George
1186 son of Edward VII = ${favourite Beatle} V
1187 son of George V = ${son of Edward VII}I
1188
1189 [stanley]
1190 favourite Beatle = ${ambv:favourite Beatle}
1191 favourite pope = ${tom:favourite pope}
1192 favourite color = black
1193 favourite state of mind = paranoid
1194 favourite movie = soylent ${common:favourite color}
1195 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1196 """).strip())
1197
1198 eq = self.assertEqual
1199 eq(cf['common']['favourite Beatle'], 'Paul')
1200 eq(cf['common']['favourite color'], 'green')
1201 eq(cf['tom']['favourite Beatle'], 'Paul')
1202 eq(cf['tom']['favourite color'], 'green')
1203 eq(cf['tom']['favourite band'], 'green day')
1204 eq(cf['tom']['favourite pope'], 'John Paul II')
1205 eq(cf['tom']['sequel'], 'John Paul III')
1206 eq(cf['ambv']['favourite Beatle'], 'George')
1207 eq(cf['ambv']['favourite color'], 'green')
1208 eq(cf['ambv']['son of Edward VII'], 'George V')
1209 eq(cf['ambv']['son of George V'], 'George VI')
1210 eq(cf['stanley']['favourite Beatle'], 'George')
1211 eq(cf['stanley']['favourite color'], 'black')
1212 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1213 eq(cf['stanley']['favourite movie'], 'soylent green')
1214 eq(cf['stanley']['favourite pope'], 'John Paul II')
1215 eq(cf['stanley']['favourite song'],
1216 'black sabbath - paranoid')
1217
1218 def test_endless_loop(self):
1219 cf = self.fromstring(textwrap.dedent("""
1220 [one for you]
1221 ping = ${one for me:pong}
1222
1223 [one for me]
1224 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001225
1226 [selfish]
1227 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001228 """).strip())
1229
1230 with self.assertRaises(configparser.InterpolationDepthError):
1231 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001232 with self.assertRaises(configparser.InterpolationDepthError):
1233 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001234
Łukasz Langa71b37a52010-12-17 21:56:32 +00001235 def test_strange_options(self):
1236 cf = self.fromstring("""
1237 [dollars]
1238 $var = $$value
1239 $var2 = ${$var}
1240 ${sick} = cannot interpolate me
1241
1242 [interpolated]
1243 $other = ${dollars:$var}
1244 $trying = ${dollars:${sick}}
1245 """)
1246
1247 self.assertEqual(cf['dollars']['$var'], '$value')
1248 self.assertEqual(cf['interpolated']['$other'], '$value')
1249 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1250 exception_class = configparser.InterpolationMissingOptionError
1251 with self.assertRaises(exception_class) as cm:
1252 cf['interpolated']['$trying']
1253 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001254 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001255
Łukasz Langae698cd52011-04-28 10:58:57 +02001256 def test_case_sensitivity_basic(self):
1257 ini = textwrap.dedent("""
1258 [common]
1259 optionlower = value
1260 OptionUpper = Value
1261
1262 [Common]
1263 optionlower = a better ${common:optionlower}
1264 OptionUpper = A Better ${common:OptionUpper}
1265
1266 [random]
1267 foolower = ${common:optionlower} redefined
1268 FooUpper = ${Common:OptionUpper} Redefined
1269 """).strip()
1270
1271 cf = self.fromstring(ini)
1272 eq = self.assertEqual
1273 eq(cf['common']['optionlower'], 'value')
1274 eq(cf['common']['OptionUpper'], 'Value')
1275 eq(cf['Common']['optionlower'], 'a better value')
1276 eq(cf['Common']['OptionUpper'], 'A Better Value')
1277 eq(cf['random']['foolower'], 'value redefined')
1278 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1279
1280 def test_case_sensitivity_conflicts(self):
1281 ini = textwrap.dedent("""
1282 [common]
1283 option = value
1284 Option = Value
1285
1286 [Common]
1287 option = a better ${common:option}
1288 Option = A Better ${common:Option}
1289
1290 [random]
1291 foo = ${common:option} redefined
1292 Foo = ${Common:Option} Redefined
1293 """).strip()
1294 with self.assertRaises(configparser.DuplicateOptionError):
1295 cf = self.fromstring(ini)
1296
1297 # raw options
1298 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1299 eq = self.assertEqual
1300 eq(cf['common']['option'], 'value')
1301 eq(cf['common']['Option'], 'Value')
1302 eq(cf['Common']['option'], 'a better value')
1303 eq(cf['Common']['Option'], 'A Better Value')
1304 eq(cf['random']['foo'], 'value redefined')
1305 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001306
1307 def test_other_errors(self):
1308 cf = self.fromstring("""
1309 [interpolation fail]
1310 case1 = ${where's the brace
1311 case2 = ${does_not_exist}
1312 case3 = ${wrong_section:wrong_value}
1313 case4 = ${i:like:colon:characters}
1314 case5 = $100 for Fail No 5!
1315 """)
1316
1317 with self.assertRaises(configparser.InterpolationSyntaxError):
1318 cf['interpolation fail']['case1']
1319 with self.assertRaises(configparser.InterpolationMissingOptionError):
1320 cf['interpolation fail']['case2']
1321 with self.assertRaises(configparser.InterpolationMissingOptionError):
1322 cf['interpolation fail']['case3']
1323 with self.assertRaises(configparser.InterpolationSyntaxError):
1324 cf['interpolation fail']['case4']
1325 with self.assertRaises(configparser.InterpolationSyntaxError):
1326 cf['interpolation fail']['case5']
1327 with self.assertRaises(ValueError):
1328 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001329
1330
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001331class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001332 allow_no_value = True
1333
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001334
Ezio Melottidc1fa802013-01-11 06:30:57 +02001335class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001336 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001337 delimiters = {'='}
1338 comment_prefixes = {'#'}
1339 allow_no_value = True
1340
1341 def test_cfgparser_dot_3(self):
1342 tricky = support.findfile("cfgparser.3")
1343 cf = self.newconfig()
1344 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1345 self.assertEqual(cf.sections(), ['strange',
1346 'corruption',
1347 'yeah, sections can be '
1348 'indented as well',
1349 'another one!',
1350 'no values here',
1351 'tricky interpolation',
1352 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001353 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001354 vars={'interpolate': '-1'}), -1)
1355 with self.assertRaises(ValueError):
1356 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001357 cf.getint(self.default_section, 'go', raw=True,
1358 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001359 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1360 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1361 longname = 'yeah, sections can be indented as well'
1362 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001363 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001364 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1365 # `go` from DEFAULT
1366 with self.assertRaises(configparser.InterpolationMissingOptionError):
1367 cf.items('no values here')
1368 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1369 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1370 cf.get('tricky interpolation', 'go'))
1371 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1372
1373 def test_unicode_failure(self):
1374 tricky = support.findfile("cfgparser.3")
1375 cf = self.newconfig()
1376 with self.assertRaises(UnicodeDecodeError):
1377 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001378
Fred Drake88444412010-09-03 04:22:36 +00001379
1380class Issue7005TestCase(unittest.TestCase):
1381 """Test output when None is set() as a value and allow_no_value == False.
1382
1383 http://bugs.python.org/issue7005
1384
1385 """
1386
1387 expected_output = "[section]\noption = None\n\n"
1388
1389 def prepare(self, config_class):
1390 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001391 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001392 cp.add_section("section")
1393 cp.set("section", "option", None)
1394 sio = io.StringIO()
1395 cp.write(sio)
1396 return sio.getvalue()
1397
1398 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001399 cp = configparser.ConfigParser(allow_no_value=False)
1400 cp.add_section("section")
1401 with self.assertRaises(TypeError):
1402 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001403
1404 def test_none_as_value_stringified_raw(self):
1405 output = self.prepare(configparser.RawConfigParser)
1406 self.assertEqual(output, self.expected_output)
1407
1408
Thomas Wouters89f507f2006-12-13 04:49:30 +00001409class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001410 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001411
1412 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001413 cf = self.fromstring("[b]\n"
1414 "o4=1\n"
1415 "o3=2\n"
1416 "o2=3\n"
1417 "o1=4\n"
1418 "[a]\n"
1419 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001420 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001421 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001422 self.assertEqual(output.getvalue(),
1423 "[a]\n"
1424 "k = v\n\n"
1425 "[b]\n"
1426 "o1 = 4\n"
1427 "o2 = 3\n"
1428 "o3 = 2\n"
1429 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001430
Fred Drake03c44a32010-02-19 06:08:41 +00001431
Ezio Melottidc1fa802013-01-11 06:30:57 +02001432class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001433 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001434 comment_prefixes = '#;'
1435 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001436
1437 def test_comment_handling(self):
1438 config_string = textwrap.dedent("""\
1439 [Commented Bar]
1440 baz=qwe ; a comment
1441 foo: bar # not a comment!
1442 # but this is a comment
1443 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001444 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001445 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001446 """)
1447 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001448 self.assertEqual(cf.get('Commented Bar', 'foo'),
1449 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001450 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001451 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1452 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001453
Ezio Melottidc1fa802013-01-11 06:30:57 +02001454class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001455 config_class = configparser.ConfigParser
1456
1457 def fromstring(self, string, defaults=None):
1458 cf = self.newconfig(defaults)
1459 cf.read_string(string)
1460 cf_copy = self.newconfig()
1461 cf_copy.read_dict(cf)
1462 # we have to clean up option duplicates that appeared because of
1463 # the magic DEFAULTSECT behaviour.
1464 for section in cf_copy.values():
1465 if section.name == self.default_section:
1466 continue
1467 for default, value in cf[self.default_section].items():
1468 if section[default] == value:
1469 del section[default]
1470 return cf_copy
1471
Łukasz Langadaab1c82011-04-27 18:10:05 +02001472
1473class FakeFile:
1474 def __init__(self):
1475 file_path = support.findfile("cfgparser.1")
Inada Naoki35715d12021-04-04 09:01:23 +09001476 with open(file_path, encoding="utf-8") as f:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001477 self.lines = f.readlines()
1478 self.lines.reverse()
1479
1480 def readline(self):
1481 if len(self.lines):
1482 return self.lines.pop()
1483 return ''
1484
1485
1486def readline_generator(f):
1487 """As advised in Doc/library/configparser.rst."""
1488 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001489 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001490 yield line
1491 line = f.readline()
1492
1493
1494class ReadFileTestCase(unittest.TestCase):
1495 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001496 file_paths = [support.findfile("cfgparser.1")]
1497 try:
1498 file_paths.append(file_paths[0].encode('utf8'))
1499 except UnicodeEncodeError:
1500 pass # unfortunately we can't test bytes on this path
1501 for file_path in file_paths:
1502 parser = configparser.ConfigParser()
Inada Naoki35715d12021-04-04 09:01:23 +09001503 with open(file_path, encoding="utf-8") as f:
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001504 parser.read_file(f)
1505 self.assertIn("Foo Bar", parser)
1506 self.assertIn("foo", parser["Foo Bar"])
1507 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001508
1509 def test_iterable(self):
1510 lines = textwrap.dedent("""
1511 [Foo Bar]
1512 foo=newbar""").strip().split('\n')
1513 parser = configparser.ConfigParser()
1514 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001515 self.assertIn("Foo Bar", parser)
1516 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001517 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1518
1519 def test_readline_generator(self):
1520 """Issue #11670."""
1521 parser = configparser.ConfigParser()
1522 with self.assertRaises(TypeError):
1523 parser.read_file(FakeFile())
1524 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001525 self.assertIn("Foo Bar", parser)
1526 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001527 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1528
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001529 def test_source_as_bytes(self):
1530 """Issue #18260."""
1531 lines = textwrap.dedent("""
1532 [badbad]
1533 [badbad]""").strip().split('\n')
1534 parser = configparser.ConfigParser()
1535 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1536 parser.read_file(lines, source=b"badbad")
1537 self.assertEqual(
1538 str(dse.exception),
1539 "While reading from b'badbad' [line 2]: section 'badbad' "
1540 "already exists"
1541 )
1542 lines = textwrap.dedent("""
1543 [badbad]
1544 bad = bad
1545 bad = bad""").strip().split('\n')
1546 parser = configparser.ConfigParser()
1547 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1548 parser.read_file(lines, source=b"badbad")
1549 self.assertEqual(
1550 str(dse.exception),
1551 "While reading from b'badbad' [line 3]: option 'bad' in section "
1552 "'badbad' already exists"
1553 )
1554 lines = textwrap.dedent("""
1555 [badbad]
1556 = bad""").strip().split('\n')
1557 parser = configparser.ConfigParser()
1558 with self.assertRaises(configparser.ParsingError) as dse:
1559 parser.read_file(lines, source=b"badbad")
1560 self.assertEqual(
1561 str(dse.exception),
1562 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1563 )
1564 lines = textwrap.dedent("""
1565 [badbad
1566 bad = bad""").strip().split('\n')
1567 parser = configparser.ConfigParser()
1568 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1569 parser.read_file(lines, source=b"badbad")
1570 self.assertEqual(
1571 str(dse.exception),
1572 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1573 "'[badbad'"
1574 )
1575
Łukasz Langadaab1c82011-04-27 18:10:05 +02001576
Łukasz Langa71b37a52010-12-17 21:56:32 +00001577class CoverageOneHundredTestCase(unittest.TestCase):
1578 """Covers edge cases in the codebase."""
1579
1580 def test_duplicate_option_error(self):
1581 error = configparser.DuplicateOptionError('section', 'option')
1582 self.assertEqual(error.section, 'section')
1583 self.assertEqual(error.option, 'option')
1584 self.assertEqual(error.source, None)
1585 self.assertEqual(error.lineno, None)
1586 self.assertEqual(error.args, ('section', 'option', None, None))
1587 self.assertEqual(str(error), "Option 'option' in section 'section' "
1588 "already exists")
1589
1590 def test_interpolation_depth_error(self):
1591 error = configparser.InterpolationDepthError('option', 'section',
1592 'rawval')
1593 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1594 self.assertEqual(error.option, 'option')
1595 self.assertEqual(error.section, 'section')
1596
1597 def test_parsing_error(self):
1598 with self.assertRaises(ValueError) as cm:
1599 configparser.ParsingError()
1600 self.assertEqual(str(cm.exception), "Required argument `source' not "
1601 "given.")
1602 with self.assertRaises(ValueError) as cm:
1603 configparser.ParsingError(source='source', filename='filename')
1604 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1605 "and `source'. Use `source'.")
1606 error = configparser.ParsingError(filename='source')
1607 self.assertEqual(error.source, 'source')
1608 with warnings.catch_warnings(record=True) as w:
1609 warnings.simplefilter("always", DeprecationWarning)
1610 self.assertEqual(error.filename, 'source')
1611 error.filename = 'filename'
1612 self.assertEqual(error.source, 'filename')
1613 for warning in w:
1614 self.assertTrue(warning.category is DeprecationWarning)
1615
1616 def test_interpolation_validation(self):
1617 parser = configparser.ConfigParser()
1618 parser.read_string("""
1619 [section]
1620 invalid_percent = %
1621 invalid_reference = %(()
1622 invalid_variable = %(does_not_exist)s
1623 """)
1624 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1625 parser['section']['invalid_percent']
1626 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1627 "'(', found: '%'")
1628 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1629 parser['section']['invalid_reference']
1630 self.assertEqual(str(cm.exception), "bad interpolation variable "
1631 "reference '%(()'")
1632
1633 def test_readfp_deprecation(self):
1634 sio = io.StringIO("""
1635 [section]
1636 option = value
1637 """)
1638 parser = configparser.ConfigParser()
1639 with warnings.catch_warnings(record=True) as w:
1640 warnings.simplefilter("always", DeprecationWarning)
1641 parser.readfp(sio, filename='StringIO')
1642 for warning in w:
1643 self.assertTrue(warning.category is DeprecationWarning)
1644 self.assertEqual(len(parser), 2)
1645 self.assertEqual(parser['section']['option'], 'value')
1646
1647 def test_safeconfigparser_deprecation(self):
1648 with warnings.catch_warnings(record=True) as w:
1649 warnings.simplefilter("always", DeprecationWarning)
1650 parser = configparser.SafeConfigParser()
1651 for warning in w:
1652 self.assertTrue(warning.category is DeprecationWarning)
1653
1654 def test_sectionproxy_repr(self):
1655 parser = configparser.ConfigParser()
1656 parser.read_string("""
1657 [section]
1658 key = value
1659 """)
1660 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001661
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001662 def test_inconsistent_converters_state(self):
1663 parser = configparser.ConfigParser()
1664 import decimal
1665 parser.converters['decimal'] = decimal.Decimal
1666 parser.read_string("""
1667 [s1]
1668 one = 1
1669 [s2]
1670 two = 2
1671 """)
1672 self.assertIn('decimal', parser.converters)
1673 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1674 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1675 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1676 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1677 del parser.getdecimal
1678 with self.assertRaises(AttributeError):
1679 parser.getdecimal('s1', 'one')
1680 self.assertIn('decimal', parser.converters)
1681 del parser.converters['decimal']
1682 self.assertNotIn('decimal', parser.converters)
1683 with self.assertRaises(AttributeError):
1684 parser.getdecimal('s1', 'one')
1685 with self.assertRaises(AttributeError):
1686 parser['s1'].getdecimal('one')
1687 with self.assertRaises(AttributeError):
1688 parser['s2'].getdecimal('two')
1689
Łukasz Langae7851952012-01-20 14:57:55 +01001690
1691class ExceptionPicklingTestCase(unittest.TestCase):
1692 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1693
1694 def test_error(self):
1695 import pickle
1696 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001697 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1698 pickled = pickle.dumps(e1, proto)
1699 e2 = pickle.loads(pickled)
1700 self.assertEqual(e1.message, e2.message)
1701 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001702
1703 def test_nosectionerror(self):
1704 import pickle
1705 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001706 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1707 pickled = pickle.dumps(e1, proto)
1708 e2 = pickle.loads(pickled)
1709 self.assertEqual(e1.message, e2.message)
1710 self.assertEqual(e1.args, e2.args)
1711 self.assertEqual(e1.section, e2.section)
1712 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001713
1714 def test_nooptionerror(self):
1715 import pickle
1716 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001717 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1718 pickled = pickle.dumps(e1, proto)
1719 e2 = pickle.loads(pickled)
1720 self.assertEqual(e1.message, e2.message)
1721 self.assertEqual(e1.args, e2.args)
1722 self.assertEqual(e1.section, e2.section)
1723 self.assertEqual(e1.option, e2.option)
1724 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001725
1726 def test_duplicatesectionerror(self):
1727 import pickle
1728 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001729 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1730 pickled = pickle.dumps(e1, proto)
1731 e2 = pickle.loads(pickled)
1732 self.assertEqual(e1.message, e2.message)
1733 self.assertEqual(e1.args, e2.args)
1734 self.assertEqual(e1.section, e2.section)
1735 self.assertEqual(e1.source, e2.source)
1736 self.assertEqual(e1.lineno, e2.lineno)
1737 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001738
1739 def test_duplicateoptionerror(self):
1740 import pickle
1741 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1742 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001743 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1744 pickled = pickle.dumps(e1, proto)
1745 e2 = pickle.loads(pickled)
1746 self.assertEqual(e1.message, e2.message)
1747 self.assertEqual(e1.args, e2.args)
1748 self.assertEqual(e1.section, e2.section)
1749 self.assertEqual(e1.option, e2.option)
1750 self.assertEqual(e1.source, e2.source)
1751 self.assertEqual(e1.lineno, e2.lineno)
1752 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001753
1754 def test_interpolationerror(self):
1755 import pickle
1756 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001757 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1758 pickled = pickle.dumps(e1, proto)
1759 e2 = pickle.loads(pickled)
1760 self.assertEqual(e1.message, e2.message)
1761 self.assertEqual(e1.args, e2.args)
1762 self.assertEqual(e1.section, e2.section)
1763 self.assertEqual(e1.option, e2.option)
1764 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001765
1766 def test_interpolationmissingoptionerror(self):
1767 import pickle
1768 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1769 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001770 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1771 pickled = pickle.dumps(e1, proto)
1772 e2 = pickle.loads(pickled)
1773 self.assertEqual(e1.message, e2.message)
1774 self.assertEqual(e1.args, e2.args)
1775 self.assertEqual(e1.section, e2.section)
1776 self.assertEqual(e1.option, e2.option)
1777 self.assertEqual(e1.reference, e2.reference)
1778 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001779
1780 def test_interpolationsyntaxerror(self):
1781 import pickle
1782 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001783 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1784 pickled = pickle.dumps(e1, proto)
1785 e2 = pickle.loads(pickled)
1786 self.assertEqual(e1.message, e2.message)
1787 self.assertEqual(e1.args, e2.args)
1788 self.assertEqual(e1.section, e2.section)
1789 self.assertEqual(e1.option, e2.option)
1790 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001791
1792 def test_interpolationdeptherror(self):
1793 import pickle
1794 e1 = configparser.InterpolationDepthError('option', 'section',
1795 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001796 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1797 pickled = pickle.dumps(e1, proto)
1798 e2 = pickle.loads(pickled)
1799 self.assertEqual(e1.message, e2.message)
1800 self.assertEqual(e1.args, e2.args)
1801 self.assertEqual(e1.section, e2.section)
1802 self.assertEqual(e1.option, e2.option)
1803 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001804
1805 def test_parsingerror(self):
1806 import pickle
1807 e1 = configparser.ParsingError('source')
1808 e1.append(1, 'line1')
1809 e1.append(2, 'line2')
1810 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001811 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1812 pickled = pickle.dumps(e1, proto)
1813 e2 = pickle.loads(pickled)
1814 self.assertEqual(e1.message, e2.message)
1815 self.assertEqual(e1.args, e2.args)
1816 self.assertEqual(e1.source, e2.source)
1817 self.assertEqual(e1.errors, e2.errors)
1818 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001819 e1 = configparser.ParsingError(filename='filename')
1820 e1.append(1, 'line1')
1821 e1.append(2, 'line2')
1822 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001823 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1824 pickled = pickle.dumps(e1, proto)
1825 e2 = pickle.loads(pickled)
1826 self.assertEqual(e1.message, e2.message)
1827 self.assertEqual(e1.args, e2.args)
1828 self.assertEqual(e1.source, e2.source)
1829 self.assertEqual(e1.errors, e2.errors)
1830 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001831
1832 def test_missingsectionheadererror(self):
1833 import pickle
1834 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001835 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1836 pickled = pickle.dumps(e1, proto)
1837 e2 = pickle.loads(pickled)
1838 self.assertEqual(e1.message, e2.message)
1839 self.assertEqual(e1.args, e2.args)
1840 self.assertEqual(e1.line, e2.line)
1841 self.assertEqual(e1.source, e2.source)
1842 self.assertEqual(e1.lineno, e2.lineno)
1843 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001844
1845
Łukasz Langacba24322012-07-07 18:54:08 +02001846class InlineCommentStrippingTestCase(unittest.TestCase):
1847 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1848 delimiter occurs earlier without preceding space.."""
1849
1850 def test_stripping(self):
1851 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1852 '//'))
1853 cfg.read_string("""
1854 [section]
1855 k1 = v1;still v1
1856 k2 = v2 ;a comment
1857 k3 = v3 ; also a comment
1858 k4 = v4;still v4 ;a comment
1859 k5 = v5;still v5 ; also a comment
1860 k6 = v6;still v6; and still v6 ;a comment
1861 k7 = v7;still v7; and still v7 ; also a comment
1862
1863 [multiprefix]
1864 k1 = v1;still v1 #a comment ; yeah, pretty much
1865 k2 = v2 // this already is a comment ; continued
1866 k3 = v3;#//still v3# and still v3 ; a comment
1867 """)
1868 s = cfg['section']
1869 self.assertEqual(s['k1'], 'v1;still v1')
1870 self.assertEqual(s['k2'], 'v2')
1871 self.assertEqual(s['k3'], 'v3')
1872 self.assertEqual(s['k4'], 'v4;still v4')
1873 self.assertEqual(s['k5'], 'v5;still v5')
1874 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1875 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1876 s = cfg['multiprefix']
1877 self.assertEqual(s['k1'], 'v1;still v1')
1878 self.assertEqual(s['k2'], 'v2')
1879 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1880
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001881
Łukasz Langa949053b2014-09-04 01:36:33 -07001882class ExceptionContextTestCase(unittest.TestCase):
1883 """ Test that implementation details doesn't leak
1884 through raising exceptions. """
1885
1886 def test_get_basic_interpolation(self):
1887 parser = configparser.ConfigParser()
1888 parser.read_string("""
1889 [Paths]
1890 home_dir: /Users
1891 my_dir: %(home_dir1)s/lumberjack
1892 my_pictures: %(my_dir)s/Pictures
1893 """)
1894 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1895 with cm:
1896 parser.get('Paths', 'my_dir')
1897 self.assertIs(cm.exception.__suppress_context__, True)
1898
1899 def test_get_extended_interpolation(self):
1900 parser = configparser.ConfigParser(
1901 interpolation=configparser.ExtendedInterpolation())
1902 parser.read_string("""
1903 [Paths]
1904 home_dir: /Users
1905 my_dir: ${home_dir1}/lumberjack
1906 my_pictures: ${my_dir}/Pictures
1907 """)
1908 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1909 with cm:
1910 parser.get('Paths', 'my_dir')
1911 self.assertIs(cm.exception.__suppress_context__, True)
1912
1913 def test_missing_options(self):
1914 parser = configparser.ConfigParser()
1915 parser.read_string("""
1916 [Paths]
1917 home_dir: /Users
1918 """)
1919 with self.assertRaises(configparser.NoSectionError) as cm:
1920 parser.options('test')
1921 self.assertIs(cm.exception.__suppress_context__, True)
1922
1923 def test_missing_section(self):
1924 config = configparser.ConfigParser()
1925 with self.assertRaises(configparser.NoSectionError) as cm:
1926 config.set('Section1', 'an_int', '15')
1927 self.assertIs(cm.exception.__suppress_context__, True)
1928
1929 def test_remove_option(self):
1930 config = configparser.ConfigParser()
1931 with self.assertRaises(configparser.NoSectionError) as cm:
1932 config.remove_option('Section1', 'an_int')
1933 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001934
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001935
1936class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1937 """Introduced in 3.5, issue #18159."""
1938
1939 config_class = configparser.ConfigParser
1940
1941 def newconfig(self, defaults=None):
1942 instance = super().newconfig(defaults=defaults)
1943 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1944 if e.strip()]
1945 return instance
1946
1947 def test_converters(self):
1948 cfg = self.newconfig()
1949 self.assertIn('boolean', cfg.converters)
1950 self.assertIn('list', cfg.converters)
1951 self.assertIsNone(cfg.converters['int'])
1952 self.assertIsNone(cfg.converters['float'])
1953 self.assertIsNone(cfg.converters['boolean'])
1954 self.assertIsNotNone(cfg.converters['list'])
1955 self.assertEqual(len(cfg.converters), 4)
1956 with self.assertRaises(ValueError):
1957 cfg.converters[''] = lambda v: v
1958 with self.assertRaises(ValueError):
1959 cfg.converters[None] = lambda v: v
1960 cfg.read_string("""
1961 [s]
1962 str = string
1963 int = 1
1964 float = 0.5
1965 list = a b c d e f g
1966 bool = yes
1967 """)
1968 s = cfg['s']
1969 self.assertEqual(s['str'], 'string')
1970 self.assertEqual(s['int'], '1')
1971 self.assertEqual(s['float'], '0.5')
1972 self.assertEqual(s['list'], 'a b c d e f g')
1973 self.assertEqual(s['bool'], 'yes')
1974 self.assertEqual(cfg.get('s', 'str'), 'string')
1975 self.assertEqual(cfg.get('s', 'int'), '1')
1976 self.assertEqual(cfg.get('s', 'float'), '0.5')
1977 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1978 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1979 self.assertEqual(cfg.get('s', 'str'), 'string')
1980 self.assertEqual(cfg.getint('s', 'int'), 1)
1981 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1982 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1983 'e', 'f', 'g'])
1984 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1985 self.assertEqual(s.get('str'), 'string')
1986 self.assertEqual(s.getint('int'), 1)
1987 self.assertEqual(s.getfloat('float'), 0.5)
1988 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1989 'e', 'f', 'g'])
1990 self.assertEqual(s.getboolean('bool'), True)
1991 with self.assertRaises(AttributeError):
1992 cfg.getdecimal('s', 'float')
1993 with self.assertRaises(AttributeError):
1994 s.getdecimal('float')
1995 import decimal
1996 cfg.converters['decimal'] = decimal.Decimal
1997 self.assertIn('decimal', cfg.converters)
1998 self.assertIsNotNone(cfg.converters['decimal'])
1999 self.assertEqual(len(cfg.converters), 5)
2000 dec0_5 = decimal.Decimal('0.5')
2001 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
2002 self.assertEqual(s.getdecimal('float'), dec0_5)
2003 del cfg.converters['decimal']
2004 self.assertNotIn('decimal', cfg.converters)
2005 self.assertEqual(len(cfg.converters), 4)
2006 with self.assertRaises(AttributeError):
2007 cfg.getdecimal('s', 'float')
2008 with self.assertRaises(AttributeError):
2009 s.getdecimal('float')
2010 with self.assertRaises(KeyError):
2011 del cfg.converters['decimal']
2012 with self.assertRaises(KeyError):
2013 del cfg.converters['']
2014 with self.assertRaises(KeyError):
2015 del cfg.converters[None]
2016
2017
2018class BlatantOverrideConvertersTestCase(unittest.TestCase):
2019 """What if somebody overrode a getboolean()? We want to make sure that in
2020 this case the automatic converters do not kick in."""
2021
2022 config = """
2023 [one]
2024 one = false
2025 two = false
2026 three = long story short
2027
2028 [two]
2029 one = false
2030 two = false
2031 three = four
2032 """
2033
2034 def test_converters_at_init(self):
2035 cfg = configparser.ConfigParser(converters={'len': len})
2036 cfg.read_string(self.config)
2037 self._test_len(cfg)
2038 self.assertIsNotNone(cfg.converters['len'])
2039
2040 def test_inheritance(self):
2041 class StrangeConfigParser(configparser.ConfigParser):
2042 gettysburg = 'a historic borough in south central Pennsylvania'
2043
2044 def getboolean(self, section, option, *, raw=False, vars=None,
2045 fallback=configparser._UNSET):
2046 if section == option:
2047 return True
2048 return super().getboolean(section, option, raw=raw, vars=vars,
2049 fallback=fallback)
2050 def getlen(self, section, option, *, raw=False, vars=None,
2051 fallback=configparser._UNSET):
2052 return self._get_conv(section, option, len, raw=raw, vars=vars,
2053 fallback=fallback)
2054
2055 cfg = StrangeConfigParser()
2056 cfg.read_string(self.config)
2057 self._test_len(cfg)
2058 self.assertIsNone(cfg.converters['len'])
2059 self.assertTrue(cfg.getboolean('one', 'one'))
2060 self.assertTrue(cfg.getboolean('two', 'two'))
2061 self.assertFalse(cfg.getboolean('one', 'two'))
2062 self.assertFalse(cfg.getboolean('two', 'one'))
2063 cfg.converters['boolean'] = cfg._convert_to_boolean
2064 self.assertFalse(cfg.getboolean('one', 'one'))
2065 self.assertFalse(cfg.getboolean('two', 'two'))
2066 self.assertFalse(cfg.getboolean('one', 'two'))
2067 self.assertFalse(cfg.getboolean('two', 'one'))
2068
2069 def _test_len(self, cfg):
2070 self.assertEqual(len(cfg.converters), 4)
2071 self.assertIn('boolean', cfg.converters)
2072 self.assertIn('len', cfg.converters)
2073 self.assertNotIn('tysburg', cfg.converters)
2074 self.assertIsNone(cfg.converters['int'])
2075 self.assertIsNone(cfg.converters['float'])
2076 self.assertIsNone(cfg.converters['boolean'])
2077 self.assertEqual(cfg.getlen('one', 'one'), 5)
2078 self.assertEqual(cfg.getlen('one', 'two'), 5)
2079 self.assertEqual(cfg.getlen('one', 'three'), 16)
2080 self.assertEqual(cfg.getlen('two', 'one'), 5)
2081 self.assertEqual(cfg.getlen('two', 'two'), 5)
2082 self.assertEqual(cfg.getlen('two', 'three'), 4)
2083 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2084 with self.assertRaises(configparser.NoOptionError):
2085 cfg.getlen('two', 'four')
2086 self.assertEqual(cfg['one'].getlen('one'), 5)
2087 self.assertEqual(cfg['one'].getlen('two'), 5)
2088 self.assertEqual(cfg['one'].getlen('three'), 16)
2089 self.assertEqual(cfg['two'].getlen('one'), 5)
2090 self.assertEqual(cfg['two'].getlen('two'), 5)
2091 self.assertEqual(cfg['two'].getlen('three'), 4)
2092 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2093 self.assertEqual(cfg['two'].getlen('four'), None)
2094
2095 def test_instance_assignment(self):
2096 cfg = configparser.ConfigParser()
2097 cfg.getboolean = lambda section, option: True
2098 cfg.getlen = lambda section, option: len(cfg[section][option])
2099 cfg.read_string(self.config)
2100 self.assertEqual(len(cfg.converters), 3)
2101 self.assertIn('boolean', cfg.converters)
2102 self.assertNotIn('len', cfg.converters)
2103 self.assertIsNone(cfg.converters['int'])
2104 self.assertIsNone(cfg.converters['float'])
2105 self.assertIsNone(cfg.converters['boolean'])
2106 self.assertTrue(cfg.getboolean('one', 'one'))
2107 self.assertTrue(cfg.getboolean('two', 'two'))
2108 self.assertTrue(cfg.getboolean('one', 'two'))
2109 self.assertTrue(cfg.getboolean('two', 'one'))
2110 cfg.converters['boolean'] = cfg._convert_to_boolean
2111 self.assertFalse(cfg.getboolean('one', 'one'))
2112 self.assertFalse(cfg.getboolean('two', 'two'))
2113 self.assertFalse(cfg.getboolean('one', 'two'))
2114 self.assertFalse(cfg.getboolean('two', 'one'))
2115 self.assertEqual(cfg.getlen('one', 'one'), 5)
2116 self.assertEqual(cfg.getlen('one', 'two'), 5)
2117 self.assertEqual(cfg.getlen('one', 'three'), 16)
2118 self.assertEqual(cfg.getlen('two', 'one'), 5)
2119 self.assertEqual(cfg.getlen('two', 'two'), 5)
2120 self.assertEqual(cfg.getlen('two', 'three'), 4)
2121 # If a getter impl is assigned straight to the instance, it won't
2122 # be available on the section proxies.
2123 with self.assertRaises(AttributeError):
2124 self.assertEqual(cfg['one'].getlen('one'), 5)
2125 with self.assertRaises(AttributeError):
2126 self.assertEqual(cfg['two'].getlen('one'), 5)
2127
2128
Martin Panter2b9b70b2016-09-09 06:46:48 +00002129class MiscTestCase(unittest.TestCase):
2130 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02002131 support.check__all__(self, configparser, not_exported={"Error"})
Martin Panter2b9b70b2016-09-09 06:46:48 +00002132
2133
Ezio Melottidc1fa802013-01-11 06:30:57 +02002134if __name__ == '__main__':
2135 unittest.main()