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