blob: 0d06080da33c523c9f11d88ad175249e8c221806 [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
Łukasz Langa535c0772010-12-04 13:48:13 +00005import sys
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")
724 # check when we pass only missing files:
725 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000726 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000727 self.assertEqual(parsed_files, [])
728 # check when we pass no files:
729 cf = self.newconfig()
730 parsed_files = cf.read([])
731 self.assertEqual(parsed_files, [])
732
Fred Drakec6f28912002-10-25 19:40:49 +0000733 # shared by subclasses
734 def get_interpolation_config(self):
735 return self.fromstring(
736 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000737 "bar{equals}something %(with1)s interpolation (1 step)\n"
738 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
739 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
740 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
741 "with11{equals}%(with10)s\n"
742 "with10{equals}%(with9)s\n"
743 "with9{equals}%(with8)s\n"
744 "with8{equals}%(With7)s\n"
745 "with7{equals}%(WITH6)s\n"
746 "with6{equals}%(with5)s\n"
747 "With5{equals}%(with4)s\n"
748 "WITH4{equals}%(with3)s\n"
749 "with3{equals}%(with2)s\n"
750 "with2{equals}%(with1)s\n"
751 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000752 "\n"
753 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000754 "foo{equals}%(bar)s\n"
755 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000756 "\n"
757 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000758 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000759 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000760
Fred Drake98e3b292002-10-25 20:42:44 +0000761 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000762 cf = self.fromstring("""
763 [section]
764 name {0[0]} %(value)s
765 key{0[1]} |%(name)s|
766 getdefault{0[1]} |%(default)s|
767 """.format(self.delimiters), defaults={"default": "<default>"})
768 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000769 L.sort()
770 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000771 with self.assertRaises(configparser.NoSectionError):
772 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000773
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100774 def test_popitem(self):
775 cf = self.fromstring("""
776 [section1]
777 name1 {0[0]} value1
778 [section2]
779 name2 {0[0]} value2
780 [section3]
781 name3 {0[0]} value3
782 """.format(self.delimiters), defaults={"default": "<default>"})
783 self.assertEqual(cf.popitem()[0], 'section1')
784 self.assertEqual(cf.popitem()[0], 'section2')
785 self.assertEqual(cf.popitem()[0], 'section3')
786 with self.assertRaises(KeyError):
787 cf.popitem()
788
789 def test_clear(self):
790 cf = self.newconfig({"foo": "Bar"})
791 self.assertEqual(
792 cf.get(self.default_section, "Foo"), "Bar",
793 "could not locate option, expecting case-insensitive option names")
794 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
795 self.assertEqual(cf.sections(), ['zing'])
796 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
797 cf.clear()
798 self.assertEqual(set(cf.sections()), set())
799 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
800
Łukasz Langa02101942012-12-31 13:55:11 +0100801 def test_setitem(self):
802 cf = self.fromstring("""
803 [section1]
804 name1 {0[0]} value1
805 [section2]
806 name2 {0[0]} value2
807 [section3]
808 name3 {0[0]} value3
809 """.format(self.delimiters), defaults={"nameD": "valueD"})
810 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
811 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
812 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
813 self.assertEqual(cf['section1']['name1'], 'value1')
814 self.assertEqual(cf['section2']['name2'], 'value2')
815 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100816 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100817 cf['section2'] = {'name22': 'value22'}
818 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
819 self.assertEqual(cf['section2']['name22'], 'value22')
820 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100821 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100822 cf['section3'] = {}
823 self.assertEqual(set(cf['section3'].keys()), {'named'})
824 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100825 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100826 cf[self.default_section] = {}
827 self.assertEqual(set(cf[self.default_section].keys()), set())
828 self.assertEqual(set(cf['section1'].keys()), {'name1'})
829 self.assertEqual(set(cf['section2'].keys()), {'name22'})
830 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100831 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100832
Łukasz Langa47a9a4b2016-11-26 14:00:39 -0800833 def test_invalid_multiline_value(self):
834 if self.allow_no_value:
835 self.skipTest('if no_value is allowed, ParsingError is not raised')
836
837 invalid = textwrap.dedent("""\
838 [DEFAULT]
839 test {0} test
840 invalid""".format(self.delimiters[0])
841 )
842 cf = self.newconfig()
843 with self.assertRaises(configparser.ParsingError):
844 cf.read_string(invalid)
845 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
846 self.assertEqual(cf['DEFAULT']['test'], 'test')
847
Fred Drake8ef67672000-09-27 22:45:25 +0000848
Ezio Melottidc1fa802013-01-11 06:30:57 +0200849class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000850 config_class = configparser.RawConfigParser
851 strict = True
852
853
Ezio Melottidc1fa802013-01-11 06:30:57 +0200854class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000855 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000856
857 def test_interpolation(self):
858 cf = self.get_interpolation_config()
859 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000860 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
861 eq(cf.get("Foo", "bar9"),
862 "something with lots of interpolation (9 steps)")
863 eq(cf.get("Foo", "bar10"),
864 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000865 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000866 if self.interpolation == configparser._UNSET:
Robert Collinsac37ba02015-08-14 11:11:35 +1200867 self.assertEqual(e.args, ("bar11", "Foo",
868 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000869 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
870 self.assertEqual(e.args, ("bar11", "Foo",
871 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000872
Fred Drake54782192002-12-31 06:57:25 +0000873 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000874 cf = self.get_interpolation_config()
875 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000876 "Interpolation Error", "name")
877 self.assertEqual(e.reference, "reference")
878 self.assertEqual(e.section, "Interpolation Error")
879 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000880 if self.interpolation == configparser._UNSET:
881 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200882 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000883 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
884 self.assertEqual(e.args, ('name', 'Interpolation Error',
885 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000886
Fred Drake98e3b292002-10-25 20:42:44 +0000887 def test_items(self):
888 self.check_items_config([('default', '<default>'),
889 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000890 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000891 ('name', 'value'),
892 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000893
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000894 def test_safe_interpolation(self):
895 # See http://www.python.org/sf/511737
896 cf = self.fromstring("[section]\n"
897 "option1{eq}xxx\n"
898 "option2{eq}%(option1)s/xxx\n"
899 "ok{eq}%(option1)s/%%s\n"
900 "not_ok{eq}%(option2)s/%%s".format(
901 eq=self.delimiters[0]))
902 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
903 if self.interpolation == configparser._UNSET:
904 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
905 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
906 with self.assertRaises(TypeError):
907 cf.get("section", "not_ok")
908
909 def test_set_malformatted_interpolation(self):
910 cf = self.fromstring("[sect]\n"
911 "option1{eq}foo\n".format(eq=self.delimiters[0]))
912
913 self.assertEqual(cf.get('sect', "option1"), "foo")
914
915 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
916 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
917 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
918
919 self.assertEqual(cf.get('sect', "option1"), "foo")
920
921 # bug #5741: double percents are *not* malformed
922 cf.set("sect", "option2", "foo%%bar")
923 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
924
David Goodger1cbf2062004-10-03 15:55:09 +0000925 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000926 cf = self.fromstring("[sect]\n"
927 "option1{eq}foo\n".format(eq=self.delimiters[0]))
928 # Check that we get a TypeError when setting non-string values
929 # in an existing section:
930 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
931 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
932 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
933 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
934 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
935 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
936 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
937 self.assertRaises(TypeError, cf.add_section, 123)
938
939 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000940 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000941 self.assertRaises(ValueError, cf.add_section, self.default_section)
942
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200943
Ezio Melottidc1fa802013-01-11 06:30:57 +0200944class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200945 config_class = configparser.ConfigParser
946 interpolation = None
947 ini = textwrap.dedent("""
948 [numbers]
949 one = 1
950 two = %(one)s * 2
951 three = ${common:one} * 3
952
953 [hexen]
954 sixteen = ${numbers:two} * 8
955 """).strip()
956
957 def assertMatchesIni(self, cf):
958 self.assertEqual(cf['numbers']['one'], '1')
959 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
960 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
961 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
962
963 def test_no_interpolation(self):
964 cf = self.fromstring(self.ini)
965 self.assertMatchesIni(cf)
966
967 def test_empty_case(self):
968 cf = self.newconfig()
969 self.assertIsNone(cf.read_string(""))
970
971 def test_none_as_default_interpolation(self):
972 class CustomConfigParser(configparser.ConfigParser):
973 _DEFAULT_INTERPOLATION = None
974
975 cf = CustomConfigParser()
976 cf.read_string(self.ini)
977 self.assertMatchesIni(cf)
978
979
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000980class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
981 config_class = configparser.ConfigParser
982 interpolation = configparser.LegacyInterpolation()
983
984 def test_set_malformatted_interpolation(self):
985 cf = self.fromstring("[sect]\n"
986 "option1{eq}foo\n".format(eq=self.delimiters[0]))
987
988 self.assertEqual(cf.get('sect', "option1"), "foo")
989
990 cf.set("sect", "option1", "%foo")
991 self.assertEqual(cf.get('sect', "option1"), "%foo")
992 cf.set("sect", "option1", "foo%")
993 self.assertEqual(cf.get('sect', "option1"), "foo%")
994 cf.set("sect", "option1", "f%oo")
995 self.assertEqual(cf.get('sect', "option1"), "f%oo")
996
997 # bug #5741: double percents are *not* malformed
998 cf.set("sect", "option2", "foo%%bar")
999 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +00001000
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001001
Georg Brandl96a60ae2010-07-28 13:13:46 +00001002class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
1003 delimiters = (':=', '$')
1004 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001005 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001006
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001007
Łukasz Langac264c092010-11-20 16:15:37 +00001008class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
1009 default_section = 'general'
1010
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001011
Ezio Melottidc1fa802013-01-11 06:30:57 +02001012class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001013 config_class = configparser.ConfigParser
1014 wonderful_spam = ("I'm having spam spam spam spam "
1015 "spam spam spam beaked beans spam "
1016 "spam spam and spam!").replace(' ', '\t\n')
1017
1018 def setUp(self):
1019 cf = self.newconfig()
1020 for i in range(100):
1021 s = 'section{}'.format(i)
1022 cf.add_section(s)
1023 for j in range(10):
1024 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1025 with open(support.TESTFN, 'w') as f:
1026 cf.write(f)
1027
1028 def tearDown(self):
1029 os.unlink(support.TESTFN)
1030
1031 def test_dominating_multiline_values(self):
1032 # We're reading from file because this is where the code changed
1033 # during performance updates in Python 3.2
1034 cf_from_file = self.newconfig()
1035 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001036 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001037 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1038 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001039
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001040
Ezio Melottidc1fa802013-01-11 06:30:57 +02001041class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001042 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001043
1044 def test_interpolation(self):
1045 cf = self.get_interpolation_config()
1046 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001047 eq(cf.get("Foo", "bar"),
1048 "something %(with1)s interpolation (1 step)")
1049 eq(cf.get("Foo", "bar9"),
1050 "something %(with9)s lots of interpolation (9 steps)")
1051 eq(cf.get("Foo", "bar10"),
1052 "something %(with10)s lots of interpolation (10 steps)")
1053 eq(cf.get("Foo", "bar11"),
1054 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001055
Fred Drake98e3b292002-10-25 20:42:44 +00001056 def test_items(self):
1057 self.check_items_config([('default', '<default>'),
1058 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001059 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001060 ('name', '%(value)s'),
1061 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001062
David Goodger1cbf2062004-10-03 15:55:09 +00001063 def test_set_nonstring_types(self):
1064 cf = self.newconfig()
1065 cf.add_section('non-string')
1066 cf.set('non-string', 'int', 1)
1067 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1068 cf.set('non-string', 'dict', {'pi': 3.14159})
1069 self.assertEqual(cf.get('non-string', 'int'), 1)
1070 self.assertEqual(cf.get('non-string', 'list'),
1071 [0, 1, 1, 2, 3, 5, 8, 13])
1072 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001073 cf.add_section(123)
1074 cf.set(123, 'this is sick', True)
1075 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001076 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001077 # would not work for SortedDict; only checking for the most common
1078 # default dictionary (OrderedDict)
1079 cf.optionxform = lambda x: x
1080 cf.set('non-string', 1, 1)
1081 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001082
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001083
Georg Brandl96a60ae2010-07-28 13:13:46 +00001084class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1085 delimiters = (':=', '$')
1086 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001087 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001088
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001089
Ezio Melottidc1fa802013-01-11 06:30:57 +02001090class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001091 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001092 comment_prefixes = ('#', ';', '----')
1093 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001094 empty_lines_in_values = False
1095
1096 def test_reading(self):
1097 smbconf = support.findfile("cfgparser.2")
1098 # check when we pass a mix of readable and non-readable files:
1099 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001100 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001101 self.assertEqual(parsed_files, [smbconf])
1102 sections = ['global', 'homes', 'printers',
1103 'print$', 'pdf-generator', 'tmp', 'Agustin']
1104 self.assertEqual(cf.sections(), sections)
1105 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1106 self.assertEqual(cf.getint("global", "max log size"), 50)
1107 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1108 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001109
Ezio Melottidc1fa802013-01-11 06:30:57 +02001110class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001111 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001112 interpolation = configparser.ExtendedInterpolation()
1113 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001114 strict = True
1115
1116 def fromstring(self, string, defaults=None, optionxform=None):
1117 cf = self.newconfig(defaults)
1118 if optionxform:
1119 cf.optionxform = optionxform
1120 cf.read_string(string)
1121 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001122
1123 def test_extended_interpolation(self):
1124 cf = self.fromstring(textwrap.dedent("""
1125 [common]
1126 favourite Beatle = Paul
1127 favourite color = green
1128
1129 [tom]
1130 favourite band = ${favourite color} day
1131 favourite pope = John ${favourite Beatle} II
1132 sequel = ${favourite pope}I
1133
1134 [ambv]
1135 favourite Beatle = George
1136 son of Edward VII = ${favourite Beatle} V
1137 son of George V = ${son of Edward VII}I
1138
1139 [stanley]
1140 favourite Beatle = ${ambv:favourite Beatle}
1141 favourite pope = ${tom:favourite pope}
1142 favourite color = black
1143 favourite state of mind = paranoid
1144 favourite movie = soylent ${common:favourite color}
1145 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1146 """).strip())
1147
1148 eq = self.assertEqual
1149 eq(cf['common']['favourite Beatle'], 'Paul')
1150 eq(cf['common']['favourite color'], 'green')
1151 eq(cf['tom']['favourite Beatle'], 'Paul')
1152 eq(cf['tom']['favourite color'], 'green')
1153 eq(cf['tom']['favourite band'], 'green day')
1154 eq(cf['tom']['favourite pope'], 'John Paul II')
1155 eq(cf['tom']['sequel'], 'John Paul III')
1156 eq(cf['ambv']['favourite Beatle'], 'George')
1157 eq(cf['ambv']['favourite color'], 'green')
1158 eq(cf['ambv']['son of Edward VII'], 'George V')
1159 eq(cf['ambv']['son of George V'], 'George VI')
1160 eq(cf['stanley']['favourite Beatle'], 'George')
1161 eq(cf['stanley']['favourite color'], 'black')
1162 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1163 eq(cf['stanley']['favourite movie'], 'soylent green')
1164 eq(cf['stanley']['favourite pope'], 'John Paul II')
1165 eq(cf['stanley']['favourite song'],
1166 'black sabbath - paranoid')
1167
1168 def test_endless_loop(self):
1169 cf = self.fromstring(textwrap.dedent("""
1170 [one for you]
1171 ping = ${one for me:pong}
1172
1173 [one for me]
1174 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001175
1176 [selfish]
1177 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001178 """).strip())
1179
1180 with self.assertRaises(configparser.InterpolationDepthError):
1181 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001182 with self.assertRaises(configparser.InterpolationDepthError):
1183 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001184
Łukasz Langa71b37a52010-12-17 21:56:32 +00001185 def test_strange_options(self):
1186 cf = self.fromstring("""
1187 [dollars]
1188 $var = $$value
1189 $var2 = ${$var}
1190 ${sick} = cannot interpolate me
1191
1192 [interpolated]
1193 $other = ${dollars:$var}
1194 $trying = ${dollars:${sick}}
1195 """)
1196
1197 self.assertEqual(cf['dollars']['$var'], '$value')
1198 self.assertEqual(cf['interpolated']['$other'], '$value')
1199 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1200 exception_class = configparser.InterpolationMissingOptionError
1201 with self.assertRaises(exception_class) as cm:
1202 cf['interpolated']['$trying']
1203 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001204 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001205
Łukasz Langae698cd52011-04-28 10:58:57 +02001206 def test_case_sensitivity_basic(self):
1207 ini = textwrap.dedent("""
1208 [common]
1209 optionlower = value
1210 OptionUpper = Value
1211
1212 [Common]
1213 optionlower = a better ${common:optionlower}
1214 OptionUpper = A Better ${common:OptionUpper}
1215
1216 [random]
1217 foolower = ${common:optionlower} redefined
1218 FooUpper = ${Common:OptionUpper} Redefined
1219 """).strip()
1220
1221 cf = self.fromstring(ini)
1222 eq = self.assertEqual
1223 eq(cf['common']['optionlower'], 'value')
1224 eq(cf['common']['OptionUpper'], 'Value')
1225 eq(cf['Common']['optionlower'], 'a better value')
1226 eq(cf['Common']['OptionUpper'], 'A Better Value')
1227 eq(cf['random']['foolower'], 'value redefined')
1228 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1229
1230 def test_case_sensitivity_conflicts(self):
1231 ini = textwrap.dedent("""
1232 [common]
1233 option = value
1234 Option = Value
1235
1236 [Common]
1237 option = a better ${common:option}
1238 Option = A Better ${common:Option}
1239
1240 [random]
1241 foo = ${common:option} redefined
1242 Foo = ${Common:Option} Redefined
1243 """).strip()
1244 with self.assertRaises(configparser.DuplicateOptionError):
1245 cf = self.fromstring(ini)
1246
1247 # raw options
1248 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1249 eq = self.assertEqual
1250 eq(cf['common']['option'], 'value')
1251 eq(cf['common']['Option'], 'Value')
1252 eq(cf['Common']['option'], 'a better value')
1253 eq(cf['Common']['Option'], 'A Better Value')
1254 eq(cf['random']['foo'], 'value redefined')
1255 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001256
1257 def test_other_errors(self):
1258 cf = self.fromstring("""
1259 [interpolation fail]
1260 case1 = ${where's the brace
1261 case2 = ${does_not_exist}
1262 case3 = ${wrong_section:wrong_value}
1263 case4 = ${i:like:colon:characters}
1264 case5 = $100 for Fail No 5!
1265 """)
1266
1267 with self.assertRaises(configparser.InterpolationSyntaxError):
1268 cf['interpolation fail']['case1']
1269 with self.assertRaises(configparser.InterpolationMissingOptionError):
1270 cf['interpolation fail']['case2']
1271 with self.assertRaises(configparser.InterpolationMissingOptionError):
1272 cf['interpolation fail']['case3']
1273 with self.assertRaises(configparser.InterpolationSyntaxError):
1274 cf['interpolation fail']['case4']
1275 with self.assertRaises(configparser.InterpolationSyntaxError):
1276 cf['interpolation fail']['case5']
1277 with self.assertRaises(ValueError):
1278 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001279
1280
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001281class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001282 allow_no_value = True
1283
Łukasz Langa47a9a4b2016-11-26 14:00:39 -08001284
Ezio Melottidc1fa802013-01-11 06:30:57 +02001285class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001286 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001287 delimiters = {'='}
1288 comment_prefixes = {'#'}
1289 allow_no_value = True
1290
1291 def test_cfgparser_dot_3(self):
1292 tricky = support.findfile("cfgparser.3")
1293 cf = self.newconfig()
1294 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1295 self.assertEqual(cf.sections(), ['strange',
1296 'corruption',
1297 'yeah, sections can be '
1298 'indented as well',
1299 'another one!',
1300 'no values here',
1301 'tricky interpolation',
1302 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001303 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001304 vars={'interpolate': '-1'}), -1)
1305 with self.assertRaises(ValueError):
1306 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001307 cf.getint(self.default_section, 'go', raw=True,
1308 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001309 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1310 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1311 longname = 'yeah, sections can be indented as well'
1312 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001313 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001314 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1315 # `go` from DEFAULT
1316 with self.assertRaises(configparser.InterpolationMissingOptionError):
1317 cf.items('no values here')
1318 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1319 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1320 cf.get('tricky interpolation', 'go'))
1321 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1322
1323 def test_unicode_failure(self):
1324 tricky = support.findfile("cfgparser.3")
1325 cf = self.newconfig()
1326 with self.assertRaises(UnicodeDecodeError):
1327 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001328
Fred Drake88444412010-09-03 04:22:36 +00001329
1330class Issue7005TestCase(unittest.TestCase):
1331 """Test output when None is set() as a value and allow_no_value == False.
1332
1333 http://bugs.python.org/issue7005
1334
1335 """
1336
1337 expected_output = "[section]\noption = None\n\n"
1338
1339 def prepare(self, config_class):
1340 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001341 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001342 cp.add_section("section")
1343 cp.set("section", "option", None)
1344 sio = io.StringIO()
1345 cp.write(sio)
1346 return sio.getvalue()
1347
1348 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001349 cp = configparser.ConfigParser(allow_no_value=False)
1350 cp.add_section("section")
1351 with self.assertRaises(TypeError):
1352 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001353
1354 def test_none_as_value_stringified_raw(self):
1355 output = self.prepare(configparser.RawConfigParser)
1356 self.assertEqual(output, self.expected_output)
1357
1358
Thomas Wouters89f507f2006-12-13 04:49:30 +00001359class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001360 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001361
1362 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001363 cf = self.fromstring("[b]\n"
1364 "o4=1\n"
1365 "o3=2\n"
1366 "o2=3\n"
1367 "o1=4\n"
1368 "[a]\n"
1369 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001370 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001371 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001372 self.assertEqual(output.getvalue(),
1373 "[a]\n"
1374 "k = v\n\n"
1375 "[b]\n"
1376 "o1 = 4\n"
1377 "o2 = 3\n"
1378 "o3 = 2\n"
1379 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001380
Fred Drake03c44a32010-02-19 06:08:41 +00001381
Ezio Melottidc1fa802013-01-11 06:30:57 +02001382class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001383 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001384 comment_prefixes = '#;'
1385 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001386
1387 def test_comment_handling(self):
1388 config_string = textwrap.dedent("""\
1389 [Commented Bar]
1390 baz=qwe ; a comment
1391 foo: bar # not a comment!
1392 # but this is a comment
1393 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001394 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001395 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001396 """)
1397 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001398 self.assertEqual(cf.get('Commented Bar', 'foo'),
1399 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001400 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001401 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1402 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001403
Ezio Melottidc1fa802013-01-11 06:30:57 +02001404class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001405 config_class = configparser.ConfigParser
1406
1407 def fromstring(self, string, defaults=None):
1408 cf = self.newconfig(defaults)
1409 cf.read_string(string)
1410 cf_copy = self.newconfig()
1411 cf_copy.read_dict(cf)
1412 # we have to clean up option duplicates that appeared because of
1413 # the magic DEFAULTSECT behaviour.
1414 for section in cf_copy.values():
1415 if section.name == self.default_section:
1416 continue
1417 for default, value in cf[self.default_section].items():
1418 if section[default] == value:
1419 del section[default]
1420 return cf_copy
1421
Łukasz Langadaab1c82011-04-27 18:10:05 +02001422
1423class FakeFile:
1424 def __init__(self):
1425 file_path = support.findfile("cfgparser.1")
1426 with open(file_path) as f:
1427 self.lines = f.readlines()
1428 self.lines.reverse()
1429
1430 def readline(self):
1431 if len(self.lines):
1432 return self.lines.pop()
1433 return ''
1434
1435
1436def readline_generator(f):
1437 """As advised in Doc/library/configparser.rst."""
1438 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001439 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001440 yield line
1441 line = f.readline()
1442
1443
1444class ReadFileTestCase(unittest.TestCase):
1445 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001446 file_paths = [support.findfile("cfgparser.1")]
1447 try:
1448 file_paths.append(file_paths[0].encode('utf8'))
1449 except UnicodeEncodeError:
1450 pass # unfortunately we can't test bytes on this path
1451 for file_path in file_paths:
1452 parser = configparser.ConfigParser()
1453 with open(file_path) as f:
1454 parser.read_file(f)
1455 self.assertIn("Foo Bar", parser)
1456 self.assertIn("foo", parser["Foo Bar"])
1457 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001458
1459 def test_iterable(self):
1460 lines = textwrap.dedent("""
1461 [Foo Bar]
1462 foo=newbar""").strip().split('\n')
1463 parser = configparser.ConfigParser()
1464 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001465 self.assertIn("Foo Bar", parser)
1466 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001467 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1468
1469 def test_readline_generator(self):
1470 """Issue #11670."""
1471 parser = configparser.ConfigParser()
1472 with self.assertRaises(TypeError):
1473 parser.read_file(FakeFile())
1474 parser.read_file(readline_generator(FakeFile()))
Ł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
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001479 def test_source_as_bytes(self):
1480 """Issue #18260."""
1481 lines = textwrap.dedent("""
1482 [badbad]
1483 [badbad]""").strip().split('\n')
1484 parser = configparser.ConfigParser()
1485 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1486 parser.read_file(lines, source=b"badbad")
1487 self.assertEqual(
1488 str(dse.exception),
1489 "While reading from b'badbad' [line 2]: section 'badbad' "
1490 "already exists"
1491 )
1492 lines = textwrap.dedent("""
1493 [badbad]
1494 bad = bad
1495 bad = bad""").strip().split('\n')
1496 parser = configparser.ConfigParser()
1497 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1498 parser.read_file(lines, source=b"badbad")
1499 self.assertEqual(
1500 str(dse.exception),
1501 "While reading from b'badbad' [line 3]: option 'bad' in section "
1502 "'badbad' already exists"
1503 )
1504 lines = textwrap.dedent("""
1505 [badbad]
1506 = bad""").strip().split('\n')
1507 parser = configparser.ConfigParser()
1508 with self.assertRaises(configparser.ParsingError) as dse:
1509 parser.read_file(lines, source=b"badbad")
1510 self.assertEqual(
1511 str(dse.exception),
1512 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1513 )
1514 lines = textwrap.dedent("""
1515 [badbad
1516 bad = bad""").strip().split('\n')
1517 parser = configparser.ConfigParser()
1518 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1519 parser.read_file(lines, source=b"badbad")
1520 self.assertEqual(
1521 str(dse.exception),
1522 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1523 "'[badbad'"
1524 )
1525
Łukasz Langadaab1c82011-04-27 18:10:05 +02001526
Łukasz Langa71b37a52010-12-17 21:56:32 +00001527class CoverageOneHundredTestCase(unittest.TestCase):
1528 """Covers edge cases in the codebase."""
1529
1530 def test_duplicate_option_error(self):
1531 error = configparser.DuplicateOptionError('section', 'option')
1532 self.assertEqual(error.section, 'section')
1533 self.assertEqual(error.option, 'option')
1534 self.assertEqual(error.source, None)
1535 self.assertEqual(error.lineno, None)
1536 self.assertEqual(error.args, ('section', 'option', None, None))
1537 self.assertEqual(str(error), "Option 'option' in section 'section' "
1538 "already exists")
1539
1540 def test_interpolation_depth_error(self):
1541 error = configparser.InterpolationDepthError('option', 'section',
1542 'rawval')
1543 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1544 self.assertEqual(error.option, 'option')
1545 self.assertEqual(error.section, 'section')
1546
1547 def test_parsing_error(self):
1548 with self.assertRaises(ValueError) as cm:
1549 configparser.ParsingError()
1550 self.assertEqual(str(cm.exception), "Required argument `source' not "
1551 "given.")
1552 with self.assertRaises(ValueError) as cm:
1553 configparser.ParsingError(source='source', filename='filename')
1554 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1555 "and `source'. Use `source'.")
1556 error = configparser.ParsingError(filename='source')
1557 self.assertEqual(error.source, 'source')
1558 with warnings.catch_warnings(record=True) as w:
1559 warnings.simplefilter("always", DeprecationWarning)
1560 self.assertEqual(error.filename, 'source')
1561 error.filename = 'filename'
1562 self.assertEqual(error.source, 'filename')
1563 for warning in w:
1564 self.assertTrue(warning.category is DeprecationWarning)
1565
1566 def test_interpolation_validation(self):
1567 parser = configparser.ConfigParser()
1568 parser.read_string("""
1569 [section]
1570 invalid_percent = %
1571 invalid_reference = %(()
1572 invalid_variable = %(does_not_exist)s
1573 """)
1574 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1575 parser['section']['invalid_percent']
1576 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1577 "'(', found: '%'")
1578 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1579 parser['section']['invalid_reference']
1580 self.assertEqual(str(cm.exception), "bad interpolation variable "
1581 "reference '%(()'")
1582
1583 def test_readfp_deprecation(self):
1584 sio = io.StringIO("""
1585 [section]
1586 option = value
1587 """)
1588 parser = configparser.ConfigParser()
1589 with warnings.catch_warnings(record=True) as w:
1590 warnings.simplefilter("always", DeprecationWarning)
1591 parser.readfp(sio, filename='StringIO')
1592 for warning in w:
1593 self.assertTrue(warning.category is DeprecationWarning)
1594 self.assertEqual(len(parser), 2)
1595 self.assertEqual(parser['section']['option'], 'value')
1596
1597 def test_safeconfigparser_deprecation(self):
1598 with warnings.catch_warnings(record=True) as w:
1599 warnings.simplefilter("always", DeprecationWarning)
1600 parser = configparser.SafeConfigParser()
1601 for warning in w:
1602 self.assertTrue(warning.category is DeprecationWarning)
1603
1604 def test_sectionproxy_repr(self):
1605 parser = configparser.ConfigParser()
1606 parser.read_string("""
1607 [section]
1608 key = value
1609 """)
1610 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001611
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001612 def test_inconsistent_converters_state(self):
1613 parser = configparser.ConfigParser()
1614 import decimal
1615 parser.converters['decimal'] = decimal.Decimal
1616 parser.read_string("""
1617 [s1]
1618 one = 1
1619 [s2]
1620 two = 2
1621 """)
1622 self.assertIn('decimal', parser.converters)
1623 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1624 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1625 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1626 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1627 del parser.getdecimal
1628 with self.assertRaises(AttributeError):
1629 parser.getdecimal('s1', 'one')
1630 self.assertIn('decimal', parser.converters)
1631 del parser.converters['decimal']
1632 self.assertNotIn('decimal', parser.converters)
1633 with self.assertRaises(AttributeError):
1634 parser.getdecimal('s1', 'one')
1635 with self.assertRaises(AttributeError):
1636 parser['s1'].getdecimal('one')
1637 with self.assertRaises(AttributeError):
1638 parser['s2'].getdecimal('two')
1639
Łukasz Langae7851952012-01-20 14:57:55 +01001640
1641class ExceptionPicklingTestCase(unittest.TestCase):
1642 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1643
1644 def test_error(self):
1645 import pickle
1646 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001647 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1648 pickled = pickle.dumps(e1, proto)
1649 e2 = pickle.loads(pickled)
1650 self.assertEqual(e1.message, e2.message)
1651 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001652
1653 def test_nosectionerror(self):
1654 import pickle
1655 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001656 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1657 pickled = pickle.dumps(e1, proto)
1658 e2 = pickle.loads(pickled)
1659 self.assertEqual(e1.message, e2.message)
1660 self.assertEqual(e1.args, e2.args)
1661 self.assertEqual(e1.section, e2.section)
1662 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001663
1664 def test_nooptionerror(self):
1665 import pickle
1666 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001667 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1668 pickled = pickle.dumps(e1, proto)
1669 e2 = pickle.loads(pickled)
1670 self.assertEqual(e1.message, e2.message)
1671 self.assertEqual(e1.args, e2.args)
1672 self.assertEqual(e1.section, e2.section)
1673 self.assertEqual(e1.option, e2.option)
1674 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001675
1676 def test_duplicatesectionerror(self):
1677 import pickle
1678 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001679 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1680 pickled = pickle.dumps(e1, proto)
1681 e2 = pickle.loads(pickled)
1682 self.assertEqual(e1.message, e2.message)
1683 self.assertEqual(e1.args, e2.args)
1684 self.assertEqual(e1.section, e2.section)
1685 self.assertEqual(e1.source, e2.source)
1686 self.assertEqual(e1.lineno, e2.lineno)
1687 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001688
1689 def test_duplicateoptionerror(self):
1690 import pickle
1691 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1692 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001693 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1694 pickled = pickle.dumps(e1, proto)
1695 e2 = pickle.loads(pickled)
1696 self.assertEqual(e1.message, e2.message)
1697 self.assertEqual(e1.args, e2.args)
1698 self.assertEqual(e1.section, e2.section)
1699 self.assertEqual(e1.option, e2.option)
1700 self.assertEqual(e1.source, e2.source)
1701 self.assertEqual(e1.lineno, e2.lineno)
1702 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001703
1704 def test_interpolationerror(self):
1705 import pickle
1706 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001707 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1708 pickled = pickle.dumps(e1, proto)
1709 e2 = pickle.loads(pickled)
1710 self.assertEqual(e1.message, e2.message)
1711 self.assertEqual(e1.args, e2.args)
1712 self.assertEqual(e1.section, e2.section)
1713 self.assertEqual(e1.option, e2.option)
1714 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001715
1716 def test_interpolationmissingoptionerror(self):
1717 import pickle
1718 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1719 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001720 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1721 pickled = pickle.dumps(e1, proto)
1722 e2 = pickle.loads(pickled)
1723 self.assertEqual(e1.message, e2.message)
1724 self.assertEqual(e1.args, e2.args)
1725 self.assertEqual(e1.section, e2.section)
1726 self.assertEqual(e1.option, e2.option)
1727 self.assertEqual(e1.reference, e2.reference)
1728 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001729
1730 def test_interpolationsyntaxerror(self):
1731 import pickle
1732 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001733 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1734 pickled = pickle.dumps(e1, proto)
1735 e2 = pickle.loads(pickled)
1736 self.assertEqual(e1.message, e2.message)
1737 self.assertEqual(e1.args, e2.args)
1738 self.assertEqual(e1.section, e2.section)
1739 self.assertEqual(e1.option, e2.option)
1740 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001741
1742 def test_interpolationdeptherror(self):
1743 import pickle
1744 e1 = configparser.InterpolationDepthError('option', 'section',
1745 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001746 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1747 pickled = pickle.dumps(e1, proto)
1748 e2 = pickle.loads(pickled)
1749 self.assertEqual(e1.message, e2.message)
1750 self.assertEqual(e1.args, e2.args)
1751 self.assertEqual(e1.section, e2.section)
1752 self.assertEqual(e1.option, e2.option)
1753 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001754
1755 def test_parsingerror(self):
1756 import pickle
1757 e1 = configparser.ParsingError('source')
1758 e1.append(1, 'line1')
1759 e1.append(2, 'line2')
1760 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001761 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1762 pickled = pickle.dumps(e1, proto)
1763 e2 = pickle.loads(pickled)
1764 self.assertEqual(e1.message, e2.message)
1765 self.assertEqual(e1.args, e2.args)
1766 self.assertEqual(e1.source, e2.source)
1767 self.assertEqual(e1.errors, e2.errors)
1768 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001769 e1 = configparser.ParsingError(filename='filename')
1770 e1.append(1, 'line1')
1771 e1.append(2, 'line2')
1772 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001773 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1774 pickled = pickle.dumps(e1, proto)
1775 e2 = pickle.loads(pickled)
1776 self.assertEqual(e1.message, e2.message)
1777 self.assertEqual(e1.args, e2.args)
1778 self.assertEqual(e1.source, e2.source)
1779 self.assertEqual(e1.errors, e2.errors)
1780 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001781
1782 def test_missingsectionheadererror(self):
1783 import pickle
1784 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001785 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1786 pickled = pickle.dumps(e1, proto)
1787 e2 = pickle.loads(pickled)
1788 self.assertEqual(e1.message, e2.message)
1789 self.assertEqual(e1.args, e2.args)
1790 self.assertEqual(e1.line, e2.line)
1791 self.assertEqual(e1.source, e2.source)
1792 self.assertEqual(e1.lineno, e2.lineno)
1793 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001794
1795
Łukasz Langacba24322012-07-07 18:54:08 +02001796class InlineCommentStrippingTestCase(unittest.TestCase):
1797 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1798 delimiter occurs earlier without preceding space.."""
1799
1800 def test_stripping(self):
1801 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1802 '//'))
1803 cfg.read_string("""
1804 [section]
1805 k1 = v1;still v1
1806 k2 = v2 ;a comment
1807 k3 = v3 ; also a comment
1808 k4 = v4;still v4 ;a comment
1809 k5 = v5;still v5 ; also a comment
1810 k6 = v6;still v6; and still v6 ;a comment
1811 k7 = v7;still v7; and still v7 ; also a comment
1812
1813 [multiprefix]
1814 k1 = v1;still v1 #a comment ; yeah, pretty much
1815 k2 = v2 // this already is a comment ; continued
1816 k3 = v3;#//still v3# and still v3 ; a comment
1817 """)
1818 s = cfg['section']
1819 self.assertEqual(s['k1'], 'v1;still v1')
1820 self.assertEqual(s['k2'], 'v2')
1821 self.assertEqual(s['k3'], 'v3')
1822 self.assertEqual(s['k4'], 'v4;still v4')
1823 self.assertEqual(s['k5'], 'v5;still v5')
1824 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1825 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1826 s = cfg['multiprefix']
1827 self.assertEqual(s['k1'], 'v1;still v1')
1828 self.assertEqual(s['k2'], 'v2')
1829 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1830
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001831
Łukasz Langa949053b2014-09-04 01:36:33 -07001832class ExceptionContextTestCase(unittest.TestCase):
1833 """ Test that implementation details doesn't leak
1834 through raising exceptions. """
1835
1836 def test_get_basic_interpolation(self):
1837 parser = configparser.ConfigParser()
1838 parser.read_string("""
1839 [Paths]
1840 home_dir: /Users
1841 my_dir: %(home_dir1)s/lumberjack
1842 my_pictures: %(my_dir)s/Pictures
1843 """)
1844 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1845 with cm:
1846 parser.get('Paths', 'my_dir')
1847 self.assertIs(cm.exception.__suppress_context__, True)
1848
1849 def test_get_extended_interpolation(self):
1850 parser = configparser.ConfigParser(
1851 interpolation=configparser.ExtendedInterpolation())
1852 parser.read_string("""
1853 [Paths]
1854 home_dir: /Users
1855 my_dir: ${home_dir1}/lumberjack
1856 my_pictures: ${my_dir}/Pictures
1857 """)
1858 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1859 with cm:
1860 parser.get('Paths', 'my_dir')
1861 self.assertIs(cm.exception.__suppress_context__, True)
1862
1863 def test_missing_options(self):
1864 parser = configparser.ConfigParser()
1865 parser.read_string("""
1866 [Paths]
1867 home_dir: /Users
1868 """)
1869 with self.assertRaises(configparser.NoSectionError) as cm:
1870 parser.options('test')
1871 self.assertIs(cm.exception.__suppress_context__, True)
1872
1873 def test_missing_section(self):
1874 config = configparser.ConfigParser()
1875 with self.assertRaises(configparser.NoSectionError) as cm:
1876 config.set('Section1', 'an_int', '15')
1877 self.assertIs(cm.exception.__suppress_context__, True)
1878
1879 def test_remove_option(self):
1880 config = configparser.ConfigParser()
1881 with self.assertRaises(configparser.NoSectionError) as cm:
1882 config.remove_option('Section1', 'an_int')
1883 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001884
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001885
1886class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1887 """Introduced in 3.5, issue #18159."""
1888
1889 config_class = configparser.ConfigParser
1890
1891 def newconfig(self, defaults=None):
1892 instance = super().newconfig(defaults=defaults)
1893 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1894 if e.strip()]
1895 return instance
1896
1897 def test_converters(self):
1898 cfg = self.newconfig()
1899 self.assertIn('boolean', cfg.converters)
1900 self.assertIn('list', cfg.converters)
1901 self.assertIsNone(cfg.converters['int'])
1902 self.assertIsNone(cfg.converters['float'])
1903 self.assertIsNone(cfg.converters['boolean'])
1904 self.assertIsNotNone(cfg.converters['list'])
1905 self.assertEqual(len(cfg.converters), 4)
1906 with self.assertRaises(ValueError):
1907 cfg.converters[''] = lambda v: v
1908 with self.assertRaises(ValueError):
1909 cfg.converters[None] = lambda v: v
1910 cfg.read_string("""
1911 [s]
1912 str = string
1913 int = 1
1914 float = 0.5
1915 list = a b c d e f g
1916 bool = yes
1917 """)
1918 s = cfg['s']
1919 self.assertEqual(s['str'], 'string')
1920 self.assertEqual(s['int'], '1')
1921 self.assertEqual(s['float'], '0.5')
1922 self.assertEqual(s['list'], 'a b c d e f g')
1923 self.assertEqual(s['bool'], 'yes')
1924 self.assertEqual(cfg.get('s', 'str'), 'string')
1925 self.assertEqual(cfg.get('s', 'int'), '1')
1926 self.assertEqual(cfg.get('s', 'float'), '0.5')
1927 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1928 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1929 self.assertEqual(cfg.get('s', 'str'), 'string')
1930 self.assertEqual(cfg.getint('s', 'int'), 1)
1931 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1932 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1933 'e', 'f', 'g'])
1934 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1935 self.assertEqual(s.get('str'), 'string')
1936 self.assertEqual(s.getint('int'), 1)
1937 self.assertEqual(s.getfloat('float'), 0.5)
1938 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1939 'e', 'f', 'g'])
1940 self.assertEqual(s.getboolean('bool'), True)
1941 with self.assertRaises(AttributeError):
1942 cfg.getdecimal('s', 'float')
1943 with self.assertRaises(AttributeError):
1944 s.getdecimal('float')
1945 import decimal
1946 cfg.converters['decimal'] = decimal.Decimal
1947 self.assertIn('decimal', cfg.converters)
1948 self.assertIsNotNone(cfg.converters['decimal'])
1949 self.assertEqual(len(cfg.converters), 5)
1950 dec0_5 = decimal.Decimal('0.5')
1951 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1952 self.assertEqual(s.getdecimal('float'), dec0_5)
1953 del cfg.converters['decimal']
1954 self.assertNotIn('decimal', cfg.converters)
1955 self.assertEqual(len(cfg.converters), 4)
1956 with self.assertRaises(AttributeError):
1957 cfg.getdecimal('s', 'float')
1958 with self.assertRaises(AttributeError):
1959 s.getdecimal('float')
1960 with self.assertRaises(KeyError):
1961 del cfg.converters['decimal']
1962 with self.assertRaises(KeyError):
1963 del cfg.converters['']
1964 with self.assertRaises(KeyError):
1965 del cfg.converters[None]
1966
1967
1968class BlatantOverrideConvertersTestCase(unittest.TestCase):
1969 """What if somebody overrode a getboolean()? We want to make sure that in
1970 this case the automatic converters do not kick in."""
1971
1972 config = """
1973 [one]
1974 one = false
1975 two = false
1976 three = long story short
1977
1978 [two]
1979 one = false
1980 two = false
1981 three = four
1982 """
1983
1984 def test_converters_at_init(self):
1985 cfg = configparser.ConfigParser(converters={'len': len})
1986 cfg.read_string(self.config)
1987 self._test_len(cfg)
1988 self.assertIsNotNone(cfg.converters['len'])
1989
1990 def test_inheritance(self):
1991 class StrangeConfigParser(configparser.ConfigParser):
1992 gettysburg = 'a historic borough in south central Pennsylvania'
1993
1994 def getboolean(self, section, option, *, raw=False, vars=None,
1995 fallback=configparser._UNSET):
1996 if section == option:
1997 return True
1998 return super().getboolean(section, option, raw=raw, vars=vars,
1999 fallback=fallback)
2000 def getlen(self, section, option, *, raw=False, vars=None,
2001 fallback=configparser._UNSET):
2002 return self._get_conv(section, option, len, raw=raw, vars=vars,
2003 fallback=fallback)
2004
2005 cfg = StrangeConfigParser()
2006 cfg.read_string(self.config)
2007 self._test_len(cfg)
2008 self.assertIsNone(cfg.converters['len'])
2009 self.assertTrue(cfg.getboolean('one', 'one'))
2010 self.assertTrue(cfg.getboolean('two', 'two'))
2011 self.assertFalse(cfg.getboolean('one', 'two'))
2012 self.assertFalse(cfg.getboolean('two', 'one'))
2013 cfg.converters['boolean'] = cfg._convert_to_boolean
2014 self.assertFalse(cfg.getboolean('one', 'one'))
2015 self.assertFalse(cfg.getboolean('two', 'two'))
2016 self.assertFalse(cfg.getboolean('one', 'two'))
2017 self.assertFalse(cfg.getboolean('two', 'one'))
2018
2019 def _test_len(self, cfg):
2020 self.assertEqual(len(cfg.converters), 4)
2021 self.assertIn('boolean', cfg.converters)
2022 self.assertIn('len', cfg.converters)
2023 self.assertNotIn('tysburg', cfg.converters)
2024 self.assertIsNone(cfg.converters['int'])
2025 self.assertIsNone(cfg.converters['float'])
2026 self.assertIsNone(cfg.converters['boolean'])
2027 self.assertEqual(cfg.getlen('one', 'one'), 5)
2028 self.assertEqual(cfg.getlen('one', 'two'), 5)
2029 self.assertEqual(cfg.getlen('one', 'three'), 16)
2030 self.assertEqual(cfg.getlen('two', 'one'), 5)
2031 self.assertEqual(cfg.getlen('two', 'two'), 5)
2032 self.assertEqual(cfg.getlen('two', 'three'), 4)
2033 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2034 with self.assertRaises(configparser.NoOptionError):
2035 cfg.getlen('two', 'four')
2036 self.assertEqual(cfg['one'].getlen('one'), 5)
2037 self.assertEqual(cfg['one'].getlen('two'), 5)
2038 self.assertEqual(cfg['one'].getlen('three'), 16)
2039 self.assertEqual(cfg['two'].getlen('one'), 5)
2040 self.assertEqual(cfg['two'].getlen('two'), 5)
2041 self.assertEqual(cfg['two'].getlen('three'), 4)
2042 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2043 self.assertEqual(cfg['two'].getlen('four'), None)
2044
2045 def test_instance_assignment(self):
2046 cfg = configparser.ConfigParser()
2047 cfg.getboolean = lambda section, option: True
2048 cfg.getlen = lambda section, option: len(cfg[section][option])
2049 cfg.read_string(self.config)
2050 self.assertEqual(len(cfg.converters), 3)
2051 self.assertIn('boolean', cfg.converters)
2052 self.assertNotIn('len', cfg.converters)
2053 self.assertIsNone(cfg.converters['int'])
2054 self.assertIsNone(cfg.converters['float'])
2055 self.assertIsNone(cfg.converters['boolean'])
2056 self.assertTrue(cfg.getboolean('one', 'one'))
2057 self.assertTrue(cfg.getboolean('two', 'two'))
2058 self.assertTrue(cfg.getboolean('one', 'two'))
2059 self.assertTrue(cfg.getboolean('two', 'one'))
2060 cfg.converters['boolean'] = cfg._convert_to_boolean
2061 self.assertFalse(cfg.getboolean('one', 'one'))
2062 self.assertFalse(cfg.getboolean('two', 'two'))
2063 self.assertFalse(cfg.getboolean('one', 'two'))
2064 self.assertFalse(cfg.getboolean('two', 'one'))
2065 self.assertEqual(cfg.getlen('one', 'one'), 5)
2066 self.assertEqual(cfg.getlen('one', 'two'), 5)
2067 self.assertEqual(cfg.getlen('one', 'three'), 16)
2068 self.assertEqual(cfg.getlen('two', 'one'), 5)
2069 self.assertEqual(cfg.getlen('two', 'two'), 5)
2070 self.assertEqual(cfg.getlen('two', 'three'), 4)
2071 # If a getter impl is assigned straight to the instance, it won't
2072 # be available on the section proxies.
2073 with self.assertRaises(AttributeError):
2074 self.assertEqual(cfg['one'].getlen('one'), 5)
2075 with self.assertRaises(AttributeError):
2076 self.assertEqual(cfg['two'].getlen('one'), 5)
2077
2078
Martin Panter2b9b70b2016-09-09 06:46:48 +00002079class MiscTestCase(unittest.TestCase):
2080 def test__all__(self):
2081 blacklist = {"Error"}
2082 support.check__all__(self, configparser, blacklist=blacklist)
2083
2084
Ezio Melottidc1fa802013-01-11 06:30:57 +02002085if __name__ == '__main__':
2086 unittest.main()