blob: 72c3f19fb41f569364d021e6af6cfec2210a46cf [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
Berker Peksag21ce65a2017-03-03 20:48:37 +03005import 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")
Berker Peksag21ce65a2017-03-03 20:48:37 +0300724 # 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
Fred Drakec6f28912002-10-25 19:40:49 +0000743 # shared by subclasses
744 def get_interpolation_config(self):
745 return self.fromstring(
746 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000747 "bar{equals}something %(with1)s interpolation (1 step)\n"
748 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
749 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
750 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
751 "with11{equals}%(with10)s\n"
752 "with10{equals}%(with9)s\n"
753 "with9{equals}%(with8)s\n"
754 "with8{equals}%(With7)s\n"
755 "with7{equals}%(WITH6)s\n"
756 "with6{equals}%(with5)s\n"
757 "With5{equals}%(with4)s\n"
758 "WITH4{equals}%(with3)s\n"
759 "with3{equals}%(with2)s\n"
760 "with2{equals}%(with1)s\n"
761 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000762 "\n"
763 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000764 "foo{equals}%(bar)s\n"
765 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000766 "\n"
767 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000768 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000769 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000770
Fred Drake98e3b292002-10-25 20:42:44 +0000771 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000772 cf = self.fromstring("""
773 [section]
774 name {0[0]} %(value)s
775 key{0[1]} |%(name)s|
776 getdefault{0[1]} |%(default)s|
777 """.format(self.delimiters), defaults={"default": "<default>"})
778 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000779 L.sort()
780 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000781 with self.assertRaises(configparser.NoSectionError):
782 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000783
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100784 def test_popitem(self):
785 cf = self.fromstring("""
786 [section1]
787 name1 {0[0]} value1
788 [section2]
789 name2 {0[0]} value2
790 [section3]
791 name3 {0[0]} value3
792 """.format(self.delimiters), defaults={"default": "<default>"})
793 self.assertEqual(cf.popitem()[0], 'section1')
794 self.assertEqual(cf.popitem()[0], 'section2')
795 self.assertEqual(cf.popitem()[0], 'section3')
796 with self.assertRaises(KeyError):
797 cf.popitem()
798
799 def test_clear(self):
800 cf = self.newconfig({"foo": "Bar"})
801 self.assertEqual(
802 cf.get(self.default_section, "Foo"), "Bar",
803 "could not locate option, expecting case-insensitive option names")
804 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
805 self.assertEqual(cf.sections(), ['zing'])
806 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
807 cf.clear()
808 self.assertEqual(set(cf.sections()), set())
809 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
810
Łukasz Langa02101942012-12-31 13:55:11 +0100811 def test_setitem(self):
812 cf = self.fromstring("""
813 [section1]
814 name1 {0[0]} value1
815 [section2]
816 name2 {0[0]} value2
817 [section3]
818 name3 {0[0]} value3
819 """.format(self.delimiters), defaults={"nameD": "valueD"})
820 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
821 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
822 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
823 self.assertEqual(cf['section1']['name1'], 'value1')
824 self.assertEqual(cf['section2']['name2'], 'value2')
825 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100826 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100827 cf['section2'] = {'name22': 'value22'}
828 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
829 self.assertEqual(cf['section2']['name22'], 'value22')
830 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100831 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100832 cf['section3'] = {}
833 self.assertEqual(set(cf['section3'].keys()), {'named'})
834 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100835 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100836 cf[self.default_section] = {}
837 self.assertEqual(set(cf[self.default_section].keys()), set())
838 self.assertEqual(set(cf['section1'].keys()), {'name1'})
839 self.assertEqual(set(cf['section2'].keys()), {'name22'})
840 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100841 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100842
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800843 def test_invalid_multiline_value(self):
844 if self.allow_no_value:
845 self.skipTest('if no_value is allowed, ParsingError is not raised')
846
847 invalid = textwrap.dedent("""\
848 [DEFAULT]
849 test {0} test
850 invalid""".format(self.delimiters[0])
851 )
852 cf = self.newconfig()
853 with self.assertRaises(configparser.ParsingError):
854 cf.read_string(invalid)
855 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
856 self.assertEqual(cf['DEFAULT']['test'], 'test')
857
Fred Drake8ef67672000-09-27 22:45:25 +0000858
Ezio Melottidc1fa802013-01-11 06:30:57 +0200859class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000860 config_class = configparser.RawConfigParser
861 strict = True
862
863
Ezio Melottidc1fa802013-01-11 06:30:57 +0200864class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000865 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000866
867 def test_interpolation(self):
868 cf = self.get_interpolation_config()
869 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000870 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
871 eq(cf.get("Foo", "bar9"),
872 "something with lots of interpolation (9 steps)")
873 eq(cf.get("Foo", "bar10"),
874 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000875 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000876 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200877 self.assertEqual(e.args, ("bar11", "Foo",
878 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000879 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
880 self.assertEqual(e.args, ("bar11", "Foo",
881 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000882
Fred Drake54782192002-12-31 06:57:25 +0000883 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000884 cf = self.get_interpolation_config()
885 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000886 "Interpolation Error", "name")
887 self.assertEqual(e.reference, "reference")
888 self.assertEqual(e.section, "Interpolation Error")
889 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000890 if self.interpolation == configparser._UNSET:
891 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200892 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000893 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
894 self.assertEqual(e.args, ('name', 'Interpolation Error',
895 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000896
Fred Drake98e3b292002-10-25 20:42:44 +0000897 def test_items(self):
898 self.check_items_config([('default', '<default>'),
899 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000900 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000901 ('name', 'value'),
902 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000903
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000904 def test_safe_interpolation(self):
905 # See http://www.python.org/sf/511737
906 cf = self.fromstring("[section]\n"
907 "option1{eq}xxx\n"
908 "option2{eq}%(option1)s/xxx\n"
909 "ok{eq}%(option1)s/%%s\n"
910 "not_ok{eq}%(option2)s/%%s".format(
911 eq=self.delimiters[0]))
912 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
913 if self.interpolation == configparser._UNSET:
914 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
915 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
916 with self.assertRaises(TypeError):
917 cf.get("section", "not_ok")
918
919 def test_set_malformatted_interpolation(self):
920 cf = self.fromstring("[sect]\n"
921 "option1{eq}foo\n".format(eq=self.delimiters[0]))
922
923 self.assertEqual(cf.get('sect', "option1"), "foo")
924
925 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
926 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
927 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
928
929 self.assertEqual(cf.get('sect', "option1"), "foo")
930
931 # bug #5741: double percents are *not* malformed
932 cf.set("sect", "option2", "foo%%bar")
933 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
934
David Goodger1cbf2062004-10-03 15:55:09 +0000935 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000936 cf = self.fromstring("[sect]\n"
937 "option1{eq}foo\n".format(eq=self.delimiters[0]))
938 # Check that we get a TypeError when setting non-string values
939 # in an existing section:
940 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
941 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
942 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
943 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
944 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
945 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
946 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
947 self.assertRaises(TypeError, cf.add_section, 123)
948
949 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000950 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000951 self.assertRaises(ValueError, cf.add_section, self.default_section)
952
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200953
Ezio Melottidc1fa802013-01-11 06:30:57 +0200954class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200955 config_class = configparser.ConfigParser
956 interpolation = None
957 ini = textwrap.dedent("""
958 [numbers]
959 one = 1
960 two = %(one)s * 2
961 three = ${common:one} * 3
962
963 [hexen]
964 sixteen = ${numbers:two} * 8
965 """).strip()
966
967 def assertMatchesIni(self, cf):
968 self.assertEqual(cf['numbers']['one'], '1')
969 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
970 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
971 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
972
973 def test_no_interpolation(self):
974 cf = self.fromstring(self.ini)
975 self.assertMatchesIni(cf)
976
977 def test_empty_case(self):
978 cf = self.newconfig()
979 self.assertIsNone(cf.read_string(""))
980
981 def test_none_as_default_interpolation(self):
982 class CustomConfigParser(configparser.ConfigParser):
983 _DEFAULT_INTERPOLATION = None
984
985 cf = CustomConfigParser()
986 cf.read_string(self.ini)
987 self.assertMatchesIni(cf)
988
989
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000990class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
991 config_class = configparser.ConfigParser
992 interpolation = configparser.LegacyInterpolation()
993
994 def test_set_malformatted_interpolation(self):
995 cf = self.fromstring("[sect]\n"
996 "option1{eq}foo\n".format(eq=self.delimiters[0]))
997
998 self.assertEqual(cf.get('sect', "option1"), "foo")
999
1000 cf.set("sect", "option1", "%foo")
1001 self.assertEqual(cf.get('sect', "option1"), "%foo")
1002 cf.set("sect", "option1", "foo%")
1003 self.assertEqual(cf.get('sect', "option1"), "foo%")
1004 cf.set("sect", "option1", "f%oo")
1005 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1006
1007 # bug #5741: double percents are *not* malformed
1008 cf.set("sect", "option2", "foo%%bar")
1009 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001010
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001011
Georg Brandl96a60ae2010-07-28 13:13:46 +00001012class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1013 delimiters = (':=', '$')
1014 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001015 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001016
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001017
Łukasz Langac264c092010-11-20 16:15:37 +00001018class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1019 default_section = 'general'
1020
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001021
Ezio Melottidc1fa802013-01-11 06:30:57 +02001022class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001023 config_class = configparser.ConfigParser
1024 wonderful_spam = ("I'm having spam spam spam spam "
1025 "spam spam spam beaked beans spam "
1026 "spam spam and spam!").replace(' ', '\t\n')
1027
1028 def setUp(self):
1029 cf = self.newconfig()
1030 for i in range(100):
1031 s = 'section{}'.format(i)
1032 cf.add_section(s)
1033 for j in range(10):
1034 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1035 with open(support.TESTFN, 'w') as f:
1036 cf.write(f)
1037
1038 def tearDown(self):
1039 os.unlink(support.TESTFN)
1040
1041 def test_dominating_multiline_values(self):
1042 # We're reading from file because this is where the code changed
1043 # during performance updates in Python 3.2
1044 cf_from_file = self.newconfig()
1045 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001046 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001047 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1048 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001049
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001050
Ezio Melottidc1fa802013-01-11 06:30:57 +02001051class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001052 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001053
1054 def test_interpolation(self):
1055 cf = self.get_interpolation_config()
1056 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001057 eq(cf.get("Foo", "bar"),
1058 "something %(with1)s interpolation (1 step)")
1059 eq(cf.get("Foo", "bar9"),
1060 "something %(with9)s lots of interpolation (9 steps)")
1061 eq(cf.get("Foo", "bar10"),
1062 "something %(with10)s lots of interpolation (10 steps)")
1063 eq(cf.get("Foo", "bar11"),
1064 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001065
Fred Drake98e3b292002-10-25 20:42:44 +00001066 def test_items(self):
1067 self.check_items_config([('default', '<default>'),
1068 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001069 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001070 ('name', '%(value)s'),
1071 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001072
David Goodger1cbf2062004-10-03 15:55:09 +00001073 def test_set_nonstring_types(self):
1074 cf = self.newconfig()
1075 cf.add_section('non-string')
1076 cf.set('non-string', 'int', 1)
1077 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1078 cf.set('non-string', 'dict', {'pi': 3.14159})
1079 self.assertEqual(cf.get('non-string', 'int'), 1)
1080 self.assertEqual(cf.get('non-string', 'list'),
1081 [0, 1, 1, 2, 3, 5, 8, 13])
1082 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001083 cf.add_section(123)
1084 cf.set(123, 'this is sick', True)
1085 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001086 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001087 # would not work for SortedDict; only checking for the most common
1088 # default dictionary (OrderedDict)
1089 cf.optionxform = lambda x: x
1090 cf.set('non-string', 1, 1)
1091 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001092
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001093
Georg Brandl96a60ae2010-07-28 13:13:46 +00001094class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1095 delimiters = (':=', '$')
1096 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001097 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001098
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001099
Ezio Melottidc1fa802013-01-11 06:30:57 +02001100class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001101 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001102 comment_prefixes = ('#', ';', '----')
1103 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001104 empty_lines_in_values = False
1105
1106 def test_reading(self):
1107 smbconf = support.findfile("cfgparser.2")
1108 # check when we pass a mix of readable and non-readable files:
1109 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001110 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001111 self.assertEqual(parsed_files, [smbconf])
1112 sections = ['global', 'homes', 'printers',
1113 'print$', 'pdf-generator', 'tmp', 'Agustin']
1114 self.assertEqual(cf.sections(), sections)
1115 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1116 self.assertEqual(cf.getint("global", "max log size"), 50)
1117 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1118 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001119
Ezio Melottidc1fa802013-01-11 06:30:57 +02001120class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001121 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001122 interpolation = configparser.ExtendedInterpolation()
1123 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001124 strict = True
1125
1126 def fromstring(self, string, defaults=None, optionxform=None):
1127 cf = self.newconfig(defaults)
1128 if optionxform:
1129 cf.optionxform = optionxform
1130 cf.read_string(string)
1131 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001132
1133 def test_extended_interpolation(self):
1134 cf = self.fromstring(textwrap.dedent("""
1135 [common]
1136 favourite Beatle = Paul
1137 favourite color = green
1138
1139 [tom]
1140 favourite band = ${favourite color} day
1141 favourite pope = John ${favourite Beatle} II
1142 sequel = ${favourite pope}I
1143
1144 [ambv]
1145 favourite Beatle = George
1146 son of Edward VII = ${favourite Beatle} V
1147 son of George V = ${son of Edward VII}I
1148
1149 [stanley]
1150 favourite Beatle = ${ambv:favourite Beatle}
1151 favourite pope = ${tom:favourite pope}
1152 favourite color = black
1153 favourite state of mind = paranoid
1154 favourite movie = soylent ${common:favourite color}
1155 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1156 """).strip())
1157
1158 eq = self.assertEqual
1159 eq(cf['common']['favourite Beatle'], 'Paul')
1160 eq(cf['common']['favourite color'], 'green')
1161 eq(cf['tom']['favourite Beatle'], 'Paul')
1162 eq(cf['tom']['favourite color'], 'green')
1163 eq(cf['tom']['favourite band'], 'green day')
1164 eq(cf['tom']['favourite pope'], 'John Paul II')
1165 eq(cf['tom']['sequel'], 'John Paul III')
1166 eq(cf['ambv']['favourite Beatle'], 'George')
1167 eq(cf['ambv']['favourite color'], 'green')
1168 eq(cf['ambv']['son of Edward VII'], 'George V')
1169 eq(cf['ambv']['son of George V'], 'George VI')
1170 eq(cf['stanley']['favourite Beatle'], 'George')
1171 eq(cf['stanley']['favourite color'], 'black')
1172 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1173 eq(cf['stanley']['favourite movie'], 'soylent green')
1174 eq(cf['stanley']['favourite pope'], 'John Paul II')
1175 eq(cf['stanley']['favourite song'],
1176 'black sabbath - paranoid')
1177
1178 def test_endless_loop(self):
1179 cf = self.fromstring(textwrap.dedent("""
1180 [one for you]
1181 ping = ${one for me:pong}
1182
1183 [one for me]
1184 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001185
1186 [selfish]
1187 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001188 """).strip())
1189
1190 with self.assertRaises(configparser.InterpolationDepthError):
1191 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001192 with self.assertRaises(configparser.InterpolationDepthError):
1193 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001194
Łukasz Langa71b37a52010-12-17 21:56:32 +00001195 def test_strange_options(self):
1196 cf = self.fromstring("""
1197 [dollars]
1198 $var = $$value
1199 $var2 = ${$var}
1200 ${sick} = cannot interpolate me
1201
1202 [interpolated]
1203 $other = ${dollars:$var}
1204 $trying = ${dollars:${sick}}
1205 """)
1206
1207 self.assertEqual(cf['dollars']['$var'], '$value')
1208 self.assertEqual(cf['interpolated']['$other'], '$value')
1209 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1210 exception_class = configparser.InterpolationMissingOptionError
1211 with self.assertRaises(exception_class) as cm:
1212 cf['interpolated']['$trying']
1213 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001214 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001215
Łukasz Langae698cd52011-04-28 10:58:57 +02001216 def test_case_sensitivity_basic(self):
1217 ini = textwrap.dedent("""
1218 [common]
1219 optionlower = value
1220 OptionUpper = Value
1221
1222 [Common]
1223 optionlower = a better ${common:optionlower}
1224 OptionUpper = A Better ${common:OptionUpper}
1225
1226 [random]
1227 foolower = ${common:optionlower} redefined
1228 FooUpper = ${Common:OptionUpper} Redefined
1229 """).strip()
1230
1231 cf = self.fromstring(ini)
1232 eq = self.assertEqual
1233 eq(cf['common']['optionlower'], 'value')
1234 eq(cf['common']['OptionUpper'], 'Value')
1235 eq(cf['Common']['optionlower'], 'a better value')
1236 eq(cf['Common']['OptionUpper'], 'A Better Value')
1237 eq(cf['random']['foolower'], 'value redefined')
1238 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1239
1240 def test_case_sensitivity_conflicts(self):
1241 ini = textwrap.dedent("""
1242 [common]
1243 option = value
1244 Option = Value
1245
1246 [Common]
1247 option = a better ${common:option}
1248 Option = A Better ${common:Option}
1249
1250 [random]
1251 foo = ${common:option} redefined
1252 Foo = ${Common:Option} Redefined
1253 """).strip()
1254 with self.assertRaises(configparser.DuplicateOptionError):
1255 cf = self.fromstring(ini)
1256
1257 # raw options
1258 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1259 eq = self.assertEqual
1260 eq(cf['common']['option'], 'value')
1261 eq(cf['common']['Option'], 'Value')
1262 eq(cf['Common']['option'], 'a better value')
1263 eq(cf['Common']['Option'], 'A Better Value')
1264 eq(cf['random']['foo'], 'value redefined')
1265 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001266
1267 def test_other_errors(self):
1268 cf = self.fromstring("""
1269 [interpolation fail]
1270 case1 = ${where's the brace
1271 case2 = ${does_not_exist}
1272 case3 = ${wrong_section:wrong_value}
1273 case4 = ${i:like:colon:characters}
1274 case5 = $100 for Fail No 5!
1275 """)
1276
1277 with self.assertRaises(configparser.InterpolationSyntaxError):
1278 cf['interpolation fail']['case1']
1279 with self.assertRaises(configparser.InterpolationMissingOptionError):
1280 cf['interpolation fail']['case2']
1281 with self.assertRaises(configparser.InterpolationMissingOptionError):
1282 cf['interpolation fail']['case3']
1283 with self.assertRaises(configparser.InterpolationSyntaxError):
1284 cf['interpolation fail']['case4']
1285 with self.assertRaises(configparser.InterpolationSyntaxError):
1286 cf['interpolation fail']['case5']
1287 with self.assertRaises(ValueError):
1288 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001289
1290
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001291class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001292 allow_no_value = True
1293
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001294
Ezio Melottidc1fa802013-01-11 06:30:57 +02001295class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001296 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001297 delimiters = {'='}
1298 comment_prefixes = {'#'}
1299 allow_no_value = True
1300
1301 def test_cfgparser_dot_3(self):
1302 tricky = support.findfile("cfgparser.3")
1303 cf = self.newconfig()
1304 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1305 self.assertEqual(cf.sections(), ['strange',
1306 'corruption',
1307 'yeah, sections can be '
1308 'indented as well',
1309 'another one!',
1310 'no values here',
1311 'tricky interpolation',
1312 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001313 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001314 vars={'interpolate': '-1'}), -1)
1315 with self.assertRaises(ValueError):
1316 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001317 cf.getint(self.default_section, 'go', raw=True,
1318 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001319 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1320 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1321 longname = 'yeah, sections can be indented as well'
1322 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001323 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001324 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1325 # `go` from DEFAULT
1326 with self.assertRaises(configparser.InterpolationMissingOptionError):
1327 cf.items('no values here')
1328 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1329 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1330 cf.get('tricky interpolation', 'go'))
1331 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1332
1333 def test_unicode_failure(self):
1334 tricky = support.findfile("cfgparser.3")
1335 cf = self.newconfig()
1336 with self.assertRaises(UnicodeDecodeError):
1337 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001338
Fred Drake88444412010-09-03 04:22:36 +00001339
1340class Issue7005TestCase(unittest.TestCase):
1341 """Test output when None is set() as a value and allow_no_value == False.
1342
1343 http://bugs.python.org/issue7005
1344
1345 """
1346
1347 expected_output = "[section]\noption = None\n\n"
1348
1349 def prepare(self, config_class):
1350 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001351 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001352 cp.add_section("section")
1353 cp.set("section", "option", None)
1354 sio = io.StringIO()
1355 cp.write(sio)
1356 return sio.getvalue()
1357
1358 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001359 cp = configparser.ConfigParser(allow_no_value=False)
1360 cp.add_section("section")
1361 with self.assertRaises(TypeError):
1362 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001363
1364 def test_none_as_value_stringified_raw(self):
1365 output = self.prepare(configparser.RawConfigParser)
1366 self.assertEqual(output, self.expected_output)
1367
1368
Thomas Wouters89f507f2006-12-13 04:49:30 +00001369class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001370 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001371
1372 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001373 cf = self.fromstring("[b]\n"
1374 "o4=1\n"
1375 "o3=2\n"
1376 "o2=3\n"
1377 "o1=4\n"
1378 "[a]\n"
1379 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001380 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001381 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001382 self.assertEqual(output.getvalue(),
1383 "[a]\n"
1384 "k = v\n\n"
1385 "[b]\n"
1386 "o1 = 4\n"
1387 "o2 = 3\n"
1388 "o3 = 2\n"
1389 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001390
Fred Drake03c44a32010-02-19 06:08:41 +00001391
Ezio Melottidc1fa802013-01-11 06:30:57 +02001392class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001393 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001394 comment_prefixes = '#;'
1395 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001396
1397 def test_comment_handling(self):
1398 config_string = textwrap.dedent("""\
1399 [Commented Bar]
1400 baz=qwe ; a comment
1401 foo: bar # not a comment!
1402 # but this is a comment
1403 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001404 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001405 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001406 """)
1407 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001408 self.assertEqual(cf.get('Commented Bar', 'foo'),
1409 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001410 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001411 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1412 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001413
Ezio Melottidc1fa802013-01-11 06:30:57 +02001414class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001415 config_class = configparser.ConfigParser
1416
1417 def fromstring(self, string, defaults=None):
1418 cf = self.newconfig(defaults)
1419 cf.read_string(string)
1420 cf_copy = self.newconfig()
1421 cf_copy.read_dict(cf)
1422 # we have to clean up option duplicates that appeared because of
1423 # the magic DEFAULTSECT behaviour.
1424 for section in cf_copy.values():
1425 if section.name == self.default_section:
1426 continue
1427 for default, value in cf[self.default_section].items():
1428 if section[default] == value:
1429 del section[default]
1430 return cf_copy
1431
Łukasz Langadaab1c82011-04-27 18:10:05 +02001432
1433class FakeFile:
1434 def __init__(self):
1435 file_path = support.findfile("cfgparser.1")
1436 with open(file_path) as f:
1437 self.lines = f.readlines()
1438 self.lines.reverse()
1439
1440 def readline(self):
1441 if len(self.lines):
1442 return self.lines.pop()
1443 return ''
1444
1445
1446def readline_generator(f):
1447 """As advised in Doc/library/configparser.rst."""
1448 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001449 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001450 yield line
1451 line = f.readline()
1452
1453
1454class ReadFileTestCase(unittest.TestCase):
1455 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001456 file_paths = [support.findfile("cfgparser.1")]
1457 try:
1458 file_paths.append(file_paths[0].encode('utf8'))
1459 except UnicodeEncodeError:
1460 pass # unfortunately we can't test bytes on this path
1461 for file_path in file_paths:
1462 parser = configparser.ConfigParser()
1463 with open(file_path) as f:
1464 parser.read_file(f)
1465 self.assertIn("Foo Bar", parser)
1466 self.assertIn("foo", parser["Foo Bar"])
1467 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001468
1469 def test_iterable(self):
1470 lines = textwrap.dedent("""
1471 [Foo Bar]
1472 foo=newbar""").strip().split('\n')
1473 parser = configparser.ConfigParser()
1474 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001475 self.assertIn("Foo Bar", parser)
1476 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001477 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1478
1479 def test_readline_generator(self):
1480 """Issue #11670."""
1481 parser = configparser.ConfigParser()
1482 with self.assertRaises(TypeError):
1483 parser.read_file(FakeFile())
1484 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001485 self.assertIn("Foo Bar", parser)
1486 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001487 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1488
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001489 def test_source_as_bytes(self):
1490 """Issue #18260."""
1491 lines = textwrap.dedent("""
1492 [badbad]
1493 [badbad]""").strip().split('\n')
1494 parser = configparser.ConfigParser()
1495 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1496 parser.read_file(lines, source=b"badbad")
1497 self.assertEqual(
1498 str(dse.exception),
1499 "While reading from b'badbad' [line 2]: section 'badbad' "
1500 "already exists"
1501 )
1502 lines = textwrap.dedent("""
1503 [badbad]
1504 bad = bad
1505 bad = bad""").strip().split('\n')
1506 parser = configparser.ConfigParser()
1507 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1508 parser.read_file(lines, source=b"badbad")
1509 self.assertEqual(
1510 str(dse.exception),
1511 "While reading from b'badbad' [line 3]: option 'bad' in section "
1512 "'badbad' already exists"
1513 )
1514 lines = textwrap.dedent("""
1515 [badbad]
1516 = bad""").strip().split('\n')
1517 parser = configparser.ConfigParser()
1518 with self.assertRaises(configparser.ParsingError) as dse:
1519 parser.read_file(lines, source=b"badbad")
1520 self.assertEqual(
1521 str(dse.exception),
1522 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1523 )
1524 lines = textwrap.dedent("""
1525 [badbad
1526 bad = bad""").strip().split('\n')
1527 parser = configparser.ConfigParser()
1528 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1529 parser.read_file(lines, source=b"badbad")
1530 self.assertEqual(
1531 str(dse.exception),
1532 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1533 "'[badbad'"
1534 )
1535
Łukasz Langadaab1c82011-04-27 18:10:05 +02001536
Łukasz Langa71b37a52010-12-17 21:56:32 +00001537class CoverageOneHundredTestCase(unittest.TestCase):
1538 """Covers edge cases in the codebase."""
1539
1540 def test_duplicate_option_error(self):
1541 error = configparser.DuplicateOptionError('section', 'option')
1542 self.assertEqual(error.section, 'section')
1543 self.assertEqual(error.option, 'option')
1544 self.assertEqual(error.source, None)
1545 self.assertEqual(error.lineno, None)
1546 self.assertEqual(error.args, ('section', 'option', None, None))
1547 self.assertEqual(str(error), "Option 'option' in section 'section' "
1548 "already exists")
1549
1550 def test_interpolation_depth_error(self):
1551 error = configparser.InterpolationDepthError('option', 'section',
1552 'rawval')
1553 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1554 self.assertEqual(error.option, 'option')
1555 self.assertEqual(error.section, 'section')
1556
1557 def test_parsing_error(self):
1558 with self.assertRaises(ValueError) as cm:
1559 configparser.ParsingError()
1560 self.assertEqual(str(cm.exception), "Required argument `source' not "
1561 "given.")
1562 with self.assertRaises(ValueError) as cm:
1563 configparser.ParsingError(source='source', filename='filename')
1564 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1565 "and `source'. Use `source'.")
1566 error = configparser.ParsingError(filename='source')
1567 self.assertEqual(error.source, 'source')
1568 with warnings.catch_warnings(record=True) as w:
1569 warnings.simplefilter("always", DeprecationWarning)
1570 self.assertEqual(error.filename, 'source')
1571 error.filename = 'filename'
1572 self.assertEqual(error.source, 'filename')
1573 for warning in w:
1574 self.assertTrue(warning.category is DeprecationWarning)
1575
1576 def test_interpolation_validation(self):
1577 parser = configparser.ConfigParser()
1578 parser.read_string("""
1579 [section]
1580 invalid_percent = %
1581 invalid_reference = %(()
1582 invalid_variable = %(does_not_exist)s
1583 """)
1584 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1585 parser['section']['invalid_percent']
1586 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1587 "'(', found: '%'")
1588 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1589 parser['section']['invalid_reference']
1590 self.assertEqual(str(cm.exception), "bad interpolation variable "
1591 "reference '%(()'")
1592
1593 def test_readfp_deprecation(self):
1594 sio = io.StringIO("""
1595 [section]
1596 option = value
1597 """)
1598 parser = configparser.ConfigParser()
1599 with warnings.catch_warnings(record=True) as w:
1600 warnings.simplefilter("always", DeprecationWarning)
1601 parser.readfp(sio, filename='StringIO')
1602 for warning in w:
1603 self.assertTrue(warning.category is DeprecationWarning)
1604 self.assertEqual(len(parser), 2)
1605 self.assertEqual(parser['section']['option'], 'value')
1606
1607 def test_safeconfigparser_deprecation(self):
1608 with warnings.catch_warnings(record=True) as w:
1609 warnings.simplefilter("always", DeprecationWarning)
1610 parser = configparser.SafeConfigParser()
1611 for warning in w:
1612 self.assertTrue(warning.category is DeprecationWarning)
1613
1614 def test_sectionproxy_repr(self):
1615 parser = configparser.ConfigParser()
1616 parser.read_string("""
1617 [section]
1618 key = value
1619 """)
1620 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001621
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001622 def test_inconsistent_converters_state(self):
1623 parser = configparser.ConfigParser()
1624 import decimal
1625 parser.converters['decimal'] = decimal.Decimal
1626 parser.read_string("""
1627 [s1]
1628 one = 1
1629 [s2]
1630 two = 2
1631 """)
1632 self.assertIn('decimal', parser.converters)
1633 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1634 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1635 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1636 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1637 del parser.getdecimal
1638 with self.assertRaises(AttributeError):
1639 parser.getdecimal('s1', 'one')
1640 self.assertIn('decimal', parser.converters)
1641 del parser.converters['decimal']
1642 self.assertNotIn('decimal', parser.converters)
1643 with self.assertRaises(AttributeError):
1644 parser.getdecimal('s1', 'one')
1645 with self.assertRaises(AttributeError):
1646 parser['s1'].getdecimal('one')
1647 with self.assertRaises(AttributeError):
1648 parser['s2'].getdecimal('two')
1649
Łukasz Langae7851952012-01-20 14:57:55 +01001650
1651class ExceptionPicklingTestCase(unittest.TestCase):
1652 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1653
1654 def test_error(self):
1655 import pickle
1656 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001657 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1658 pickled = pickle.dumps(e1, proto)
1659 e2 = pickle.loads(pickled)
1660 self.assertEqual(e1.message, e2.message)
1661 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001662
1663 def test_nosectionerror(self):
1664 import pickle
1665 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001666 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1667 pickled = pickle.dumps(e1, proto)
1668 e2 = pickle.loads(pickled)
1669 self.assertEqual(e1.message, e2.message)
1670 self.assertEqual(e1.args, e2.args)
1671 self.assertEqual(e1.section, e2.section)
1672 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001673
1674 def test_nooptionerror(self):
1675 import pickle
1676 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001677 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1678 pickled = pickle.dumps(e1, proto)
1679 e2 = pickle.loads(pickled)
1680 self.assertEqual(e1.message, e2.message)
1681 self.assertEqual(e1.args, e2.args)
1682 self.assertEqual(e1.section, e2.section)
1683 self.assertEqual(e1.option, e2.option)
1684 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001685
1686 def test_duplicatesectionerror(self):
1687 import pickle
1688 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001689 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1690 pickled = pickle.dumps(e1, proto)
1691 e2 = pickle.loads(pickled)
1692 self.assertEqual(e1.message, e2.message)
1693 self.assertEqual(e1.args, e2.args)
1694 self.assertEqual(e1.section, e2.section)
1695 self.assertEqual(e1.source, e2.source)
1696 self.assertEqual(e1.lineno, e2.lineno)
1697 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001698
1699 def test_duplicateoptionerror(self):
1700 import pickle
1701 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1702 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001703 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1704 pickled = pickle.dumps(e1, proto)
1705 e2 = pickle.loads(pickled)
1706 self.assertEqual(e1.message, e2.message)
1707 self.assertEqual(e1.args, e2.args)
1708 self.assertEqual(e1.section, e2.section)
1709 self.assertEqual(e1.option, e2.option)
1710 self.assertEqual(e1.source, e2.source)
1711 self.assertEqual(e1.lineno, e2.lineno)
1712 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001713
1714 def test_interpolationerror(self):
1715 import pickle
1716 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001717 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1718 pickled = pickle.dumps(e1, proto)
1719 e2 = pickle.loads(pickled)
1720 self.assertEqual(e1.message, e2.message)
1721 self.assertEqual(e1.args, e2.args)
1722 self.assertEqual(e1.section, e2.section)
1723 self.assertEqual(e1.option, e2.option)
1724 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001725
1726 def test_interpolationmissingoptionerror(self):
1727 import pickle
1728 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1729 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001730 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1731 pickled = pickle.dumps(e1, proto)
1732 e2 = pickle.loads(pickled)
1733 self.assertEqual(e1.message, e2.message)
1734 self.assertEqual(e1.args, e2.args)
1735 self.assertEqual(e1.section, e2.section)
1736 self.assertEqual(e1.option, e2.option)
1737 self.assertEqual(e1.reference, e2.reference)
1738 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001739
1740 def test_interpolationsyntaxerror(self):
1741 import pickle
1742 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001743 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1744 pickled = pickle.dumps(e1, proto)
1745 e2 = pickle.loads(pickled)
1746 self.assertEqual(e1.message, e2.message)
1747 self.assertEqual(e1.args, e2.args)
1748 self.assertEqual(e1.section, e2.section)
1749 self.assertEqual(e1.option, e2.option)
1750 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001751
1752 def test_interpolationdeptherror(self):
1753 import pickle
1754 e1 = configparser.InterpolationDepthError('option', 'section',
1755 'rawval')
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_parsingerror(self):
1766 import pickle
1767 e1 = configparser.ParsingError('source')
1768 e1.append(1, 'line1')
1769 e1.append(2, 'line2')
1770 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001771 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1772 pickled = pickle.dumps(e1, proto)
1773 e2 = pickle.loads(pickled)
1774 self.assertEqual(e1.message, e2.message)
1775 self.assertEqual(e1.args, e2.args)
1776 self.assertEqual(e1.source, e2.source)
1777 self.assertEqual(e1.errors, e2.errors)
1778 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001779 e1 = configparser.ParsingError(filename='filename')
1780 e1.append(1, 'line1')
1781 e1.append(2, 'line2')
1782 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001783 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1784 pickled = pickle.dumps(e1, proto)
1785 e2 = pickle.loads(pickled)
1786 self.assertEqual(e1.message, e2.message)
1787 self.assertEqual(e1.args, e2.args)
1788 self.assertEqual(e1.source, e2.source)
1789 self.assertEqual(e1.errors, e2.errors)
1790 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001791
1792 def test_missingsectionheadererror(self):
1793 import pickle
1794 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
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.line, e2.line)
1801 self.assertEqual(e1.source, e2.source)
1802 self.assertEqual(e1.lineno, e2.lineno)
1803 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001804
1805
Łukasz Langacba24322012-07-07 18:54:08 +02001806class InlineCommentStrippingTestCase(unittest.TestCase):
1807 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1808 delimiter occurs earlier without preceding space.."""
1809
1810 def test_stripping(self):
1811 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1812 '//'))
1813 cfg.read_string("""
1814 [section]
1815 k1 = v1;still v1
1816 k2 = v2 ;a comment
1817 k3 = v3 ; also a comment
1818 k4 = v4;still v4 ;a comment
1819 k5 = v5;still v5 ; also a comment
1820 k6 = v6;still v6; and still v6 ;a comment
1821 k7 = v7;still v7; and still v7 ; also a comment
1822
1823 [multiprefix]
1824 k1 = v1;still v1 #a comment ; yeah, pretty much
1825 k2 = v2 // this already is a comment ; continued
1826 k3 = v3;#//still v3# and still v3 ; a comment
1827 """)
1828 s = cfg['section']
1829 self.assertEqual(s['k1'], 'v1;still v1')
1830 self.assertEqual(s['k2'], 'v2')
1831 self.assertEqual(s['k3'], 'v3')
1832 self.assertEqual(s['k4'], 'v4;still v4')
1833 self.assertEqual(s['k5'], 'v5;still v5')
1834 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1835 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1836 s = cfg['multiprefix']
1837 self.assertEqual(s['k1'], 'v1;still v1')
1838 self.assertEqual(s['k2'], 'v2')
1839 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1840
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001841
Łukasz Langa949053b2014-09-04 01:36:33 -07001842class ExceptionContextTestCase(unittest.TestCase):
1843 """ Test that implementation details doesn't leak
1844 through raising exceptions. """
1845
1846 def test_get_basic_interpolation(self):
1847 parser = configparser.ConfigParser()
1848 parser.read_string("""
1849 [Paths]
1850 home_dir: /Users
1851 my_dir: %(home_dir1)s/lumberjack
1852 my_pictures: %(my_dir)s/Pictures
1853 """)
1854 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1855 with cm:
1856 parser.get('Paths', 'my_dir')
1857 self.assertIs(cm.exception.__suppress_context__, True)
1858
1859 def test_get_extended_interpolation(self):
1860 parser = configparser.ConfigParser(
1861 interpolation=configparser.ExtendedInterpolation())
1862 parser.read_string("""
1863 [Paths]
1864 home_dir: /Users
1865 my_dir: ${home_dir1}/lumberjack
1866 my_pictures: ${my_dir}/Pictures
1867 """)
1868 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1869 with cm:
1870 parser.get('Paths', 'my_dir')
1871 self.assertIs(cm.exception.__suppress_context__, True)
1872
1873 def test_missing_options(self):
1874 parser = configparser.ConfigParser()
1875 parser.read_string("""
1876 [Paths]
1877 home_dir: /Users
1878 """)
1879 with self.assertRaises(configparser.NoSectionError) as cm:
1880 parser.options('test')
1881 self.assertIs(cm.exception.__suppress_context__, True)
1882
1883 def test_missing_section(self):
1884 config = configparser.ConfigParser()
1885 with self.assertRaises(configparser.NoSectionError) as cm:
1886 config.set('Section1', 'an_int', '15')
1887 self.assertIs(cm.exception.__suppress_context__, True)
1888
1889 def test_remove_option(self):
1890 config = configparser.ConfigParser()
1891 with self.assertRaises(configparser.NoSectionError) as cm:
1892 config.remove_option('Section1', 'an_int')
1893 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001894
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001895
1896class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1897 """Introduced in 3.5, issue #18159."""
1898
1899 config_class = configparser.ConfigParser
1900
1901 def newconfig(self, defaults=None):
1902 instance = super().newconfig(defaults=defaults)
1903 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1904 if e.strip()]
1905 return instance
1906
1907 def test_converters(self):
1908 cfg = self.newconfig()
1909 self.assertIn('boolean', cfg.converters)
1910 self.assertIn('list', cfg.converters)
1911 self.assertIsNone(cfg.converters['int'])
1912 self.assertIsNone(cfg.converters['float'])
1913 self.assertIsNone(cfg.converters['boolean'])
1914 self.assertIsNotNone(cfg.converters['list'])
1915 self.assertEqual(len(cfg.converters), 4)
1916 with self.assertRaises(ValueError):
1917 cfg.converters[''] = lambda v: v
1918 with self.assertRaises(ValueError):
1919 cfg.converters[None] = lambda v: v
1920 cfg.read_string("""
1921 [s]
1922 str = string
1923 int = 1
1924 float = 0.5
1925 list = a b c d e f g
1926 bool = yes
1927 """)
1928 s = cfg['s']
1929 self.assertEqual(s['str'], 'string')
1930 self.assertEqual(s['int'], '1')
1931 self.assertEqual(s['float'], '0.5')
1932 self.assertEqual(s['list'], 'a b c d e f g')
1933 self.assertEqual(s['bool'], 'yes')
1934 self.assertEqual(cfg.get('s', 'str'), 'string')
1935 self.assertEqual(cfg.get('s', 'int'), '1')
1936 self.assertEqual(cfg.get('s', 'float'), '0.5')
1937 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1938 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1939 self.assertEqual(cfg.get('s', 'str'), 'string')
1940 self.assertEqual(cfg.getint('s', 'int'), 1)
1941 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1942 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1943 'e', 'f', 'g'])
1944 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1945 self.assertEqual(s.get('str'), 'string')
1946 self.assertEqual(s.getint('int'), 1)
1947 self.assertEqual(s.getfloat('float'), 0.5)
1948 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1949 'e', 'f', 'g'])
1950 self.assertEqual(s.getboolean('bool'), True)
1951 with self.assertRaises(AttributeError):
1952 cfg.getdecimal('s', 'float')
1953 with self.assertRaises(AttributeError):
1954 s.getdecimal('float')
1955 import decimal
1956 cfg.converters['decimal'] = decimal.Decimal
1957 self.assertIn('decimal', cfg.converters)
1958 self.assertIsNotNone(cfg.converters['decimal'])
1959 self.assertEqual(len(cfg.converters), 5)
1960 dec0_5 = decimal.Decimal('0.5')
1961 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1962 self.assertEqual(s.getdecimal('float'), dec0_5)
1963 del cfg.converters['decimal']
1964 self.assertNotIn('decimal', cfg.converters)
1965 self.assertEqual(len(cfg.converters), 4)
1966 with self.assertRaises(AttributeError):
1967 cfg.getdecimal('s', 'float')
1968 with self.assertRaises(AttributeError):
1969 s.getdecimal('float')
1970 with self.assertRaises(KeyError):
1971 del cfg.converters['decimal']
1972 with self.assertRaises(KeyError):
1973 del cfg.converters['']
1974 with self.assertRaises(KeyError):
1975 del cfg.converters[None]
1976
1977
1978class BlatantOverrideConvertersTestCase(unittest.TestCase):
1979 """What if somebody overrode a getboolean()? We want to make sure that in
1980 this case the automatic converters do not kick in."""
1981
1982 config = """
1983 [one]
1984 one = false
1985 two = false
1986 three = long story short
1987
1988 [two]
1989 one = false
1990 two = false
1991 three = four
1992 """
1993
1994 def test_converters_at_init(self):
1995 cfg = configparser.ConfigParser(converters={'len': len})
1996 cfg.read_string(self.config)
1997 self._test_len(cfg)
1998 self.assertIsNotNone(cfg.converters['len'])
1999
2000 def test_inheritance(self):
2001 class StrangeConfigParser(configparser.ConfigParser):
2002 gettysburg = 'a historic borough in south central Pennsylvania'
2003
2004 def getboolean(self, section, option, *, raw=False, vars=None,
2005 fallback=configparser._UNSET):
2006 if section == option:
2007 return True
2008 return super().getboolean(section, option, raw=raw, vars=vars,
2009 fallback=fallback)
2010 def getlen(self, section, option, *, raw=False, vars=None,
2011 fallback=configparser._UNSET):
2012 return self._get_conv(section, option, len, raw=raw, vars=vars,
2013 fallback=fallback)
2014
2015 cfg = StrangeConfigParser()
2016 cfg.read_string(self.config)
2017 self._test_len(cfg)
2018 self.assertIsNone(cfg.converters['len'])
2019 self.assertTrue(cfg.getboolean('one', 'one'))
2020 self.assertTrue(cfg.getboolean('two', 'two'))
2021 self.assertFalse(cfg.getboolean('one', 'two'))
2022 self.assertFalse(cfg.getboolean('two', 'one'))
2023 cfg.converters['boolean'] = cfg._convert_to_boolean
2024 self.assertFalse(cfg.getboolean('one', 'one'))
2025 self.assertFalse(cfg.getboolean('two', 'two'))
2026 self.assertFalse(cfg.getboolean('one', 'two'))
2027 self.assertFalse(cfg.getboolean('two', 'one'))
2028
2029 def _test_len(self, cfg):
2030 self.assertEqual(len(cfg.converters), 4)
2031 self.assertIn('boolean', cfg.converters)
2032 self.assertIn('len', cfg.converters)
2033 self.assertNotIn('tysburg', cfg.converters)
2034 self.assertIsNone(cfg.converters['int'])
2035 self.assertIsNone(cfg.converters['float'])
2036 self.assertIsNone(cfg.converters['boolean'])
2037 self.assertEqual(cfg.getlen('one', 'one'), 5)
2038 self.assertEqual(cfg.getlen('one', 'two'), 5)
2039 self.assertEqual(cfg.getlen('one', 'three'), 16)
2040 self.assertEqual(cfg.getlen('two', 'one'), 5)
2041 self.assertEqual(cfg.getlen('two', 'two'), 5)
2042 self.assertEqual(cfg.getlen('two', 'three'), 4)
2043 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2044 with self.assertRaises(configparser.NoOptionError):
2045 cfg.getlen('two', 'four')
2046 self.assertEqual(cfg['one'].getlen('one'), 5)
2047 self.assertEqual(cfg['one'].getlen('two'), 5)
2048 self.assertEqual(cfg['one'].getlen('three'), 16)
2049 self.assertEqual(cfg['two'].getlen('one'), 5)
2050 self.assertEqual(cfg['two'].getlen('two'), 5)
2051 self.assertEqual(cfg['two'].getlen('three'), 4)
2052 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2053 self.assertEqual(cfg['two'].getlen('four'), None)
2054
2055 def test_instance_assignment(self):
2056 cfg = configparser.ConfigParser()
2057 cfg.getboolean = lambda section, option: True
2058 cfg.getlen = lambda section, option: len(cfg[section][option])
2059 cfg.read_string(self.config)
2060 self.assertEqual(len(cfg.converters), 3)
2061 self.assertIn('boolean', cfg.converters)
2062 self.assertNotIn('len', cfg.converters)
2063 self.assertIsNone(cfg.converters['int'])
2064 self.assertIsNone(cfg.converters['float'])
2065 self.assertIsNone(cfg.converters['boolean'])
2066 self.assertTrue(cfg.getboolean('one', 'one'))
2067 self.assertTrue(cfg.getboolean('two', 'two'))
2068 self.assertTrue(cfg.getboolean('one', 'two'))
2069 self.assertTrue(cfg.getboolean('two', 'one'))
2070 cfg.converters['boolean'] = cfg._convert_to_boolean
2071 self.assertFalse(cfg.getboolean('one', 'one'))
2072 self.assertFalse(cfg.getboolean('two', 'two'))
2073 self.assertFalse(cfg.getboolean('one', 'two'))
2074 self.assertFalse(cfg.getboolean('two', 'one'))
2075 self.assertEqual(cfg.getlen('one', 'one'), 5)
2076 self.assertEqual(cfg.getlen('one', 'two'), 5)
2077 self.assertEqual(cfg.getlen('one', 'three'), 16)
2078 self.assertEqual(cfg.getlen('two', 'one'), 5)
2079 self.assertEqual(cfg.getlen('two', 'two'), 5)
2080 self.assertEqual(cfg.getlen('two', 'three'), 4)
2081 # If a getter impl is assigned straight to the instance, it won't
2082 # be available on the section proxies.
2083 with self.assertRaises(AttributeError):
2084 self.assertEqual(cfg['one'].getlen('one'), 5)
2085 with self.assertRaises(AttributeError):
2086 self.assertEqual(cfg['two'].getlen('one'), 5)
2087
2088
Martin Panter2b9b70b2016-09-09 06:46:48 +00002089class MiscTestCase(unittest.TestCase):
2090 def test__all__(self):
2091 blacklist = {"Error"}
2092 support.check__all__(self, configparser, blacklist=blacklist)
2093
2094
Ezio Melottidc1fa802013-01-11 06:30:57 +02002095if __name__ == '__main__':
2096 unittest.main()