blob: e9b03e6c62ef141e4644f54cd2add2b6713a1713 [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',
Miss Islington (bot)1cc67692021-07-13 07:35:30 -070082 'This One Has A ] In It',
Fred Drake03c44a32010-02-19 06:08:41 +000083 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000084
Fred Drake03c44a32010-02-19 06:08:41 +000085 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000086 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000087 E.sort()
Łukasz Langa71b37a52010-12-17 21:56:32 +000088 F = [('baz', 'qwe'), ('foo', 'bar3')]
Łukasz Langa26d513c2010-11-10 18:57:39 +000089
90 # API access
91 L = cf.sections()
92 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000093 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000094 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +000095 L = cf.items('Spacey Bar From The Beginning')
96 L.sort()
97 eq(L, F)
Fred Drake8ef67672000-09-27 22:45:25 +000098
Łukasz Langa26d513c2010-11-10 18:57:39 +000099 # mapping access
100 L = [section for section in cf]
101 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +0000102 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000103 E.sort()
104 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000105 L = cf['Spacey Bar From The Beginning'].items()
106 L = sorted(list(L))
107 eq(L, F)
108 L = cf.items()
109 L = sorted(list(L))
110 self.assertEqual(len(L), len(E))
111 for name, section in L:
112 eq(name, section.name)
113 eq(cf.defaults(), cf[self.default_section])
Łukasz Langa26d513c2010-11-10 18:57:39 +0000114
Fred Drakec6f28912002-10-25 19:40:49 +0000115 # The use of spaces in the section names serves as a
116 # regression test for SourceForge bug #583248:
117 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +0000118
119 # API access
120 eq(cf.get('Foo Bar', 'foo'), 'bar1')
121 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
122 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000123 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000124 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000125 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000126 eq(cf.get('Spaces', 'key with spaces'), 'value')
127 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000128 eq(cf.getint('Types', 'int'), 42)
129 eq(cf.get('Types', 'int'), "42")
130 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
131 eq(cf.get('Types', 'float'), "0.44")
132 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000133 eq(cf.get('Types', '123'), 'strange but acceptable')
Miss Islington (bot)1cc67692021-07-13 07:35:30 -0700134 eq(cf.get('This One Has A ] In It', 'forks'), 'spoons')
Fred Drake03c44a32010-02-19 06:08:41 +0000135 if self.allow_no_value:
136 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000137
Łukasz Langa26d513c2010-11-10 18:57:39 +0000138 # test vars= and fallback=
139 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000140 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
141 with self.assertRaises(configparser.NoSectionError):
142 cf.get('No Such Foo Bar', 'foo')
143 with self.assertRaises(configparser.NoOptionError):
144 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000145 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
146 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
147 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
148 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
149 eq(cf.getint('Types', 'int', fallback=18), 42)
150 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
151 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000152 with self.assertRaises(configparser.NoOptionError):
153 cf.getint('Types', 'no-such-int')
Fred Drakecc645b92010-09-04 04:35:34 +0000154 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000155 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000156 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000157 fallback=0.0), 0.0)
158 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000159 with self.assertRaises(configparser.NoOptionError):
160 cf.getfloat('Types', 'no-such-float')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000161 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
162 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000163 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000164 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000165 with self.assertRaises(configparser.NoOptionError):
166 cf.getboolean('Types', 'no-such-boolean')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000167 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000168 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000169 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000170 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000171 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000172
Łukasz Langa26d513c2010-11-10 18:57:39 +0000173 # mapping access
174 eq(cf['Foo Bar']['foo'], 'bar1')
175 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000176 section = cf['Spacey Bar From The Beginning']
177 eq(section.name, 'Spacey Bar From The Beginning')
178 self.assertIs(section.parser, cf)
179 with self.assertRaises(AttributeError):
180 section.name = 'Name is read-only'
181 with self.assertRaises(AttributeError):
182 section.parser = 'Parser is read-only'
183 eq(section['foo'], 'bar3')
184 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000185 eq(cf['Commented Bar']['foo'], 'bar4')
186 eq(cf['Commented Bar']['baz'], 'qwe')
187 eq(cf['Spaces']['key with spaces'], 'value')
188 eq(cf['Spaces']['another with spaces'], 'splat!')
189 eq(cf['Long Line']['foo'],
190 'this line is much, much longer than my editor\nlikes it.')
191 if self.allow_no_value:
192 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000193 # test vars= and fallback=
194 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
195 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
196 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
197 with self.assertRaises(KeyError):
198 cf['No Such Foo Bar']['foo']
199 with self.assertRaises(KeyError):
200 cf['Foo Bar']['no-such-foo']
201 with self.assertRaises(KeyError):
202 cf['No Such Foo Bar'].get('foo', fallback='baz')
203 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
204 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
Łukasz Langa71b37a52010-12-17 21:56:32 +0000205 eq(cf['Foo Bar'].get('no-such-foo'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000206 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
207 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
208 with self.assertRaises(KeyError):
209 cf['No Such Spacey Bar'].get('foo', None)
210 eq(cf['Types'].getint('int', 18), 42)
211 eq(cf['Types'].getint('int', fallback=18), 42)
212 eq(cf['Types'].getint('no-such-int', 18), 18)
213 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
214 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
215 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000216 eq(cf['Types'].getint('no-such-int'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000217 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
218 self.assertAlmostEqual(cf['Types'].getfloat('float',
219 fallback=0.0), 0.44)
220 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
221 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
222 fallback=0.0), 0.0)
223 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
224 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000225 eq(cf['Types'].getfloat('no-such-float'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000226 eq(cf['Types'].getboolean('boolean', True), False)
227 eq(cf['Types'].getboolean('boolean', fallback=True), False)
228 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
229 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
230 "yes") # sic!
231 eq(cf['Types'].getboolean('no-such-boolean', True), True)
232 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000233 eq(cf['Types'].getboolean('no-such-boolean'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000234 if self.allow_no_value:
235 eq(cf['NoValue'].get('option-without-value', False), None)
236 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
237 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
238 eq(cf['NoValue'].get('no-such-option-without-value',
239 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000240
Łukasz Langa71b37a52010-12-17 21:56:32 +0000241 # Make sure the right things happen for remove_section() and
242 # remove_option(); added to include check for SourceForge bug #123324.
Łukasz Langa26d513c2010-11-10 18:57:39 +0000243
Łukasz Langa71b37a52010-12-17 21:56:32 +0000244 cf[self.default_section]['this_value'] = '1'
245 cf[self.default_section]['that_value'] = '2'
246
247 # API access
248 self.assertTrue(cf.remove_section('Spaces'))
249 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
250 self.assertFalse(cf.remove_section('Spaces'))
251 self.assertFalse(cf.remove_section(self.default_section))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000252 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000253 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000254 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000255 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000256 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000257 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000258 " that was removed")
Łukasz Langa71b37a52010-12-17 21:56:32 +0000259 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
260 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
261 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
262 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
263 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
Fred Drakec6f28912002-10-25 19:40:49 +0000264
Michael Foordbd6c0792010-07-25 23:09:25 +0000265 with self.assertRaises(configparser.NoSectionError) as cm:
266 cf.remove_option('No Such Section', 'foo')
267 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000268
269 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000270 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000271
Łukasz Langa26d513c2010-11-10 18:57:39 +0000272 # mapping access
Łukasz Langa71b37a52010-12-17 21:56:32 +0000273 del cf['Types']
274 self.assertFalse('Types' in cf)
275 with self.assertRaises(KeyError):
276 del cf['Types']
277 with self.assertRaises(ValueError):
278 del cf[self.default_section]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000279 del cf['Spacey Bar']['foo']
280 self.assertFalse('foo' in cf['Spacey Bar'])
281 with self.assertRaises(KeyError):
282 del cf['Spacey Bar']['foo']
Łukasz Langa71b37a52010-12-17 21:56:32 +0000283 self.assertTrue('that_value' in cf['Spacey Bar'])
284 with self.assertRaises(KeyError):
285 del cf['Spacey Bar']['that_value']
286 del cf[self.default_section]['that_value']
287 self.assertFalse('that_value' in cf['Spacey Bar'])
288 with self.assertRaises(KeyError):
289 del cf[self.default_section]['that_value']
Łukasz Langa26d513c2010-11-10 18:57:39 +0000290 with self.assertRaises(KeyError):
291 del cf['No Such Section']['foo']
292
Łukasz Langa71b37a52010-12-17 21:56:32 +0000293 # Don't add new asserts below in this method as most of the options
294 # and sections are now removed.
295
Fred Drakea4923622010-08-09 12:52:45 +0000296 def test_basic(self):
297 config_string = """\
298[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000299foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000300[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000301foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000302[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000303 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000304 baz {0[0]} qwe
305[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000306foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000307baz{0[0]}qwe {1[0]}another one
308[Long Line]
309foo{0[1]} this line is much, much longer than my editor
310 likes it.
311[Section\\with$weird%characters[\t]
312[Internationalized Stuff]
313foo[bg]{0[1]} Bulgarian
314foo{0[0]}Default
315foo[en]{0[0]}English
316foo[de]{0[0]}Deutsch
317[Spaces]
318key with spaces {0[1]} value
319another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000320[Types]
321int {0[1]} 42
322float {0[0]} 0.44
323boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000324123 {0[1]} strange but acceptable
Miss Islington (bot)1cc67692021-07-13 07:35:30 -0700325[This One Has A ] In It]
326 forks {0[0]} spoons
Fred Drakea4923622010-08-09 12:52:45 +0000327""".format(self.delimiters, self.comment_prefixes)
328 if self.allow_no_value:
329 config_string += (
330 "[NoValue]\n"
331 "option-without-value\n"
332 )
333 cf = self.fromstring(config_string)
334 self.basic_test(cf)
335 if self.strict:
336 with self.assertRaises(configparser.DuplicateOptionError):
337 cf.read_string(textwrap.dedent("""\
338 [Duplicate Options Here]
339 option {0[0]} with a value
340 option {0[1]} with another value
341 """.format(self.delimiters)))
342 with self.assertRaises(configparser.DuplicateSectionError):
343 cf.read_string(textwrap.dedent("""\
344 [And Now For Something]
345 completely different {0[0]} True
346 [And Now For Something]
347 the larch {0[1]} 1
348 """.format(self.delimiters)))
349 else:
350 cf.read_string(textwrap.dedent("""\
351 [Duplicate Options Here]
352 option {0[0]} with a value
353 option {0[1]} with another value
354 """.format(self.delimiters)))
355
356 cf.read_string(textwrap.dedent("""\
357 [And Now For Something]
358 completely different {0[0]} True
359 [And Now For Something]
360 the larch {0[1]} 1
361 """.format(self.delimiters)))
362
363 def test_basic_from_dict(self):
364 config = {
365 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000366 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000367 },
368 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000369 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000370 },
371 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000372 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000373 "baz": "qwe",
374 },
375 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000376 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000377 "baz": "qwe",
378 },
379 "Long Line": {
380 "foo": "this line is much, much longer than my editor\nlikes "
381 "it.",
382 },
383 "Section\\with$weird%characters[\t": {
384 },
385 "Internationalized Stuff": {
386 "foo[bg]": "Bulgarian",
387 "foo": "Default",
388 "foo[en]": "English",
389 "foo[de]": "Deutsch",
390 },
391 "Spaces": {
392 "key with spaces": "value",
393 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000394 },
395 "Types": {
396 "int": 42,
397 "float": 0.44,
398 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000399 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000400 },
Miss Islington (bot)1cc67692021-07-13 07:35:30 -0700401 "This One Has A ] In It": {
402 "forks": "spoons"
403 },
Fred Drakea4923622010-08-09 12:52:45 +0000404 }
405 if self.allow_no_value:
406 config.update({
407 "NoValue": {
408 "option-without-value": None,
409 }
410 })
411 cf = self.newconfig()
412 cf.read_dict(config)
413 self.basic_test(cf)
414 if self.strict:
Łukasz Langa71b37a52010-12-17 21:56:32 +0000415 with self.assertRaises(configparser.DuplicateSectionError):
416 cf.read_dict({
417 '1': {'key': 'value'},
418 1: {'key2': 'value2'},
419 })
Fred Drakea4923622010-08-09 12:52:45 +0000420 with self.assertRaises(configparser.DuplicateOptionError):
421 cf.read_dict({
422 "Duplicate Options Here": {
423 'option': 'with a value',
424 'OPTION': 'with another value',
425 },
426 })
427 else:
428 cf.read_dict({
Łukasz Langa71b37a52010-12-17 21:56:32 +0000429 'section': {'key': 'value'},
430 'SECTION': {'key2': 'value2'},
431 })
432 cf.read_dict({
Fred Drakea4923622010-08-09 12:52:45 +0000433 "Duplicate Options Here": {
434 'option': 'with a value',
435 'OPTION': 'with another value',
436 },
437 })
438
Fred Drakec6f28912002-10-25 19:40:49 +0000439 def test_case_sensitivity(self):
440 cf = self.newconfig()
441 cf.add_section("A")
442 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000443 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000444 L = cf.sections()
445 L.sort()
446 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000447 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000448 cf.set("a", "B", "value")
449 eq(cf.options("a"), ["b"])
450 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000451 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000452 with self.assertRaises(configparser.NoSectionError):
453 # section names are case-sensitive
454 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000455 self.assertTrue(cf.has_option("a", "b"))
Łukasz Langa71b37a52010-12-17 21:56:32 +0000456 self.assertFalse(cf.has_option("b", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000457 cf.set("A", "A-B", "A-B value")
458 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000459 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000460 cf.has_option("A", opt),
461 "has_option() returned false for option which should exist")
462 eq(cf.options("A"), ["a-b"])
463 eq(cf.options("a"), ["b"])
464 cf.remove_option("a", "B")
465 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000466
Fred Drakec6f28912002-10-25 19:40:49 +0000467 # SF bug #432369:
468 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000469 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000470 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000471 eq(cf.options("MySection"), ["option"])
472 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000473
Fred Drakec6f28912002-10-25 19:40:49 +0000474 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000475 cf = self.fromstring("[section]\n"
476 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000477 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000478 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000479
Fred Drake3c823aa2001-02-26 21:55:34 +0000480
Łukasz Langa26d513c2010-11-10 18:57:39 +0000481 def test_case_sensitivity_mapping_access(self):
482 cf = self.newconfig()
483 cf["A"] = {}
484 cf["a"] = {"B": "value"}
485 cf["B"] = {}
486 L = [section for section in cf]
487 L.sort()
488 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000489 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000490 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000491 eq(cf["a"].keys(), {"b"})
492 eq(cf["a"]["b"], "value",
493 "could not locate option, expecting case-insensitive option names")
494 with self.assertRaises(KeyError):
495 # section names are case-sensitive
496 cf["b"]["A"] = "value"
497 self.assertTrue("b" in cf["a"])
498 cf["A"]["A-B"] = "A-B value"
499 for opt in ("a-b", "A-b", "a-B", "A-B"):
500 self.assertTrue(
501 opt in cf["A"],
502 "has_option() returned false for option which should exist")
503 eq(cf["A"].keys(), {"a-b"})
504 eq(cf["a"].keys(), {"b"})
505 del cf["a"]["B"]
506 elem_eq(cf["a"].keys(), {})
507
508 # SF bug #432369:
509 cf = self.fromstring(
510 "[MySection]\nOption{} first line \n\tsecond line \n".format(
511 self.delimiters[0]))
512 eq(cf["MySection"].keys(), {"option"})
513 eq(cf["MySection"]["Option"], "first line\nsecond line")
514
515 # SF bug #561822:
516 cf = self.fromstring("[section]\n"
517 "nekey{}nevalue\n".format(self.delimiters[0]),
518 defaults={"key":"value"})
519 self.assertTrue("Key" in cf["section"])
520
David Goodger68a1abd2004-10-03 15:40:25 +0000521 def test_default_case_sensitivity(self):
522 cf = self.newconfig({"foo": "Bar"})
523 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000524 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000525 "could not locate option, expecting case-insensitive option names")
526 cf = self.newconfig({"Foo": "Bar"})
527 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000528 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000529 "could not locate option, expecting case-insensitive defaults")
530
Fred Drakec6f28912002-10-25 19:40:49 +0000531 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000532 cf = self.newconfig()
533 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000534 "[Foo]\n"
535 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000536 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000537 "[Foo]\n"
538 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000539 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000540 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000541 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000542 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000543 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000544 "[Foo]\n wrong-indent\n")
545 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000546 # read_file on a real file
547 tricky = support.findfile("cfgparser.3")
548 if self.delimiters[0] == '=':
549 error = configparser.ParsingError
550 expected = (tricky,)
551 else:
552 error = configparser.MissingSectionHeaderError
553 expected = (tricky, 1,
554 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000555 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000556 e = self.parse_error(cf, error, f)
557 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000558
Fred Drakea4923622010-08-09 12:52:45 +0000559 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000560 if hasattr(src, 'readline'):
561 sio = src
562 else:
563 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000564 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000565 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000566 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000567
Fred Drakec6f28912002-10-25 19:40:49 +0000568 def test_query_errors(self):
569 cf = self.newconfig()
570 self.assertEqual(cf.sections(), [],
571 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000572 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000573 "new ConfigParser should have no acknowledged "
574 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000575 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000576 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000577 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000578 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000579 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000580 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000581 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000582 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000583 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000584
Fred Drakea4923622010-08-09 12:52:45 +0000585 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000586 try:
Fred Drakea4923622010-08-09 12:52:45 +0000587 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000588 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000589 return e
590 else:
591 self.fail("expected exception type %s.%s"
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300592 % (exc.__module__, exc.__qualname__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000593
Fred Drakec6f28912002-10-25 19:40:49 +0000594 def test_boolean(self):
595 cf = self.fromstring(
596 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000597 "T1{equals}1\n"
598 "T2{equals}TRUE\n"
599 "T3{equals}True\n"
600 "T4{equals}oN\n"
601 "T5{equals}yes\n"
602 "F1{equals}0\n"
603 "F2{equals}FALSE\n"
604 "F3{equals}False\n"
605 "F4{equals}oFF\n"
606 "F5{equals}nO\n"
607 "E1{equals}2\n"
608 "E2{equals}foo\n"
609 "E3{equals}-1\n"
610 "E4{equals}0.1\n"
611 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000612 )
613 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000614 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
615 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000616 self.assertRaises(ValueError,
617 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000618
Fred Drakec6f28912002-10-25 19:40:49 +0000619 def test_weird_errors(self):
620 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000621 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000622 with self.assertRaises(configparser.DuplicateSectionError) as cm:
623 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000624 e = cm.exception
625 self.assertEqual(str(e), "Section 'Foo' already exists")
626 self.assertEqual(e.args, ("Foo", None, None))
627
628 if self.strict:
629 with self.assertRaises(configparser.DuplicateSectionError) as cm:
630 cf.read_string(textwrap.dedent("""\
631 [Foo]
632 will this be added{equals}True
633 [Bar]
634 what about this{equals}True
635 [Foo]
636 oops{equals}this won't
637 """.format(equals=self.delimiters[0])), source='<foo-bar>')
638 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200639 self.assertEqual(str(e), "While reading from '<foo-bar>' "
640 "[line 5]: section 'Foo' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000641 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
642
643 with self.assertRaises(configparser.DuplicateOptionError) as cm:
644 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
645 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200646 self.assertEqual(str(e), "While reading from '<dict>': option "
647 "'opt' in section 'Bar' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000648 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000649
650 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000651 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000652 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000653 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000654 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000655 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000656 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000657 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000658 "[Long Line - With Comments!]\n"
659 "test {0[1]} we {comment} can\n"
660 " also {comment} place\n"
661 " comments {comment} in\n"
662 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000663 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
664 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000665 )
Fred Drake03c44a32010-02-19 06:08:41 +0000666 if self.allow_no_value:
667 config_string += (
668 "[Valueless]\n"
669 "option-without-value\n"
670 )
671
672 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000673 for space_around_delimiters in (True, False):
674 output = io.StringIO()
675 cf.write(output, space_around_delimiters=space_around_delimiters)
676 delimiter = self.delimiters[0]
677 if space_around_delimiters:
678 delimiter = " {} ".format(delimiter)
679 expect_string = (
680 "[{default_section}]\n"
681 "foo{equals}another very\n"
682 "\tlong line\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000683 "\n"
Łukasz Langa71b37a52010-12-17 21:56:32 +0000684 "[Long Line]\n"
685 "foo{equals}this line is much, much longer than my editor\n"
686 "\tlikes it.\n"
687 "\n"
688 "[Long Line - With Comments!]\n"
689 "test{equals}we\n"
690 "\talso\n"
691 "\tcomments\n"
692 "\tmultiline\n"
693 "\n".format(equals=delimiter,
694 default_section=self.default_section)
Fred Drake03c44a32010-02-19 06:08:41 +0000695 )
Łukasz Langa71b37a52010-12-17 21:56:32 +0000696 if self.allow_no_value:
697 expect_string += (
698 "[Valueless]\n"
699 "option-without-value\n"
700 "\n"
701 )
702 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000703
Fred Drakeabc086f2004-05-18 03:29:52 +0000704 def test_set_string_types(self):
705 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000706 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000707 # Check that we don't get an exception when setting values in
708 # an existing section using strings:
709 class mystr(str):
710 pass
711 cf.set("sect", "option1", "splat")
712 cf.set("sect", "option1", mystr("splat"))
713 cf.set("sect", "option2", "splat")
714 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000715 cf.set("sect", "option1", "splat")
716 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000717
Fred Drake82903142004-05-18 04:24:02 +0000718 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000719 if self.delimiters[0] != '=':
Zachary Ware9fe6d862013-12-08 00:20:35 -0600720 self.skipTest('incompatible format')
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000721 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000722 # check when we pass a mix of readable and non-readable files:
723 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900724 parsed_files = cf.read([file1, "nonexistent-file"], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000725 self.assertEqual(parsed_files, [file1])
726 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
727 # check when we pass only a filename:
728 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900729 parsed_files = cf.read(file1, encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000730 self.assertEqual(parsed_files, [file1])
731 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
David Ellis85b8d012017-03-03 17:14:27 +0000732 # check when we pass only a Path object:
733 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900734 parsed_files = cf.read(pathlib.Path(file1), encoding="utf-8")
David Ellis85b8d012017-03-03 17:14:27 +0000735 self.assertEqual(parsed_files, [file1])
736 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
737 # check when we passed both a filename and a Path object:
738 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900739 parsed_files = cf.read([pathlib.Path(file1), file1], encoding="utf-8")
David Ellis85b8d012017-03-03 17:14:27 +0000740 self.assertEqual(parsed_files, [file1, file1])
741 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
Fred Drake82903142004-05-18 04:24:02 +0000742 # check when we pass only missing files:
743 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900744 parsed_files = cf.read(["nonexistent-file"], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000745 self.assertEqual(parsed_files, [])
746 # check when we pass no files:
747 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900748 parsed_files = cf.read([], encoding="utf-8")
Fred Drake82903142004-05-18 04:24:02 +0000749 self.assertEqual(parsed_files, [])
750
Vincent Michele3148532017-11-02 13:47:04 +0100751 def test_read_returns_file_list_with_bytestring_path(self):
752 if self.delimiters[0] != '=':
753 self.skipTest('incompatible format')
754 file1_bytestring = support.findfile("cfgparser.1").encode()
755 # check when passing an existing bytestring path
756 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900757 parsed_files = cf.read(file1_bytestring, encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100758 self.assertEqual(parsed_files, [file1_bytestring])
759 # check when passing an non-existing bytestring path
760 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900761 parsed_files = cf.read(b'nonexistent-file', encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100762 self.assertEqual(parsed_files, [])
763 # check when passing both an existing and non-existing bytestring path
764 cf = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +0900765 parsed_files = cf.read([file1_bytestring, b'nonexistent-file'], encoding="utf-8")
Vincent Michele3148532017-11-02 13:47:04 +0100766 self.assertEqual(parsed_files, [file1_bytestring])
767
Fred Drakec6f28912002-10-25 19:40:49 +0000768 # shared by subclasses
769 def get_interpolation_config(self):
770 return self.fromstring(
771 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000772 "bar{equals}something %(with1)s interpolation (1 step)\n"
773 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
774 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
775 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
776 "with11{equals}%(with10)s\n"
777 "with10{equals}%(with9)s\n"
778 "with9{equals}%(with8)s\n"
779 "with8{equals}%(With7)s\n"
780 "with7{equals}%(WITH6)s\n"
781 "with6{equals}%(with5)s\n"
782 "With5{equals}%(with4)s\n"
783 "WITH4{equals}%(with3)s\n"
784 "with3{equals}%(with2)s\n"
785 "with2{equals}%(with1)s\n"
786 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000787 "\n"
788 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000789 "foo{equals}%(bar)s\n"
790 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000791 "\n"
792 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000793 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000794 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000795
Fred Drake98e3b292002-10-25 20:42:44 +0000796 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000797 cf = self.fromstring("""
798 [section]
799 name {0[0]} %(value)s
800 key{0[1]} |%(name)s|
801 getdefault{0[1]} |%(default)s|
802 """.format(self.delimiters), defaults={"default": "<default>"})
803 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000804 L.sort()
805 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000806 with self.assertRaises(configparser.NoSectionError):
807 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000808
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100809 def test_popitem(self):
810 cf = self.fromstring("""
811 [section1]
812 name1 {0[0]} value1
813 [section2]
814 name2 {0[0]} value2
815 [section3]
816 name3 {0[0]} value3
817 """.format(self.delimiters), defaults={"default": "<default>"})
818 self.assertEqual(cf.popitem()[0], 'section1')
819 self.assertEqual(cf.popitem()[0], 'section2')
820 self.assertEqual(cf.popitem()[0], 'section3')
821 with self.assertRaises(KeyError):
822 cf.popitem()
823
824 def test_clear(self):
825 cf = self.newconfig({"foo": "Bar"})
826 self.assertEqual(
827 cf.get(self.default_section, "Foo"), "Bar",
828 "could not locate option, expecting case-insensitive option names")
829 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
830 self.assertEqual(cf.sections(), ['zing'])
831 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
832 cf.clear()
833 self.assertEqual(set(cf.sections()), set())
834 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
835
Łukasz Langa02101942012-12-31 13:55:11 +0100836 def test_setitem(self):
837 cf = self.fromstring("""
838 [section1]
839 name1 {0[0]} value1
840 [section2]
841 name2 {0[0]} value2
842 [section3]
843 name3 {0[0]} value3
844 """.format(self.delimiters), defaults={"nameD": "valueD"})
845 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
846 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
847 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
848 self.assertEqual(cf['section1']['name1'], 'value1')
849 self.assertEqual(cf['section2']['name2'], 'value2')
850 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100851 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100852 cf['section2'] = {'name22': 'value22'}
853 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
854 self.assertEqual(cf['section2']['name22'], 'value22')
855 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100856 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100857 cf['section3'] = {}
858 self.assertEqual(set(cf['section3'].keys()), {'named'})
859 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100860 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Cheryl Sabella33cd0582018-06-12 16:37:51 -0400861 # For bpo-32108, assigning default_section to itself.
862 cf[self.default_section] = cf[self.default_section]
863 self.assertNotEqual(set(cf[self.default_section].keys()), set())
Łukasz Langa02101942012-12-31 13:55:11 +0100864 cf[self.default_section] = {}
865 self.assertEqual(set(cf[self.default_section].keys()), set())
866 self.assertEqual(set(cf['section1'].keys()), {'name1'})
867 self.assertEqual(set(cf['section2'].keys()), {'name22'})
868 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100869 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Cheryl Sabella33cd0582018-06-12 16:37:51 -0400870 # For bpo-32108, assigning section to itself.
871 cf['section2'] = cf['section2']
872 self.assertEqual(set(cf['section2'].keys()), {'name22'})
Łukasz Langa02101942012-12-31 13:55:11 +0100873
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800874 def test_invalid_multiline_value(self):
875 if self.allow_no_value:
876 self.skipTest('if no_value is allowed, ParsingError is not raised')
877
878 invalid = textwrap.dedent("""\
879 [DEFAULT]
880 test {0} test
881 invalid""".format(self.delimiters[0])
882 )
883 cf = self.newconfig()
884 with self.assertRaises(configparser.ParsingError):
885 cf.read_string(invalid)
886 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
887 self.assertEqual(cf['DEFAULT']['test'], 'test')
888
Fred Drake8ef67672000-09-27 22:45:25 +0000889
Ezio Melottidc1fa802013-01-11 06:30:57 +0200890class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000891 config_class = configparser.RawConfigParser
892 strict = True
893
894
Ezio Melottidc1fa802013-01-11 06:30:57 +0200895class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000896 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000897
898 def test_interpolation(self):
899 cf = self.get_interpolation_config()
900 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000901 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
902 eq(cf.get("Foo", "bar9"),
903 "something with lots of interpolation (9 steps)")
904 eq(cf.get("Foo", "bar10"),
905 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000906 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000907 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200908 self.assertEqual(e.args, ("bar11", "Foo",
909 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000910 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
911 self.assertEqual(e.args, ("bar11", "Foo",
912 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000913
Fred Drake54782192002-12-31 06:57:25 +0000914 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000915 cf = self.get_interpolation_config()
916 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000917 "Interpolation Error", "name")
918 self.assertEqual(e.reference, "reference")
919 self.assertEqual(e.section, "Interpolation Error")
920 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000921 if self.interpolation == configparser._UNSET:
922 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200923 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000924 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
925 self.assertEqual(e.args, ('name', 'Interpolation Error',
926 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000927
Fred Drake98e3b292002-10-25 20:42:44 +0000928 def test_items(self):
929 self.check_items_config([('default', '<default>'),
930 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000931 ('key', '|value|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +0100932 ('name', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000933
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000934 def test_safe_interpolation(self):
935 # See http://www.python.org/sf/511737
936 cf = self.fromstring("[section]\n"
937 "option1{eq}xxx\n"
938 "option2{eq}%(option1)s/xxx\n"
939 "ok{eq}%(option1)s/%%s\n"
940 "not_ok{eq}%(option2)s/%%s".format(
941 eq=self.delimiters[0]))
942 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
943 if self.interpolation == configparser._UNSET:
944 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
945 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
946 with self.assertRaises(TypeError):
947 cf.get("section", "not_ok")
948
949 def test_set_malformatted_interpolation(self):
950 cf = self.fromstring("[sect]\n"
951 "option1{eq}foo\n".format(eq=self.delimiters[0]))
952
953 self.assertEqual(cf.get('sect', "option1"), "foo")
954
955 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
956 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
957 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
958
959 self.assertEqual(cf.get('sect', "option1"), "foo")
960
961 # bug #5741: double percents are *not* malformed
962 cf.set("sect", "option2", "foo%%bar")
963 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
964
David Goodger1cbf2062004-10-03 15:55:09 +0000965 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000966 cf = self.fromstring("[sect]\n"
967 "option1{eq}foo\n".format(eq=self.delimiters[0]))
968 # Check that we get a TypeError when setting non-string values
969 # in an existing section:
970 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
971 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
972 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
973 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
974 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
975 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
976 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
977 self.assertRaises(TypeError, cf.add_section, 123)
978
979 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000980 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000981 self.assertRaises(ValueError, cf.add_section, self.default_section)
982
Łukasz Langaa5fab172017-08-24 09:43:53 -0700983 def test_defaults_keyword(self):
984 """bpo-23835 fix for ConfigParser"""
985 cf = self.newconfig(defaults={1: 2.4})
986 self.assertEqual(cf[self.default_section]['1'], '2.4')
987 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
988 cf = self.newconfig(defaults={"A": 5.2})
989 self.assertEqual(cf[self.default_section]['a'], '5.2')
990 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
991
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200992
Ezio Melottidc1fa802013-01-11 06:30:57 +0200993class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200994 config_class = configparser.ConfigParser
995 interpolation = None
996 ini = textwrap.dedent("""
997 [numbers]
998 one = 1
999 two = %(one)s * 2
1000 three = ${common:one} * 3
1001
1002 [hexen]
1003 sixteen = ${numbers:two} * 8
1004 """).strip()
1005
1006 def assertMatchesIni(self, cf):
1007 self.assertEqual(cf['numbers']['one'], '1')
1008 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
1009 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
1010 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
1011
1012 def test_no_interpolation(self):
1013 cf = self.fromstring(self.ini)
1014 self.assertMatchesIni(cf)
1015
1016 def test_empty_case(self):
1017 cf = self.newconfig()
1018 self.assertIsNone(cf.read_string(""))
1019
1020 def test_none_as_default_interpolation(self):
1021 class CustomConfigParser(configparser.ConfigParser):
1022 _DEFAULT_INTERPOLATION = None
1023
1024 cf = CustomConfigParser()
1025 cf.read_string(self.ini)
1026 self.assertMatchesIni(cf)
1027
1028
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001029class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
1030 config_class = configparser.ConfigParser
1031 interpolation = configparser.LegacyInterpolation()
1032
1033 def test_set_malformatted_interpolation(self):
1034 cf = self.fromstring("[sect]\n"
1035 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1036
1037 self.assertEqual(cf.get('sect', "option1"), "foo")
1038
1039 cf.set("sect", "option1", "%foo")
1040 self.assertEqual(cf.get('sect', "option1"), "%foo")
1041 cf.set("sect", "option1", "foo%")
1042 self.assertEqual(cf.get('sect', "option1"), "foo%")
1043 cf.set("sect", "option1", "f%oo")
1044 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1045
1046 # bug #5741: double percents are *not* malformed
1047 cf.set("sect", "option2", "foo%%bar")
1048 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001049
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001050
Georg Brandl96a60ae2010-07-28 13:13:46 +00001051class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1052 delimiters = (':=', '$')
1053 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001054 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001055
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001056
Łukasz Langac264c092010-11-20 16:15:37 +00001057class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1058 default_section = 'general'
1059
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001060
Ezio Melottidc1fa802013-01-11 06:30:57 +02001061class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001062 config_class = configparser.ConfigParser
1063 wonderful_spam = ("I'm having spam spam spam spam "
1064 "spam spam spam beaked beans spam "
1065 "spam spam and spam!").replace(' ', '\t\n')
1066
1067 def setUp(self):
1068 cf = self.newconfig()
1069 for i in range(100):
1070 s = 'section{}'.format(i)
1071 cf.add_section(s)
1072 for j in range(10):
1073 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
Inada Naoki35715d12021-04-04 09:01:23 +09001074 with open(os_helper.TESTFN, 'w', encoding="utf-8") as f:
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001075 cf.write(f)
1076
1077 def tearDown(self):
Hai Shia7f5d932020-08-04 00:41:24 +08001078 os.unlink(os_helper.TESTFN)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001079
1080 def test_dominating_multiline_values(self):
1081 # We're reading from file because this is where the code changed
1082 # during performance updates in Python 3.2
1083 cf_from_file = self.newconfig()
Inada Naoki35715d12021-04-04 09:01:23 +09001084 with open(os_helper.TESTFN, encoding="utf-8") as f:
Fred Drakea4923622010-08-09 12:52:45 +00001085 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001086 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1087 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001088
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001089
Ezio Melottidc1fa802013-01-11 06:30:57 +02001090class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001091 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001092
1093 def test_interpolation(self):
1094 cf = self.get_interpolation_config()
1095 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001096 eq(cf.get("Foo", "bar"),
1097 "something %(with1)s interpolation (1 step)")
1098 eq(cf.get("Foo", "bar9"),
1099 "something %(with9)s lots of interpolation (9 steps)")
1100 eq(cf.get("Foo", "bar10"),
1101 "something %(with10)s lots of interpolation (10 steps)")
1102 eq(cf.get("Foo", "bar11"),
1103 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001104
Fred Drake98e3b292002-10-25 20:42:44 +00001105 def test_items(self):
1106 self.check_items_config([('default', '<default>'),
1107 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001108 ('key', '|%(name)s|'),
Chris Bradbury1d4a7332018-04-23 20:16:17 +01001109 ('name', '%(value)s')])
Fred Drake98e3b292002-10-25 20:42:44 +00001110
David Goodger1cbf2062004-10-03 15:55:09 +00001111 def test_set_nonstring_types(self):
1112 cf = self.newconfig()
1113 cf.add_section('non-string')
1114 cf.set('non-string', 'int', 1)
1115 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1116 cf.set('non-string', 'dict', {'pi': 3.14159})
1117 self.assertEqual(cf.get('non-string', 'int'), 1)
1118 self.assertEqual(cf.get('non-string', 'list'),
1119 [0, 1, 1, 2, 3, 5, 8, 13])
1120 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001121 cf.add_section(123)
1122 cf.set(123, 'this is sick', True)
1123 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001124 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001125 # would not work for SortedDict; only checking for the most common
John Reese3a5b0d82018-06-05 16:31:33 -07001126 # default dictionary (dict)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001127 cf.optionxform = lambda x: x
1128 cf.set('non-string', 1, 1)
1129 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001130
Łukasz Langaa5fab172017-08-24 09:43:53 -07001131 def test_defaults_keyword(self):
1132 """bpo-23835 legacy behavior for RawConfigParser"""
1133 with self.assertRaises(AttributeError) as ctx:
1134 self.newconfig(defaults={1: 2.4})
1135 err = ctx.exception
1136 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1137 cf = self.newconfig(defaults={"A": 5.2})
1138 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1139
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001140
Georg Brandl96a60ae2010-07-28 13:13:46 +00001141class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1142 delimiters = (':=', '$')
1143 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001144 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001145
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001146
Ezio Melottidc1fa802013-01-11 06:30:57 +02001147class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001148 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001149 comment_prefixes = ('#', ';', '----')
1150 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001151 empty_lines_in_values = False
1152
1153 def test_reading(self):
1154 smbconf = support.findfile("cfgparser.2")
1155 # check when we pass a mix of readable and non-readable files:
1156 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001157 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001158 self.assertEqual(parsed_files, [smbconf])
1159 sections = ['global', 'homes', 'printers',
1160 'print$', 'pdf-generator', 'tmp', 'Agustin']
1161 self.assertEqual(cf.sections(), sections)
1162 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1163 self.assertEqual(cf.getint("global", "max log size"), 50)
1164 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1165 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001166
Ezio Melottidc1fa802013-01-11 06:30:57 +02001167class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001168 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001169 interpolation = configparser.ExtendedInterpolation()
1170 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001171 strict = True
1172
1173 def fromstring(self, string, defaults=None, optionxform=None):
1174 cf = self.newconfig(defaults)
1175 if optionxform:
1176 cf.optionxform = optionxform
1177 cf.read_string(string)
1178 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001179
1180 def test_extended_interpolation(self):
1181 cf = self.fromstring(textwrap.dedent("""
1182 [common]
1183 favourite Beatle = Paul
1184 favourite color = green
1185
1186 [tom]
1187 favourite band = ${favourite color} day
1188 favourite pope = John ${favourite Beatle} II
1189 sequel = ${favourite pope}I
1190
1191 [ambv]
1192 favourite Beatle = George
1193 son of Edward VII = ${favourite Beatle} V
1194 son of George V = ${son of Edward VII}I
1195
1196 [stanley]
1197 favourite Beatle = ${ambv:favourite Beatle}
1198 favourite pope = ${tom:favourite pope}
1199 favourite color = black
1200 favourite state of mind = paranoid
1201 favourite movie = soylent ${common:favourite color}
1202 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1203 """).strip())
1204
1205 eq = self.assertEqual
1206 eq(cf['common']['favourite Beatle'], 'Paul')
1207 eq(cf['common']['favourite color'], 'green')
1208 eq(cf['tom']['favourite Beatle'], 'Paul')
1209 eq(cf['tom']['favourite color'], 'green')
1210 eq(cf['tom']['favourite band'], 'green day')
1211 eq(cf['tom']['favourite pope'], 'John Paul II')
1212 eq(cf['tom']['sequel'], 'John Paul III')
1213 eq(cf['ambv']['favourite Beatle'], 'George')
1214 eq(cf['ambv']['favourite color'], 'green')
1215 eq(cf['ambv']['son of Edward VII'], 'George V')
1216 eq(cf['ambv']['son of George V'], 'George VI')
1217 eq(cf['stanley']['favourite Beatle'], 'George')
1218 eq(cf['stanley']['favourite color'], 'black')
1219 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1220 eq(cf['stanley']['favourite movie'], 'soylent green')
1221 eq(cf['stanley']['favourite pope'], 'John Paul II')
1222 eq(cf['stanley']['favourite song'],
1223 'black sabbath - paranoid')
1224
1225 def test_endless_loop(self):
1226 cf = self.fromstring(textwrap.dedent("""
1227 [one for you]
1228 ping = ${one for me:pong}
1229
1230 [one for me]
1231 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001232
1233 [selfish]
1234 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001235 """).strip())
1236
1237 with self.assertRaises(configparser.InterpolationDepthError):
1238 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001239 with self.assertRaises(configparser.InterpolationDepthError):
1240 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001241
Łukasz Langa71b37a52010-12-17 21:56:32 +00001242 def test_strange_options(self):
1243 cf = self.fromstring("""
1244 [dollars]
1245 $var = $$value
1246 $var2 = ${$var}
1247 ${sick} = cannot interpolate me
1248
1249 [interpolated]
1250 $other = ${dollars:$var}
1251 $trying = ${dollars:${sick}}
1252 """)
1253
1254 self.assertEqual(cf['dollars']['$var'], '$value')
1255 self.assertEqual(cf['interpolated']['$other'], '$value')
1256 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1257 exception_class = configparser.InterpolationMissingOptionError
1258 with self.assertRaises(exception_class) as cm:
1259 cf['interpolated']['$trying']
1260 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001261 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001262
Łukasz Langae698cd52011-04-28 10:58:57 +02001263 def test_case_sensitivity_basic(self):
1264 ini = textwrap.dedent("""
1265 [common]
1266 optionlower = value
1267 OptionUpper = Value
1268
1269 [Common]
1270 optionlower = a better ${common:optionlower}
1271 OptionUpper = A Better ${common:OptionUpper}
1272
1273 [random]
1274 foolower = ${common:optionlower} redefined
1275 FooUpper = ${Common:OptionUpper} Redefined
1276 """).strip()
1277
1278 cf = self.fromstring(ini)
1279 eq = self.assertEqual
1280 eq(cf['common']['optionlower'], 'value')
1281 eq(cf['common']['OptionUpper'], 'Value')
1282 eq(cf['Common']['optionlower'], 'a better value')
1283 eq(cf['Common']['OptionUpper'], 'A Better Value')
1284 eq(cf['random']['foolower'], 'value redefined')
1285 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1286
1287 def test_case_sensitivity_conflicts(self):
1288 ini = textwrap.dedent("""
1289 [common]
1290 option = value
1291 Option = Value
1292
1293 [Common]
1294 option = a better ${common:option}
1295 Option = A Better ${common:Option}
1296
1297 [random]
1298 foo = ${common:option} redefined
1299 Foo = ${Common:Option} Redefined
1300 """).strip()
1301 with self.assertRaises(configparser.DuplicateOptionError):
1302 cf = self.fromstring(ini)
1303
1304 # raw options
1305 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1306 eq = self.assertEqual
1307 eq(cf['common']['option'], 'value')
1308 eq(cf['common']['Option'], 'Value')
1309 eq(cf['Common']['option'], 'a better value')
1310 eq(cf['Common']['Option'], 'A Better Value')
1311 eq(cf['random']['foo'], 'value redefined')
1312 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001313
1314 def test_other_errors(self):
1315 cf = self.fromstring("""
1316 [interpolation fail]
1317 case1 = ${where's the brace
1318 case2 = ${does_not_exist}
1319 case3 = ${wrong_section:wrong_value}
1320 case4 = ${i:like:colon:characters}
1321 case5 = $100 for Fail No 5!
1322 """)
1323
1324 with self.assertRaises(configparser.InterpolationSyntaxError):
1325 cf['interpolation fail']['case1']
1326 with self.assertRaises(configparser.InterpolationMissingOptionError):
1327 cf['interpolation fail']['case2']
1328 with self.assertRaises(configparser.InterpolationMissingOptionError):
1329 cf['interpolation fail']['case3']
1330 with self.assertRaises(configparser.InterpolationSyntaxError):
1331 cf['interpolation fail']['case4']
1332 with self.assertRaises(configparser.InterpolationSyntaxError):
1333 cf['interpolation fail']['case5']
1334 with self.assertRaises(ValueError):
1335 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001336
1337
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001338class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001339 allow_no_value = True
1340
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001341
Ezio Melottidc1fa802013-01-11 06:30:57 +02001342class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001343 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001344 delimiters = {'='}
1345 comment_prefixes = {'#'}
1346 allow_no_value = True
1347
1348 def test_cfgparser_dot_3(self):
1349 tricky = support.findfile("cfgparser.3")
1350 cf = self.newconfig()
1351 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1352 self.assertEqual(cf.sections(), ['strange',
1353 'corruption',
1354 'yeah, sections can be '
1355 'indented as well',
1356 'another one!',
1357 'no values here',
1358 'tricky interpolation',
1359 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001360 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001361 vars={'interpolate': '-1'}), -1)
1362 with self.assertRaises(ValueError):
1363 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001364 cf.getint(self.default_section, 'go', raw=True,
1365 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001366 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1367 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1368 longname = 'yeah, sections can be indented as well'
1369 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001370 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001371 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1372 # `go` from DEFAULT
1373 with self.assertRaises(configparser.InterpolationMissingOptionError):
1374 cf.items('no values here')
1375 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1376 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1377 cf.get('tricky interpolation', 'go'))
1378 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1379
1380 def test_unicode_failure(self):
1381 tricky = support.findfile("cfgparser.3")
1382 cf = self.newconfig()
1383 with self.assertRaises(UnicodeDecodeError):
1384 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001385
Fred Drake88444412010-09-03 04:22:36 +00001386
1387class Issue7005TestCase(unittest.TestCase):
1388 """Test output when None is set() as a value and allow_no_value == False.
1389
1390 http://bugs.python.org/issue7005
1391
1392 """
1393
1394 expected_output = "[section]\noption = None\n\n"
1395
1396 def prepare(self, config_class):
1397 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001398 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001399 cp.add_section("section")
1400 cp.set("section", "option", None)
1401 sio = io.StringIO()
1402 cp.write(sio)
1403 return sio.getvalue()
1404
1405 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001406 cp = configparser.ConfigParser(allow_no_value=False)
1407 cp.add_section("section")
1408 with self.assertRaises(TypeError):
1409 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001410
1411 def test_none_as_value_stringified_raw(self):
1412 output = self.prepare(configparser.RawConfigParser)
1413 self.assertEqual(output, self.expected_output)
1414
1415
Thomas Wouters89f507f2006-12-13 04:49:30 +00001416class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001417 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001418
1419 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001420 cf = self.fromstring("[b]\n"
1421 "o4=1\n"
1422 "o3=2\n"
1423 "o2=3\n"
1424 "o1=4\n"
1425 "[a]\n"
1426 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001427 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001428 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001429 self.assertEqual(output.getvalue(),
1430 "[a]\n"
1431 "k = v\n\n"
1432 "[b]\n"
1433 "o1 = 4\n"
1434 "o2 = 3\n"
1435 "o3 = 2\n"
1436 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001437
Fred Drake03c44a32010-02-19 06:08:41 +00001438
Ezio Melottidc1fa802013-01-11 06:30:57 +02001439class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001440 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001441 comment_prefixes = '#;'
1442 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001443
1444 def test_comment_handling(self):
1445 config_string = textwrap.dedent("""\
1446 [Commented Bar]
1447 baz=qwe ; a comment
1448 foo: bar # not a comment!
1449 # but this is a comment
1450 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001451 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001452 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001453 """)
1454 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001455 self.assertEqual(cf.get('Commented Bar', 'foo'),
1456 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001457 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001458 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1459 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001460
Ezio Melottidc1fa802013-01-11 06:30:57 +02001461class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001462 config_class = configparser.ConfigParser
1463
1464 def fromstring(self, string, defaults=None):
1465 cf = self.newconfig(defaults)
1466 cf.read_string(string)
1467 cf_copy = self.newconfig()
1468 cf_copy.read_dict(cf)
1469 # we have to clean up option duplicates that appeared because of
1470 # the magic DEFAULTSECT behaviour.
1471 for section in cf_copy.values():
1472 if section.name == self.default_section:
1473 continue
1474 for default, value in cf[self.default_section].items():
1475 if section[default] == value:
1476 del section[default]
1477 return cf_copy
1478
Łukasz Langadaab1c82011-04-27 18:10:05 +02001479
1480class FakeFile:
1481 def __init__(self):
1482 file_path = support.findfile("cfgparser.1")
Inada Naoki35715d12021-04-04 09:01:23 +09001483 with open(file_path, encoding="utf-8") as f:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001484 self.lines = f.readlines()
1485 self.lines.reverse()
1486
1487 def readline(self):
1488 if len(self.lines):
1489 return self.lines.pop()
1490 return ''
1491
1492
1493def readline_generator(f):
1494 """As advised in Doc/library/configparser.rst."""
1495 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001496 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001497 yield line
1498 line = f.readline()
1499
1500
1501class ReadFileTestCase(unittest.TestCase):
1502 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001503 file_paths = [support.findfile("cfgparser.1")]
1504 try:
1505 file_paths.append(file_paths[0].encode('utf8'))
1506 except UnicodeEncodeError:
1507 pass # unfortunately we can't test bytes on this path
1508 for file_path in file_paths:
1509 parser = configparser.ConfigParser()
Inada Naoki35715d12021-04-04 09:01:23 +09001510 with open(file_path, encoding="utf-8") as f:
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001511 parser.read_file(f)
1512 self.assertIn("Foo Bar", parser)
1513 self.assertIn("foo", parser["Foo Bar"])
1514 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001515
1516 def test_iterable(self):
1517 lines = textwrap.dedent("""
1518 [Foo Bar]
1519 foo=newbar""").strip().split('\n')
1520 parser = configparser.ConfigParser()
1521 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001522 self.assertIn("Foo Bar", parser)
1523 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001524 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1525
1526 def test_readline_generator(self):
1527 """Issue #11670."""
1528 parser = configparser.ConfigParser()
1529 with self.assertRaises(TypeError):
1530 parser.read_file(FakeFile())
1531 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001532 self.assertIn("Foo Bar", parser)
1533 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001534 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1535
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001536 def test_source_as_bytes(self):
1537 """Issue #18260."""
1538 lines = textwrap.dedent("""
1539 [badbad]
1540 [badbad]""").strip().split('\n')
1541 parser = configparser.ConfigParser()
1542 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1543 parser.read_file(lines, source=b"badbad")
1544 self.assertEqual(
1545 str(dse.exception),
1546 "While reading from b'badbad' [line 2]: section 'badbad' "
1547 "already exists"
1548 )
1549 lines = textwrap.dedent("""
1550 [badbad]
1551 bad = bad
1552 bad = bad""").strip().split('\n')
1553 parser = configparser.ConfigParser()
1554 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1555 parser.read_file(lines, source=b"badbad")
1556 self.assertEqual(
1557 str(dse.exception),
1558 "While reading from b'badbad' [line 3]: option 'bad' in section "
1559 "'badbad' already exists"
1560 )
1561 lines = textwrap.dedent("""
1562 [badbad]
1563 = bad""").strip().split('\n')
1564 parser = configparser.ConfigParser()
1565 with self.assertRaises(configparser.ParsingError) as dse:
1566 parser.read_file(lines, source=b"badbad")
1567 self.assertEqual(
1568 str(dse.exception),
1569 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1570 )
1571 lines = textwrap.dedent("""
1572 [badbad
1573 bad = bad""").strip().split('\n')
1574 parser = configparser.ConfigParser()
1575 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1576 parser.read_file(lines, source=b"badbad")
1577 self.assertEqual(
1578 str(dse.exception),
1579 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1580 "'[badbad'"
1581 )
1582
Łukasz Langadaab1c82011-04-27 18:10:05 +02001583
Łukasz Langa71b37a52010-12-17 21:56:32 +00001584class CoverageOneHundredTestCase(unittest.TestCase):
1585 """Covers edge cases in the codebase."""
1586
1587 def test_duplicate_option_error(self):
1588 error = configparser.DuplicateOptionError('section', 'option')
1589 self.assertEqual(error.section, 'section')
1590 self.assertEqual(error.option, 'option')
1591 self.assertEqual(error.source, None)
1592 self.assertEqual(error.lineno, None)
1593 self.assertEqual(error.args, ('section', 'option', None, None))
1594 self.assertEqual(str(error), "Option 'option' in section 'section' "
1595 "already exists")
1596
1597 def test_interpolation_depth_error(self):
1598 error = configparser.InterpolationDepthError('option', 'section',
1599 'rawval')
1600 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1601 self.assertEqual(error.option, 'option')
1602 self.assertEqual(error.section, 'section')
1603
1604 def test_parsing_error(self):
1605 with self.assertRaises(ValueError) as cm:
1606 configparser.ParsingError()
1607 self.assertEqual(str(cm.exception), "Required argument `source' not "
1608 "given.")
1609 with self.assertRaises(ValueError) as cm:
1610 configparser.ParsingError(source='source', filename='filename')
1611 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1612 "and `source'. Use `source'.")
1613 error = configparser.ParsingError(filename='source')
1614 self.assertEqual(error.source, 'source')
1615 with warnings.catch_warnings(record=True) as w:
1616 warnings.simplefilter("always", DeprecationWarning)
1617 self.assertEqual(error.filename, 'source')
1618 error.filename = 'filename'
1619 self.assertEqual(error.source, 'filename')
1620 for warning in w:
1621 self.assertTrue(warning.category is DeprecationWarning)
1622
1623 def test_interpolation_validation(self):
1624 parser = configparser.ConfigParser()
1625 parser.read_string("""
1626 [section]
1627 invalid_percent = %
1628 invalid_reference = %(()
1629 invalid_variable = %(does_not_exist)s
1630 """)
1631 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1632 parser['section']['invalid_percent']
1633 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1634 "'(', found: '%'")
1635 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1636 parser['section']['invalid_reference']
1637 self.assertEqual(str(cm.exception), "bad interpolation variable "
1638 "reference '%(()'")
1639
1640 def test_readfp_deprecation(self):
1641 sio = io.StringIO("""
1642 [section]
1643 option = value
1644 """)
1645 parser = configparser.ConfigParser()
1646 with warnings.catch_warnings(record=True) as w:
1647 warnings.simplefilter("always", DeprecationWarning)
1648 parser.readfp(sio, filename='StringIO')
1649 for warning in w:
1650 self.assertTrue(warning.category is DeprecationWarning)
1651 self.assertEqual(len(parser), 2)
1652 self.assertEqual(parser['section']['option'], 'value')
1653
1654 def test_safeconfigparser_deprecation(self):
1655 with warnings.catch_warnings(record=True) as w:
1656 warnings.simplefilter("always", DeprecationWarning)
1657 parser = configparser.SafeConfigParser()
1658 for warning in w:
1659 self.assertTrue(warning.category is DeprecationWarning)
1660
1661 def test_sectionproxy_repr(self):
1662 parser = configparser.ConfigParser()
1663 parser.read_string("""
1664 [section]
1665 key = value
1666 """)
1667 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001668
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001669 def test_inconsistent_converters_state(self):
1670 parser = configparser.ConfigParser()
1671 import decimal
1672 parser.converters['decimal'] = decimal.Decimal
1673 parser.read_string("""
1674 [s1]
1675 one = 1
1676 [s2]
1677 two = 2
1678 """)
1679 self.assertIn('decimal', parser.converters)
1680 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1681 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1682 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1683 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1684 del parser.getdecimal
1685 with self.assertRaises(AttributeError):
1686 parser.getdecimal('s1', 'one')
1687 self.assertIn('decimal', parser.converters)
1688 del parser.converters['decimal']
1689 self.assertNotIn('decimal', parser.converters)
1690 with self.assertRaises(AttributeError):
1691 parser.getdecimal('s1', 'one')
1692 with self.assertRaises(AttributeError):
1693 parser['s1'].getdecimal('one')
1694 with self.assertRaises(AttributeError):
1695 parser['s2'].getdecimal('two')
1696
Łukasz Langae7851952012-01-20 14:57:55 +01001697
1698class ExceptionPicklingTestCase(unittest.TestCase):
1699 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1700
1701 def test_error(self):
1702 import pickle
1703 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001704 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1705 pickled = pickle.dumps(e1, proto)
1706 e2 = pickle.loads(pickled)
1707 self.assertEqual(e1.message, e2.message)
1708 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001709
1710 def test_nosectionerror(self):
1711 import pickle
1712 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001713 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1714 pickled = pickle.dumps(e1, proto)
1715 e2 = pickle.loads(pickled)
1716 self.assertEqual(e1.message, e2.message)
1717 self.assertEqual(e1.args, e2.args)
1718 self.assertEqual(e1.section, e2.section)
1719 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001720
1721 def test_nooptionerror(self):
1722 import pickle
1723 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001724 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1725 pickled = pickle.dumps(e1, proto)
1726 e2 = pickle.loads(pickled)
1727 self.assertEqual(e1.message, e2.message)
1728 self.assertEqual(e1.args, e2.args)
1729 self.assertEqual(e1.section, e2.section)
1730 self.assertEqual(e1.option, e2.option)
1731 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001732
1733 def test_duplicatesectionerror(self):
1734 import pickle
1735 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001736 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1737 pickled = pickle.dumps(e1, proto)
1738 e2 = pickle.loads(pickled)
1739 self.assertEqual(e1.message, e2.message)
1740 self.assertEqual(e1.args, e2.args)
1741 self.assertEqual(e1.section, e2.section)
1742 self.assertEqual(e1.source, e2.source)
1743 self.assertEqual(e1.lineno, e2.lineno)
1744 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001745
1746 def test_duplicateoptionerror(self):
1747 import pickle
1748 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1749 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001750 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1751 pickled = pickle.dumps(e1, proto)
1752 e2 = pickle.loads(pickled)
1753 self.assertEqual(e1.message, e2.message)
1754 self.assertEqual(e1.args, e2.args)
1755 self.assertEqual(e1.section, e2.section)
1756 self.assertEqual(e1.option, e2.option)
1757 self.assertEqual(e1.source, e2.source)
1758 self.assertEqual(e1.lineno, e2.lineno)
1759 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001760
1761 def test_interpolationerror(self):
1762 import pickle
1763 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001764 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1765 pickled = pickle.dumps(e1, proto)
1766 e2 = pickle.loads(pickled)
1767 self.assertEqual(e1.message, e2.message)
1768 self.assertEqual(e1.args, e2.args)
1769 self.assertEqual(e1.section, e2.section)
1770 self.assertEqual(e1.option, e2.option)
1771 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001772
1773 def test_interpolationmissingoptionerror(self):
1774 import pickle
1775 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1776 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001777 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1778 pickled = pickle.dumps(e1, proto)
1779 e2 = pickle.loads(pickled)
1780 self.assertEqual(e1.message, e2.message)
1781 self.assertEqual(e1.args, e2.args)
1782 self.assertEqual(e1.section, e2.section)
1783 self.assertEqual(e1.option, e2.option)
1784 self.assertEqual(e1.reference, e2.reference)
1785 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001786
1787 def test_interpolationsyntaxerror(self):
1788 import pickle
1789 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001790 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1791 pickled = pickle.dumps(e1, proto)
1792 e2 = pickle.loads(pickled)
1793 self.assertEqual(e1.message, e2.message)
1794 self.assertEqual(e1.args, e2.args)
1795 self.assertEqual(e1.section, e2.section)
1796 self.assertEqual(e1.option, e2.option)
1797 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001798
1799 def test_interpolationdeptherror(self):
1800 import pickle
1801 e1 = configparser.InterpolationDepthError('option', 'section',
1802 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001803 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1804 pickled = pickle.dumps(e1, proto)
1805 e2 = pickle.loads(pickled)
1806 self.assertEqual(e1.message, e2.message)
1807 self.assertEqual(e1.args, e2.args)
1808 self.assertEqual(e1.section, e2.section)
1809 self.assertEqual(e1.option, e2.option)
1810 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001811
1812 def test_parsingerror(self):
1813 import pickle
1814 e1 = configparser.ParsingError('source')
1815 e1.append(1, 'line1')
1816 e1.append(2, 'line2')
1817 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001818 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1819 pickled = pickle.dumps(e1, proto)
1820 e2 = pickle.loads(pickled)
1821 self.assertEqual(e1.message, e2.message)
1822 self.assertEqual(e1.args, e2.args)
1823 self.assertEqual(e1.source, e2.source)
1824 self.assertEqual(e1.errors, e2.errors)
1825 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001826 e1 = configparser.ParsingError(filename='filename')
1827 e1.append(1, 'line1')
1828 e1.append(2, 'line2')
1829 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001830 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1831 pickled = pickle.dumps(e1, proto)
1832 e2 = pickle.loads(pickled)
1833 self.assertEqual(e1.message, e2.message)
1834 self.assertEqual(e1.args, e2.args)
1835 self.assertEqual(e1.source, e2.source)
1836 self.assertEqual(e1.errors, e2.errors)
1837 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001838
1839 def test_missingsectionheadererror(self):
1840 import pickle
1841 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001842 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1843 pickled = pickle.dumps(e1, proto)
1844 e2 = pickle.loads(pickled)
1845 self.assertEqual(e1.message, e2.message)
1846 self.assertEqual(e1.args, e2.args)
1847 self.assertEqual(e1.line, e2.line)
1848 self.assertEqual(e1.source, e2.source)
1849 self.assertEqual(e1.lineno, e2.lineno)
1850 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001851
1852
Łukasz Langacba24322012-07-07 18:54:08 +02001853class InlineCommentStrippingTestCase(unittest.TestCase):
1854 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1855 delimiter occurs earlier without preceding space.."""
1856
1857 def test_stripping(self):
1858 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1859 '//'))
1860 cfg.read_string("""
1861 [section]
1862 k1 = v1;still v1
1863 k2 = v2 ;a comment
1864 k3 = v3 ; also a comment
1865 k4 = v4;still v4 ;a comment
1866 k5 = v5;still v5 ; also a comment
1867 k6 = v6;still v6; and still v6 ;a comment
1868 k7 = v7;still v7; and still v7 ; also a comment
1869
1870 [multiprefix]
1871 k1 = v1;still v1 #a comment ; yeah, pretty much
1872 k2 = v2 // this already is a comment ; continued
1873 k3 = v3;#//still v3# and still v3 ; a comment
1874 """)
1875 s = cfg['section']
1876 self.assertEqual(s['k1'], 'v1;still v1')
1877 self.assertEqual(s['k2'], 'v2')
1878 self.assertEqual(s['k3'], 'v3')
1879 self.assertEqual(s['k4'], 'v4;still v4')
1880 self.assertEqual(s['k5'], 'v5;still v5')
1881 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1882 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1883 s = cfg['multiprefix']
1884 self.assertEqual(s['k1'], 'v1;still v1')
1885 self.assertEqual(s['k2'], 'v2')
1886 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1887
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001888
Łukasz Langa949053b2014-09-04 01:36:33 -07001889class ExceptionContextTestCase(unittest.TestCase):
1890 """ Test that implementation details doesn't leak
1891 through raising exceptions. """
1892
1893 def test_get_basic_interpolation(self):
1894 parser = configparser.ConfigParser()
1895 parser.read_string("""
1896 [Paths]
1897 home_dir: /Users
1898 my_dir: %(home_dir1)s/lumberjack
1899 my_pictures: %(my_dir)s/Pictures
1900 """)
1901 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1902 with cm:
1903 parser.get('Paths', 'my_dir')
1904 self.assertIs(cm.exception.__suppress_context__, True)
1905
1906 def test_get_extended_interpolation(self):
1907 parser = configparser.ConfigParser(
1908 interpolation=configparser.ExtendedInterpolation())
1909 parser.read_string("""
1910 [Paths]
1911 home_dir: /Users
1912 my_dir: ${home_dir1}/lumberjack
1913 my_pictures: ${my_dir}/Pictures
1914 """)
1915 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1916 with cm:
1917 parser.get('Paths', 'my_dir')
1918 self.assertIs(cm.exception.__suppress_context__, True)
1919
1920 def test_missing_options(self):
1921 parser = configparser.ConfigParser()
1922 parser.read_string("""
1923 [Paths]
1924 home_dir: /Users
1925 """)
1926 with self.assertRaises(configparser.NoSectionError) as cm:
1927 parser.options('test')
1928 self.assertIs(cm.exception.__suppress_context__, True)
1929
1930 def test_missing_section(self):
1931 config = configparser.ConfigParser()
1932 with self.assertRaises(configparser.NoSectionError) as cm:
1933 config.set('Section1', 'an_int', '15')
1934 self.assertIs(cm.exception.__suppress_context__, True)
1935
1936 def test_remove_option(self):
1937 config = configparser.ConfigParser()
1938 with self.assertRaises(configparser.NoSectionError) as cm:
1939 config.remove_option('Section1', 'an_int')
1940 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001941
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001942
1943class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1944 """Introduced in 3.5, issue #18159."""
1945
1946 config_class = configparser.ConfigParser
1947
1948 def newconfig(self, defaults=None):
1949 instance = super().newconfig(defaults=defaults)
1950 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1951 if e.strip()]
1952 return instance
1953
1954 def test_converters(self):
1955 cfg = self.newconfig()
1956 self.assertIn('boolean', cfg.converters)
1957 self.assertIn('list', cfg.converters)
1958 self.assertIsNone(cfg.converters['int'])
1959 self.assertIsNone(cfg.converters['float'])
1960 self.assertIsNone(cfg.converters['boolean'])
1961 self.assertIsNotNone(cfg.converters['list'])
1962 self.assertEqual(len(cfg.converters), 4)
1963 with self.assertRaises(ValueError):
1964 cfg.converters[''] = lambda v: v
1965 with self.assertRaises(ValueError):
1966 cfg.converters[None] = lambda v: v
1967 cfg.read_string("""
1968 [s]
1969 str = string
1970 int = 1
1971 float = 0.5
1972 list = a b c d e f g
1973 bool = yes
1974 """)
1975 s = cfg['s']
1976 self.assertEqual(s['str'], 'string')
1977 self.assertEqual(s['int'], '1')
1978 self.assertEqual(s['float'], '0.5')
1979 self.assertEqual(s['list'], 'a b c d e f g')
1980 self.assertEqual(s['bool'], 'yes')
1981 self.assertEqual(cfg.get('s', 'str'), 'string')
1982 self.assertEqual(cfg.get('s', 'int'), '1')
1983 self.assertEqual(cfg.get('s', 'float'), '0.5')
1984 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1985 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1986 self.assertEqual(cfg.get('s', 'str'), 'string')
1987 self.assertEqual(cfg.getint('s', 'int'), 1)
1988 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1989 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1990 'e', 'f', 'g'])
1991 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1992 self.assertEqual(s.get('str'), 'string')
1993 self.assertEqual(s.getint('int'), 1)
1994 self.assertEqual(s.getfloat('float'), 0.5)
1995 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1996 'e', 'f', 'g'])
1997 self.assertEqual(s.getboolean('bool'), True)
1998 with self.assertRaises(AttributeError):
1999 cfg.getdecimal('s', 'float')
2000 with self.assertRaises(AttributeError):
2001 s.getdecimal('float')
2002 import decimal
2003 cfg.converters['decimal'] = decimal.Decimal
2004 self.assertIn('decimal', cfg.converters)
2005 self.assertIsNotNone(cfg.converters['decimal'])
2006 self.assertEqual(len(cfg.converters), 5)
2007 dec0_5 = decimal.Decimal('0.5')
2008 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
2009 self.assertEqual(s.getdecimal('float'), dec0_5)
2010 del cfg.converters['decimal']
2011 self.assertNotIn('decimal', cfg.converters)
2012 self.assertEqual(len(cfg.converters), 4)
2013 with self.assertRaises(AttributeError):
2014 cfg.getdecimal('s', 'float')
2015 with self.assertRaises(AttributeError):
2016 s.getdecimal('float')
2017 with self.assertRaises(KeyError):
2018 del cfg.converters['decimal']
2019 with self.assertRaises(KeyError):
2020 del cfg.converters['']
2021 with self.assertRaises(KeyError):
2022 del cfg.converters[None]
2023
2024
2025class BlatantOverrideConvertersTestCase(unittest.TestCase):
2026 """What if somebody overrode a getboolean()? We want to make sure that in
2027 this case the automatic converters do not kick in."""
2028
2029 config = """
2030 [one]
2031 one = false
2032 two = false
2033 three = long story short
2034
2035 [two]
2036 one = false
2037 two = false
2038 three = four
2039 """
2040
2041 def test_converters_at_init(self):
2042 cfg = configparser.ConfigParser(converters={'len': len})
2043 cfg.read_string(self.config)
2044 self._test_len(cfg)
2045 self.assertIsNotNone(cfg.converters['len'])
2046
2047 def test_inheritance(self):
2048 class StrangeConfigParser(configparser.ConfigParser):
2049 gettysburg = 'a historic borough in south central Pennsylvania'
2050
2051 def getboolean(self, section, option, *, raw=False, vars=None,
2052 fallback=configparser._UNSET):
2053 if section == option:
2054 return True
2055 return super().getboolean(section, option, raw=raw, vars=vars,
2056 fallback=fallback)
2057 def getlen(self, section, option, *, raw=False, vars=None,
2058 fallback=configparser._UNSET):
2059 return self._get_conv(section, option, len, raw=raw, vars=vars,
2060 fallback=fallback)
2061
2062 cfg = StrangeConfigParser()
2063 cfg.read_string(self.config)
2064 self._test_len(cfg)
2065 self.assertIsNone(cfg.converters['len'])
2066 self.assertTrue(cfg.getboolean('one', 'one'))
2067 self.assertTrue(cfg.getboolean('two', 'two'))
2068 self.assertFalse(cfg.getboolean('one', 'two'))
2069 self.assertFalse(cfg.getboolean('two', 'one'))
2070 cfg.converters['boolean'] = cfg._convert_to_boolean
2071 self.assertFalse(cfg.getboolean('one', 'one'))
2072 self.assertFalse(cfg.getboolean('two', 'two'))
2073 self.assertFalse(cfg.getboolean('one', 'two'))
2074 self.assertFalse(cfg.getboolean('two', 'one'))
2075
2076 def _test_len(self, cfg):
2077 self.assertEqual(len(cfg.converters), 4)
2078 self.assertIn('boolean', cfg.converters)
2079 self.assertIn('len', cfg.converters)
2080 self.assertNotIn('tysburg', cfg.converters)
2081 self.assertIsNone(cfg.converters['int'])
2082 self.assertIsNone(cfg.converters['float'])
2083 self.assertIsNone(cfg.converters['boolean'])
2084 self.assertEqual(cfg.getlen('one', 'one'), 5)
2085 self.assertEqual(cfg.getlen('one', 'two'), 5)
2086 self.assertEqual(cfg.getlen('one', 'three'), 16)
2087 self.assertEqual(cfg.getlen('two', 'one'), 5)
2088 self.assertEqual(cfg.getlen('two', 'two'), 5)
2089 self.assertEqual(cfg.getlen('two', 'three'), 4)
2090 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2091 with self.assertRaises(configparser.NoOptionError):
2092 cfg.getlen('two', 'four')
2093 self.assertEqual(cfg['one'].getlen('one'), 5)
2094 self.assertEqual(cfg['one'].getlen('two'), 5)
2095 self.assertEqual(cfg['one'].getlen('three'), 16)
2096 self.assertEqual(cfg['two'].getlen('one'), 5)
2097 self.assertEqual(cfg['two'].getlen('two'), 5)
2098 self.assertEqual(cfg['two'].getlen('three'), 4)
2099 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2100 self.assertEqual(cfg['two'].getlen('four'), None)
2101
2102 def test_instance_assignment(self):
2103 cfg = configparser.ConfigParser()
2104 cfg.getboolean = lambda section, option: True
2105 cfg.getlen = lambda section, option: len(cfg[section][option])
2106 cfg.read_string(self.config)
2107 self.assertEqual(len(cfg.converters), 3)
2108 self.assertIn('boolean', cfg.converters)
2109 self.assertNotIn('len', cfg.converters)
2110 self.assertIsNone(cfg.converters['int'])
2111 self.assertIsNone(cfg.converters['float'])
2112 self.assertIsNone(cfg.converters['boolean'])
2113 self.assertTrue(cfg.getboolean('one', 'one'))
2114 self.assertTrue(cfg.getboolean('two', 'two'))
2115 self.assertTrue(cfg.getboolean('one', 'two'))
2116 self.assertTrue(cfg.getboolean('two', 'one'))
2117 cfg.converters['boolean'] = cfg._convert_to_boolean
2118 self.assertFalse(cfg.getboolean('one', 'one'))
2119 self.assertFalse(cfg.getboolean('two', 'two'))
2120 self.assertFalse(cfg.getboolean('one', 'two'))
2121 self.assertFalse(cfg.getboolean('two', 'one'))
2122 self.assertEqual(cfg.getlen('one', 'one'), 5)
2123 self.assertEqual(cfg.getlen('one', 'two'), 5)
2124 self.assertEqual(cfg.getlen('one', 'three'), 16)
2125 self.assertEqual(cfg.getlen('two', 'one'), 5)
2126 self.assertEqual(cfg.getlen('two', 'two'), 5)
2127 self.assertEqual(cfg.getlen('two', 'three'), 4)
2128 # If a getter impl is assigned straight to the instance, it won't
2129 # be available on the section proxies.
2130 with self.assertRaises(AttributeError):
2131 self.assertEqual(cfg['one'].getlen('one'), 5)
2132 with self.assertRaises(AttributeError):
2133 self.assertEqual(cfg['two'].getlen('one'), 5)
2134
2135
Martin Panter2b9b70b2016-09-09 06:46:48 +00002136class MiscTestCase(unittest.TestCase):
2137 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02002138 support.check__all__(self, configparser, not_exported={"Error"})
Martin Panter2b9b70b2016-09-09 06:46:48 +00002139
2140
Ezio Melottidc1fa802013-01-11 06:30:57 +02002141if __name__ == '__main__':
2142 unittest.main()