blob: 470d2cd1f32e60f000cf2978487b81d678e37673 [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
Raymond Hettingerf80680d2008-02-06 00:07:11 +000012class SortedDict(collections.UserDict):
Fred Drake03c44a32010-02-19 06:08:41 +000013
Thomas Wouters89f507f2006-12-13 04:49:30 +000014 def items(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000015 return sorted(self.data.items())
Thomas Wouters89f507f2006-12-13 04:49:30 +000016
17 def keys(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000018 return sorted(self.data.keys())
Thomas Wouters9fe394c2007-02-05 01:24:16 +000019
Thomas Wouters89f507f2006-12-13 04:49:30 +000020 def values(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000021 return [i[1] for i in self.items()]
Thomas Wouters89f507f2006-12-13 04:49:30 +000022
Łukasz Langae698cd52011-04-28 10:58:57 +020023 def iteritems(self):
24 return iter(self.items())
25
26 def iterkeys(self):
27 return iter(self.keys())
28
29 def itervalues(self):
30 return iter(self.values())
31
Thomas Wouters89f507f2006-12-13 04:49:30 +000032 __iter__ = iterkeys
Fred Drake3d5f7e82000-12-04 16:30:40 +000033
Fred Drake03c44a32010-02-19 06:08:41 +000034
Ezio Melottidc1fa802013-01-11 06:30:57 +020035class CfgParserTestCaseClass:
Fred Drake03c44a32010-02-19 06:08:41 +000036 allow_no_value = False
Georg Brandl96a60ae2010-07-28 13:13:46 +000037 delimiters = ('=', ':')
38 comment_prefixes = (';', '#')
Łukasz Langab25a7912010-12-17 01:32:29 +000039 inline_comment_prefixes = (';', '#')
Georg Brandl96a60ae2010-07-28 13:13:46 +000040 empty_lines_in_values = True
41 dict_type = configparser._default_dict
Fred Drakea4923622010-08-09 12:52:45 +000042 strict = False
Łukasz Langac264c092010-11-20 16:15:37 +000043 default_section = configparser.DEFAULTSECT
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000044 interpolation = configparser._UNSET
Fred Drake03c44a32010-02-19 06:08:41 +000045
Fred Drakec6f28912002-10-25 19:40:49 +000046 def newconfig(self, defaults=None):
Georg Brandl96a60ae2010-07-28 13:13:46 +000047 arguments = dict(
Fred Drakea4923622010-08-09 12:52:45 +000048 defaults=defaults,
Georg Brandl96a60ae2010-07-28 13:13:46 +000049 allow_no_value=self.allow_no_value,
50 delimiters=self.delimiters,
51 comment_prefixes=self.comment_prefixes,
Łukasz Langab25a7912010-12-17 01:32:29 +000052 inline_comment_prefixes=self.inline_comment_prefixes,
Georg Brandl96a60ae2010-07-28 13:13:46 +000053 empty_lines_in_values=self.empty_lines_in_values,
54 dict_type=self.dict_type,
Fred Drakea4923622010-08-09 12:52:45 +000055 strict=self.strict,
Łukasz Langac264c092010-11-20 16:15:37 +000056 default_section=self.default_section,
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000057 interpolation=self.interpolation,
Georg Brandl96a60ae2010-07-28 13:13:46 +000058 )
Łukasz Langa7f64c8a2010-12-16 01:16:22 +000059 instance = self.config_class(**arguments)
Łukasz Langab6a6f5f2010-12-03 16:28:00 +000060 return instance
Fred Drake8ef67672000-09-27 22:45:25 +000061
Fred Drakec6f28912002-10-25 19:40:49 +000062 def fromstring(self, string, defaults=None):
63 cf = self.newconfig(defaults)
Fred Drakea4923622010-08-09 12:52:45 +000064 cf.read_string(string)
Fred Drakec6f28912002-10-25 19:40:49 +000065 return cf
Fred Drake8ef67672000-09-27 22:45:25 +000066
Georg Brandl96a60ae2010-07-28 13:13:46 +000067class BasicTestCase(CfgParserTestCaseClass):
68
Fred Drakea4923622010-08-09 12:52:45 +000069 def basic_test(self, cf):
Georg Brandl96a60ae2010-07-28 13:13:46 +000070 E = ['Commented Bar',
71 'Foo Bar',
72 'Internationalized Stuff',
73 'Long Line',
74 'Section\\with$weird%characters[\t',
75 'Spaces',
76 'Spacey Bar',
77 'Spacey Bar From The Beginning',
Fred Drakecc645b92010-09-04 04:35:34 +000078 'Types',
Fred Drake03c44a32010-02-19 06:08:41 +000079 ]
Łukasz Langa26d513c2010-11-10 18:57:39 +000080
Fred Drake03c44a32010-02-19 06:08:41 +000081 if self.allow_no_value:
Fred Drakecc645b92010-09-04 04:35:34 +000082 E.append('NoValue')
Fred Drake03c44a32010-02-19 06:08:41 +000083 E.sort()
Łukasz Langa71b37a52010-12-17 21:56:32 +000084 F = [('baz', 'qwe'), ('foo', 'bar3')]
Łukasz Langa26d513c2010-11-10 18:57:39 +000085
86 # API access
87 L = cf.sections()
88 L.sort()
Fred Drakec6f28912002-10-25 19:40:49 +000089 eq = self.assertEqual
Fred Drake03c44a32010-02-19 06:08:41 +000090 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +000091 L = cf.items('Spacey Bar From The Beginning')
92 L.sort()
93 eq(L, F)
Fred Drake8ef67672000-09-27 22:45:25 +000094
Łukasz Langa26d513c2010-11-10 18:57:39 +000095 # mapping access
96 L = [section for section in cf]
97 L.sort()
Łukasz Langac264c092010-11-20 16:15:37 +000098 E.append(self.default_section)
Łukasz Langa26d513c2010-11-10 18:57:39 +000099 E.sort()
100 eq(L, E)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000101 L = cf['Spacey Bar From The Beginning'].items()
102 L = sorted(list(L))
103 eq(L, F)
104 L = cf.items()
105 L = sorted(list(L))
106 self.assertEqual(len(L), len(E))
107 for name, section in L:
108 eq(name, section.name)
109 eq(cf.defaults(), cf[self.default_section])
Łukasz Langa26d513c2010-11-10 18:57:39 +0000110
Fred Drakec6f28912002-10-25 19:40:49 +0000111 # The use of spaces in the section names serves as a
112 # regression test for SourceForge bug #583248:
113 # http://www.python.org/sf/583248
Łukasz Langa26d513c2010-11-10 18:57:39 +0000114
115 # API access
116 eq(cf.get('Foo Bar', 'foo'), 'bar1')
117 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
118 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000119 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000120 eq(cf.get('Commented Bar', 'foo'), 'bar4')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000121 eq(cf.get('Commented Bar', 'baz'), 'qwe')
Fred Drakec6f28912002-10-25 19:40:49 +0000122 eq(cf.get('Spaces', 'key with spaces'), 'value')
123 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
Fred Drakecc645b92010-09-04 04:35:34 +0000124 eq(cf.getint('Types', 'int'), 42)
125 eq(cf.get('Types', 'int'), "42")
126 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
127 eq(cf.get('Types', 'float'), "0.44")
128 eq(cf.getboolean('Types', 'boolean'), False)
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000129 eq(cf.get('Types', '123'), 'strange but acceptable')
Fred Drake03c44a32010-02-19 06:08:41 +0000130 if self.allow_no_value:
131 eq(cf.get('NoValue', 'option-without-value'), None)
Fred Drake3d5f7e82000-12-04 16:30:40 +0000132
Łukasz Langa26d513c2010-11-10 18:57:39 +0000133 # test vars= and fallback=
134 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
Fred Drakecc645b92010-09-04 04:35:34 +0000135 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
136 with self.assertRaises(configparser.NoSectionError):
137 cf.get('No Such Foo Bar', 'foo')
138 with self.assertRaises(configparser.NoOptionError):
139 cf.get('Foo Bar', 'no-such-foo')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000140 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
141 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
142 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
143 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
144 eq(cf.getint('Types', 'int', fallback=18), 42)
145 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
146 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000147 with self.assertRaises(configparser.NoOptionError):
148 cf.getint('Types', 'no-such-int')
Fred Drakecc645b92010-09-04 04:35:34 +0000149 self.assertAlmostEqual(cf.getfloat('Types', 'float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000150 fallback=0.0), 0.44)
Fred Drakecc645b92010-09-04 04:35:34 +0000151 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000152 fallback=0.0), 0.0)
153 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000154 with self.assertRaises(configparser.NoOptionError):
155 cf.getfloat('Types', 'no-such-float')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000156 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
157 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
Fred Drakecc645b92010-09-04 04:35:34 +0000158 "yes") # sic!
Łukasz Langa26d513c2010-11-10 18:57:39 +0000159 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000160 with self.assertRaises(configparser.NoOptionError):
161 cf.getboolean('Types', 'no-such-boolean')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000162 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
Fred Drakecc645b92010-09-04 04:35:34 +0000163 if self.allow_no_value:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000164 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
Fred Drakecc645b92010-09-04 04:35:34 +0000165 eq(cf.get('NoValue', 'no-such-option-without-value',
Łukasz Langa26d513c2010-11-10 18:57:39 +0000166 fallback=False), False)
Fred Drakecc645b92010-09-04 04:35:34 +0000167
Łukasz Langa26d513c2010-11-10 18:57:39 +0000168 # mapping access
169 eq(cf['Foo Bar']['foo'], 'bar1')
170 eq(cf['Spacey Bar']['foo'], 'bar2')
Łukasz Langaa73dc9d2010-11-21 13:56:42 +0000171 section = cf['Spacey Bar From The Beginning']
172 eq(section.name, 'Spacey Bar From The Beginning')
173 self.assertIs(section.parser, cf)
174 with self.assertRaises(AttributeError):
175 section.name = 'Name is read-only'
176 with self.assertRaises(AttributeError):
177 section.parser = 'Parser is read-only'
178 eq(section['foo'], 'bar3')
179 eq(section['baz'], 'qwe')
Łukasz Langa26d513c2010-11-10 18:57:39 +0000180 eq(cf['Commented Bar']['foo'], 'bar4')
181 eq(cf['Commented Bar']['baz'], 'qwe')
182 eq(cf['Spaces']['key with spaces'], 'value')
183 eq(cf['Spaces']['another with spaces'], 'splat!')
184 eq(cf['Long Line']['foo'],
185 'this line is much, much longer than my editor\nlikes it.')
186 if self.allow_no_value:
187 eq(cf['NoValue']['option-without-value'], None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000188 # test vars= and fallback=
189 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
190 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
191 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
192 with self.assertRaises(KeyError):
193 cf['No Such Foo Bar']['foo']
194 with self.assertRaises(KeyError):
195 cf['Foo Bar']['no-such-foo']
196 with self.assertRaises(KeyError):
197 cf['No Such Foo Bar'].get('foo', fallback='baz')
198 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
199 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
Łukasz Langa71b37a52010-12-17 21:56:32 +0000200 eq(cf['Foo Bar'].get('no-such-foo'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000201 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
202 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
203 with self.assertRaises(KeyError):
204 cf['No Such Spacey Bar'].get('foo', None)
205 eq(cf['Types'].getint('int', 18), 42)
206 eq(cf['Types'].getint('int', fallback=18), 42)
207 eq(cf['Types'].getint('no-such-int', 18), 18)
208 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
209 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
210 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000211 eq(cf['Types'].getint('no-such-int'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000212 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
213 self.assertAlmostEqual(cf['Types'].getfloat('float',
214 fallback=0.0), 0.44)
215 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
216 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
217 fallback=0.0), 0.0)
218 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
219 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
Łukasz Langa71b37a52010-12-17 21:56:32 +0000220 eq(cf['Types'].getfloat('no-such-float'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000221 eq(cf['Types'].getboolean('boolean', True), False)
222 eq(cf['Types'].getboolean('boolean', fallback=True), False)
223 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
224 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
225 "yes") # sic!
226 eq(cf['Types'].getboolean('no-such-boolean', True), True)
227 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000228 eq(cf['Types'].getboolean('no-such-boolean'), None)
Łukasz Langa2f0fd0f2010-12-04 17:48:18 +0000229 if self.allow_no_value:
230 eq(cf['NoValue'].get('option-without-value', False), None)
231 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
232 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
233 eq(cf['NoValue'].get('no-such-option-without-value',
234 fallback=False), False)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000235
Łukasz Langa71b37a52010-12-17 21:56:32 +0000236 # Make sure the right things happen for remove_section() and
237 # remove_option(); added to include check for SourceForge bug #123324.
Łukasz Langa26d513c2010-11-10 18:57:39 +0000238
Łukasz Langa71b37a52010-12-17 21:56:32 +0000239 cf[self.default_section]['this_value'] = '1'
240 cf[self.default_section]['that_value'] = '2'
241
242 # API access
243 self.assertTrue(cf.remove_section('Spaces'))
244 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
245 self.assertFalse(cf.remove_section('Spaces'))
246 self.assertFalse(cf.remove_section(self.default_section))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000247 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000248 "remove_option() failed to report existence of option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
Fred Drakec6f28912002-10-25 19:40:49 +0000250 "remove_option() failed to remove option")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000251 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
Mark Dickinson934896d2009-02-21 20:59:32 +0000252 "remove_option() failed to report non-existence of option"
Fred Drakec6f28912002-10-25 19:40:49 +0000253 " that was removed")
Łukasz Langa71b37a52010-12-17 21:56:32 +0000254 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
255 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
256 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
257 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
258 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
Fred Drakec6f28912002-10-25 19:40:49 +0000259
Michael Foordbd6c0792010-07-25 23:09:25 +0000260 with self.assertRaises(configparser.NoSectionError) as cm:
261 cf.remove_option('No Such Section', 'foo')
262 self.assertEqual(cm.exception.args, ('No Such Section',))
Fred Drakec6f28912002-10-25 19:40:49 +0000263
264 eq(cf.get('Long Line', 'foo'),
Andrew M. Kuchling1bf71172002-03-08 18:10:12 +0000265 'this line is much, much longer than my editor\nlikes it.')
Fred Drake3d5f7e82000-12-04 16:30:40 +0000266
Łukasz Langa26d513c2010-11-10 18:57:39 +0000267 # mapping access
Łukasz Langa71b37a52010-12-17 21:56:32 +0000268 del cf['Types']
269 self.assertFalse('Types' in cf)
270 with self.assertRaises(KeyError):
271 del cf['Types']
272 with self.assertRaises(ValueError):
273 del cf[self.default_section]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000274 del cf['Spacey Bar']['foo']
275 self.assertFalse('foo' in cf['Spacey Bar'])
276 with self.assertRaises(KeyError):
277 del cf['Spacey Bar']['foo']
Łukasz Langa71b37a52010-12-17 21:56:32 +0000278 self.assertTrue('that_value' in cf['Spacey Bar'])
279 with self.assertRaises(KeyError):
280 del cf['Spacey Bar']['that_value']
281 del cf[self.default_section]['that_value']
282 self.assertFalse('that_value' in cf['Spacey Bar'])
283 with self.assertRaises(KeyError):
284 del cf[self.default_section]['that_value']
Łukasz Langa26d513c2010-11-10 18:57:39 +0000285 with self.assertRaises(KeyError):
286 del cf['No Such Section']['foo']
287
Łukasz Langa71b37a52010-12-17 21:56:32 +0000288 # Don't add new asserts below in this method as most of the options
289 # and sections are now removed.
290
Fred Drakea4923622010-08-09 12:52:45 +0000291 def test_basic(self):
292 config_string = """\
293[Foo Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000294foo{0[0]}bar1
Fred Drakea4923622010-08-09 12:52:45 +0000295[Spacey Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000296foo {0[0]} bar2
Fred Drakea4923622010-08-09 12:52:45 +0000297[Spacey Bar From The Beginning]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000298 foo {0[0]} bar3
Fred Drakea4923622010-08-09 12:52:45 +0000299 baz {0[0]} qwe
300[Commented Bar]
Łukasz Langa26d513c2010-11-10 18:57:39 +0000301foo{0[1]} bar4 {1[1]} comment
Fred Drakea4923622010-08-09 12:52:45 +0000302baz{0[0]}qwe {1[0]}another one
303[Long Line]
304foo{0[1]} this line is much, much longer than my editor
305 likes it.
306[Section\\with$weird%characters[\t]
307[Internationalized Stuff]
308foo[bg]{0[1]} Bulgarian
309foo{0[0]}Default
310foo[en]{0[0]}English
311foo[de]{0[0]}Deutsch
312[Spaces]
313key with spaces {0[1]} value
314another with spaces {0[0]} splat!
Fred Drakecc645b92010-09-04 04:35:34 +0000315[Types]
316int {0[1]} 42
317float {0[0]} 0.44
318boolean {0[0]} NO
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000319123 {0[1]} strange but acceptable
Fred Drakea4923622010-08-09 12:52:45 +0000320""".format(self.delimiters, self.comment_prefixes)
321 if self.allow_no_value:
322 config_string += (
323 "[NoValue]\n"
324 "option-without-value\n"
325 )
326 cf = self.fromstring(config_string)
327 self.basic_test(cf)
328 if self.strict:
329 with self.assertRaises(configparser.DuplicateOptionError):
330 cf.read_string(textwrap.dedent("""\
331 [Duplicate Options Here]
332 option {0[0]} with a value
333 option {0[1]} with another value
334 """.format(self.delimiters)))
335 with self.assertRaises(configparser.DuplicateSectionError):
336 cf.read_string(textwrap.dedent("""\
337 [And Now For Something]
338 completely different {0[0]} True
339 [And Now For Something]
340 the larch {0[1]} 1
341 """.format(self.delimiters)))
342 else:
343 cf.read_string(textwrap.dedent("""\
344 [Duplicate Options Here]
345 option {0[0]} with a value
346 option {0[1]} with another value
347 """.format(self.delimiters)))
348
349 cf.read_string(textwrap.dedent("""\
350 [And Now For Something]
351 completely different {0[0]} True
352 [And Now For Something]
353 the larch {0[1]} 1
354 """.format(self.delimiters)))
355
356 def test_basic_from_dict(self):
357 config = {
358 "Foo Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000359 "foo": "bar1",
Fred Drakea4923622010-08-09 12:52:45 +0000360 },
361 "Spacey Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000362 "foo": "bar2",
Fred Drakea4923622010-08-09 12:52:45 +0000363 },
364 "Spacey Bar From The Beginning": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000365 "foo": "bar3",
Fred Drakea4923622010-08-09 12:52:45 +0000366 "baz": "qwe",
367 },
368 "Commented Bar": {
Łukasz Langa26d513c2010-11-10 18:57:39 +0000369 "foo": "bar4",
Fred Drakea4923622010-08-09 12:52:45 +0000370 "baz": "qwe",
371 },
372 "Long Line": {
373 "foo": "this line is much, much longer than my editor\nlikes "
374 "it.",
375 },
376 "Section\\with$weird%characters[\t": {
377 },
378 "Internationalized Stuff": {
379 "foo[bg]": "Bulgarian",
380 "foo": "Default",
381 "foo[en]": "English",
382 "foo[de]": "Deutsch",
383 },
384 "Spaces": {
385 "key with spaces": "value",
386 "another with spaces": "splat!",
Fred Drakecc645b92010-09-04 04:35:34 +0000387 },
388 "Types": {
389 "int": 42,
390 "float": 0.44,
391 "boolean": False,
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000392 123: "strange but acceptable",
Fred Drakecc645b92010-09-04 04:35:34 +0000393 },
Fred Drakea4923622010-08-09 12:52:45 +0000394 }
395 if self.allow_no_value:
396 config.update({
397 "NoValue": {
398 "option-without-value": None,
399 }
400 })
401 cf = self.newconfig()
402 cf.read_dict(config)
403 self.basic_test(cf)
404 if self.strict:
Łukasz Langa71b37a52010-12-17 21:56:32 +0000405 with self.assertRaises(configparser.DuplicateSectionError):
406 cf.read_dict({
407 '1': {'key': 'value'},
408 1: {'key2': 'value2'},
409 })
Fred Drakea4923622010-08-09 12:52:45 +0000410 with self.assertRaises(configparser.DuplicateOptionError):
411 cf.read_dict({
412 "Duplicate Options Here": {
413 'option': 'with a value',
414 'OPTION': 'with another value',
415 },
416 })
417 else:
418 cf.read_dict({
Łukasz Langa71b37a52010-12-17 21:56:32 +0000419 'section': {'key': 'value'},
420 'SECTION': {'key2': 'value2'},
421 })
422 cf.read_dict({
Fred Drakea4923622010-08-09 12:52:45 +0000423 "Duplicate Options Here": {
424 'option': 'with a value',
425 'OPTION': 'with another value',
426 },
427 })
428
Fred Drakec6f28912002-10-25 19:40:49 +0000429 def test_case_sensitivity(self):
430 cf = self.newconfig()
431 cf.add_section("A")
432 cf.add_section("a")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000433 cf.add_section("B")
Fred Drakec6f28912002-10-25 19:40:49 +0000434 L = cf.sections()
435 L.sort()
436 eq = self.assertEqual
Łukasz Langa26d513c2010-11-10 18:57:39 +0000437 eq(L, ["A", "B", "a"])
Fred Drakec6f28912002-10-25 19:40:49 +0000438 cf.set("a", "B", "value")
439 eq(cf.options("a"), ["b"])
440 eq(cf.get("a", "b"), "value",
Fred Drake3c823aa2001-02-26 21:55:34 +0000441 "could not locate option, expecting case-insensitive option names")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000442 with self.assertRaises(configparser.NoSectionError):
443 # section names are case-sensitive
444 cf.set("b", "A", "value")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000445 self.assertTrue(cf.has_option("a", "b"))
Łukasz Langa71b37a52010-12-17 21:56:32 +0000446 self.assertFalse(cf.has_option("b", "b"))
Fred Drakec6f28912002-10-25 19:40:49 +0000447 cf.set("A", "A-B", "A-B value")
448 for opt in ("a-b", "A-b", "a-B", "A-B"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000449 self.assertTrue(
Fred Drakec6f28912002-10-25 19:40:49 +0000450 cf.has_option("A", opt),
451 "has_option() returned false for option which should exist")
452 eq(cf.options("A"), ["a-b"])
453 eq(cf.options("a"), ["b"])
454 cf.remove_option("a", "B")
455 eq(cf.options("a"), [])
Fred Drake3c823aa2001-02-26 21:55:34 +0000456
Fred Drakec6f28912002-10-25 19:40:49 +0000457 # SF bug #432369:
458 cf = self.fromstring(
Łukasz Langa26d513c2010-11-10 18:57:39 +0000459 "[MySection]\nOption{} first line \n\tsecond line \n".format(
Georg Brandl96a60ae2010-07-28 13:13:46 +0000460 self.delimiters[0]))
Fred Drakec6f28912002-10-25 19:40:49 +0000461 eq(cf.options("MySection"), ["option"])
462 eq(cf.get("MySection", "Option"), "first line\nsecond line")
Fred Drakebeb67132001-07-06 17:22:48 +0000463
Fred Drakec6f28912002-10-25 19:40:49 +0000464 # SF bug #561822:
Georg Brandl96a60ae2010-07-28 13:13:46 +0000465 cf = self.fromstring("[section]\n"
466 "nekey{}nevalue\n".format(self.delimiters[0]),
Fred Drakec6f28912002-10-25 19:40:49 +0000467 defaults={"key":"value"})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000468 self.assertTrue(cf.has_option("section", "Key"))
Fred Drake309db062002-09-27 15:35:23 +0000469
Fred Drake3c823aa2001-02-26 21:55:34 +0000470
Łukasz Langa26d513c2010-11-10 18:57:39 +0000471 def test_case_sensitivity_mapping_access(self):
472 cf = self.newconfig()
473 cf["A"] = {}
474 cf["a"] = {"B": "value"}
475 cf["B"] = {}
476 L = [section for section in cf]
477 L.sort()
478 eq = self.assertEqual
Ezio Melotti263cbdf2010-11-29 02:02:10 +0000479 elem_eq = self.assertCountEqual
Łukasz Langac264c092010-11-20 16:15:37 +0000480 eq(L, sorted(["A", "B", self.default_section, "a"]))
Łukasz Langa26d513c2010-11-10 18:57:39 +0000481 eq(cf["a"].keys(), {"b"})
482 eq(cf["a"]["b"], "value",
483 "could not locate option, expecting case-insensitive option names")
484 with self.assertRaises(KeyError):
485 # section names are case-sensitive
486 cf["b"]["A"] = "value"
487 self.assertTrue("b" in cf["a"])
488 cf["A"]["A-B"] = "A-B value"
489 for opt in ("a-b", "A-b", "a-B", "A-B"):
490 self.assertTrue(
491 opt in cf["A"],
492 "has_option() returned false for option which should exist")
493 eq(cf["A"].keys(), {"a-b"})
494 eq(cf["a"].keys(), {"b"})
495 del cf["a"]["B"]
496 elem_eq(cf["a"].keys(), {})
497
498 # SF bug #432369:
499 cf = self.fromstring(
500 "[MySection]\nOption{} first line \n\tsecond line \n".format(
501 self.delimiters[0]))
502 eq(cf["MySection"].keys(), {"option"})
503 eq(cf["MySection"]["Option"], "first line\nsecond line")
504
505 # SF bug #561822:
506 cf = self.fromstring("[section]\n"
507 "nekey{}nevalue\n".format(self.delimiters[0]),
508 defaults={"key":"value"})
509 self.assertTrue("Key" in cf["section"])
510
David Goodger68a1abd2004-10-03 15:40:25 +0000511 def test_default_case_sensitivity(self):
512 cf = self.newconfig({"foo": "Bar"})
513 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000514 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000515 "could not locate option, expecting case-insensitive option names")
516 cf = self.newconfig({"Foo": "Bar"})
517 self.assertEqual(
Łukasz Langac264c092010-11-20 16:15:37 +0000518 cf.get(self.default_section, "Foo"), "Bar",
David Goodger68a1abd2004-10-03 15:40:25 +0000519 "could not locate option, expecting case-insensitive defaults")
520
Fred Drakec6f28912002-10-25 19:40:49 +0000521 def test_parse_errors(self):
Fred Drakea4923622010-08-09 12:52:45 +0000522 cf = self.newconfig()
523 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000524 "[Foo]\n"
525 "{}val-without-opt-name\n".format(self.delimiters[0]))
Fred Drakea4923622010-08-09 12:52:45 +0000526 self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000527 "[Foo]\n"
528 "{}val-without-opt-name\n".format(self.delimiters[1]))
Fred Drakea4923622010-08-09 12:52:45 +0000529 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000530 "No Section!\n")
Michael Foordbd6c0792010-07-25 23:09:25 +0000531 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
Georg Brandl96a60ae2010-07-28 13:13:46 +0000532 if not self.allow_no_value:
Fred Drakea4923622010-08-09 12:52:45 +0000533 e = self.parse_error(cf, configparser.ParsingError,
Georg Brandl96a60ae2010-07-28 13:13:46 +0000534 "[Foo]\n wrong-indent\n")
535 self.assertEqual(e.args, ('<???>',))
Florent Xicluna42d54452010-09-22 22:35:38 +0000536 # read_file on a real file
537 tricky = support.findfile("cfgparser.3")
538 if self.delimiters[0] == '=':
539 error = configparser.ParsingError
540 expected = (tricky,)
541 else:
542 error = configparser.MissingSectionHeaderError
543 expected = (tricky, 1,
544 ' # INI with as many tricky parts as possible\n')
Florent Xiclunad12a4202010-09-23 00:46:13 +0000545 with open(tricky, encoding='utf-8') as f:
Florent Xicluna42d54452010-09-22 22:35:38 +0000546 e = self.parse_error(cf, error, f)
547 self.assertEqual(e.args, expected)
Fred Drake168bead2001-10-08 17:13:12 +0000548
Fred Drakea4923622010-08-09 12:52:45 +0000549 def parse_error(self, cf, exc, src):
Florent Xicluna42d54452010-09-22 22:35:38 +0000550 if hasattr(src, 'readline'):
551 sio = src
552 else:
553 sio = io.StringIO(src)
Michael Foordbd6c0792010-07-25 23:09:25 +0000554 with self.assertRaises(exc) as cm:
Fred Drakea4923622010-08-09 12:52:45 +0000555 cf.read_file(sio)
Michael Foordbd6c0792010-07-25 23:09:25 +0000556 return cm.exception
Fred Drake168bead2001-10-08 17:13:12 +0000557
Fred Drakec6f28912002-10-25 19:40:49 +0000558 def test_query_errors(self):
559 cf = self.newconfig()
560 self.assertEqual(cf.sections(), [],
561 "new ConfigParser should have no defined sections")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000562 self.assertFalse(cf.has_section("Foo"),
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000563 "new ConfigParser should have no acknowledged "
564 "sections")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000565 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000566 cf.options("Foo")
Georg Brandl96a60ae2010-07-28 13:13:46 +0000567 with self.assertRaises(configparser.NoSectionError):
Michael Foordbd6c0792010-07-25 23:09:25 +0000568 cf.set("foo", "bar", "value")
Fred Drakea4923622010-08-09 12:52:45 +0000569 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000570 self.assertEqual(e.args, ("foo",))
Fred Drakec6f28912002-10-25 19:40:49 +0000571 cf.add_section("foo")
Fred Drakea4923622010-08-09 12:52:45 +0000572 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
Michael Foordbd6c0792010-07-25 23:09:25 +0000573 self.assertEqual(e.args, ("bar", "foo"))
Fred Drake8ef67672000-09-27 22:45:25 +0000574
Fred Drakea4923622010-08-09 12:52:45 +0000575 def get_error(self, cf, exc, section, option):
Fred Drake54782192002-12-31 06:57:25 +0000576 try:
Fred Drakea4923622010-08-09 12:52:45 +0000577 cf.get(section, option)
Guido van Rossumb940e112007-01-10 16:19:56 +0000578 except exc as e:
Fred Drake54782192002-12-31 06:57:25 +0000579 return e
580 else:
581 self.fail("expected exception type %s.%s"
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300582 % (exc.__module__, exc.__qualname__))
Fred Drake3af0eb82002-10-25 18:09:24 +0000583
Fred Drakec6f28912002-10-25 19:40:49 +0000584 def test_boolean(self):
585 cf = self.fromstring(
586 "[BOOLTEST]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000587 "T1{equals}1\n"
588 "T2{equals}TRUE\n"
589 "T3{equals}True\n"
590 "T4{equals}oN\n"
591 "T5{equals}yes\n"
592 "F1{equals}0\n"
593 "F2{equals}FALSE\n"
594 "F3{equals}False\n"
595 "F4{equals}oFF\n"
596 "F5{equals}nO\n"
597 "E1{equals}2\n"
598 "E2{equals}foo\n"
599 "E3{equals}-1\n"
600 "E4{equals}0.1\n"
601 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
Fred Drakec6f28912002-10-25 19:40:49 +0000602 )
603 for x in range(1, 5):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000604 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
605 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
Fred Drakec6f28912002-10-25 19:40:49 +0000606 self.assertRaises(ValueError,
607 cf.getboolean, 'BOOLTEST', 'e%d' % x)
Fred Drake95b96d32001-02-12 17:23:20 +0000608
Fred Drakec6f28912002-10-25 19:40:49 +0000609 def test_weird_errors(self):
610 cf = self.newconfig()
Fred Drake8ef67672000-09-27 22:45:25 +0000611 cf.add_section("Foo")
Michael Foordbd6c0792010-07-25 23:09:25 +0000612 with self.assertRaises(configparser.DuplicateSectionError) as cm:
613 cf.add_section("Foo")
Fred Drakea4923622010-08-09 12:52:45 +0000614 e = cm.exception
615 self.assertEqual(str(e), "Section 'Foo' already exists")
616 self.assertEqual(e.args, ("Foo", None, None))
617
618 if self.strict:
619 with self.assertRaises(configparser.DuplicateSectionError) as cm:
620 cf.read_string(textwrap.dedent("""\
621 [Foo]
622 will this be added{equals}True
623 [Bar]
624 what about this{equals}True
625 [Foo]
626 oops{equals}this won't
627 """.format(equals=self.delimiters[0])), source='<foo-bar>')
628 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200629 self.assertEqual(str(e), "While reading from '<foo-bar>' "
630 "[line 5]: section 'Foo' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000631 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
632
633 with self.assertRaises(configparser.DuplicateOptionError) as cm:
634 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
635 e = cm.exception
Łukasz Langaf9b4eb42013-06-23 19:10:25 +0200636 self.assertEqual(str(e), "While reading from '<dict>': option "
637 "'opt' in section 'Bar' already exists")
Fred Drakea4923622010-08-09 12:52:45 +0000638 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
Fred Drakec6f28912002-10-25 19:40:49 +0000639
640 def test_write(self):
Fred Drake03c44a32010-02-19 06:08:41 +0000641 config_string = (
Fred Drakec6f28912002-10-25 19:40:49 +0000642 "[Long Line]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000643 "foo{0[0]} this line is much, much longer than my editor\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000644 " likes it.\n"
Łukasz Langac264c092010-11-20 16:15:37 +0000645 "[{default_section}]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000646 "foo{0[1]} another very\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000647 " long line\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000648 "[Long Line - With Comments!]\n"
649 "test {0[1]} we {comment} can\n"
650 " also {comment} place\n"
651 " comments {comment} in\n"
652 " multiline {comment} values"
Łukasz Langac264c092010-11-20 16:15:37 +0000653 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
654 default_section=self.default_section)
Fred Drakec6f28912002-10-25 19:40:49 +0000655 )
Fred Drake03c44a32010-02-19 06:08:41 +0000656 if self.allow_no_value:
657 config_string += (
658 "[Valueless]\n"
659 "option-without-value\n"
660 )
661
662 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000663 for space_around_delimiters in (True, False):
664 output = io.StringIO()
665 cf.write(output, space_around_delimiters=space_around_delimiters)
666 delimiter = self.delimiters[0]
667 if space_around_delimiters:
668 delimiter = " {} ".format(delimiter)
669 expect_string = (
670 "[{default_section}]\n"
671 "foo{equals}another very\n"
672 "\tlong line\n"
Fred Drake03c44a32010-02-19 06:08:41 +0000673 "\n"
Łukasz Langa71b37a52010-12-17 21:56:32 +0000674 "[Long Line]\n"
675 "foo{equals}this line is much, much longer than my editor\n"
676 "\tlikes it.\n"
677 "\n"
678 "[Long Line - With Comments!]\n"
679 "test{equals}we\n"
680 "\talso\n"
681 "\tcomments\n"
682 "\tmultiline\n"
683 "\n".format(equals=delimiter,
684 default_section=self.default_section)
Fred Drake03c44a32010-02-19 06:08:41 +0000685 )
Łukasz Langa71b37a52010-12-17 21:56:32 +0000686 if self.allow_no_value:
687 expect_string += (
688 "[Valueless]\n"
689 "option-without-value\n"
690 "\n"
691 )
692 self.assertEqual(output.getvalue(), expect_string)
Fred Drakec6f28912002-10-25 19:40:49 +0000693
Fred Drakeabc086f2004-05-18 03:29:52 +0000694 def test_set_string_types(self):
695 cf = self.fromstring("[sect]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000696 "option1{eq}foo\n".format(eq=self.delimiters[0]))
Fred Drakeabc086f2004-05-18 03:29:52 +0000697 # Check that we don't get an exception when setting values in
698 # an existing section using strings:
699 class mystr(str):
700 pass
701 cf.set("sect", "option1", "splat")
702 cf.set("sect", "option1", mystr("splat"))
703 cf.set("sect", "option2", "splat")
704 cf.set("sect", "option2", mystr("splat"))
Walter Dörwald5de48bd2007-06-11 21:38:39 +0000705 cf.set("sect", "option1", "splat")
706 cf.set("sect", "option2", "splat")
Fred Drakeabc086f2004-05-18 03:29:52 +0000707
Fred Drake82903142004-05-18 04:24:02 +0000708 def test_read_returns_file_list(self):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000709 if self.delimiters[0] != '=':
Zachary Ware9fe6d862013-12-08 00:20:35 -0600710 self.skipTest('incompatible format')
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000711 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000712 # check when we pass a mix of readable and non-readable files:
713 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000714 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000715 self.assertEqual(parsed_files, [file1])
716 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
717 # check when we pass only a filename:
718 cf = self.newconfig()
719 parsed_files = cf.read(file1)
720 self.assertEqual(parsed_files, [file1])
721 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
722 # check when we pass only missing files:
723 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000724 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000725 self.assertEqual(parsed_files, [])
726 # check when we pass no files:
727 cf = self.newconfig()
728 parsed_files = cf.read([])
729 self.assertEqual(parsed_files, [])
730
Fred Drakec6f28912002-10-25 19:40:49 +0000731 # shared by subclasses
732 def get_interpolation_config(self):
733 return self.fromstring(
734 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000735 "bar{equals}something %(with1)s interpolation (1 step)\n"
736 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
737 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
738 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
739 "with11{equals}%(with10)s\n"
740 "with10{equals}%(with9)s\n"
741 "with9{equals}%(with8)s\n"
742 "with8{equals}%(With7)s\n"
743 "with7{equals}%(WITH6)s\n"
744 "with6{equals}%(with5)s\n"
745 "With5{equals}%(with4)s\n"
746 "WITH4{equals}%(with3)s\n"
747 "with3{equals}%(with2)s\n"
748 "with2{equals}%(with1)s\n"
749 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000750 "\n"
751 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000752 "foo{equals}%(bar)s\n"
753 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000754 "\n"
755 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000756 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000757 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000758
Fred Drake98e3b292002-10-25 20:42:44 +0000759 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000760 cf = self.fromstring("""
761 [section]
762 name {0[0]} %(value)s
763 key{0[1]} |%(name)s|
764 getdefault{0[1]} |%(default)s|
765 """.format(self.delimiters), defaults={"default": "<default>"})
766 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000767 L.sort()
768 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000769 with self.assertRaises(configparser.NoSectionError):
770 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000771
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100772 def test_popitem(self):
773 cf = self.fromstring("""
774 [section1]
775 name1 {0[0]} value1
776 [section2]
777 name2 {0[0]} value2
778 [section3]
779 name3 {0[0]} value3
780 """.format(self.delimiters), defaults={"default": "<default>"})
781 self.assertEqual(cf.popitem()[0], 'section1')
782 self.assertEqual(cf.popitem()[0], 'section2')
783 self.assertEqual(cf.popitem()[0], 'section3')
784 with self.assertRaises(KeyError):
785 cf.popitem()
786
787 def test_clear(self):
788 cf = self.newconfig({"foo": "Bar"})
789 self.assertEqual(
790 cf.get(self.default_section, "Foo"), "Bar",
791 "could not locate option, expecting case-insensitive option names")
792 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
793 self.assertEqual(cf.sections(), ['zing'])
794 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
795 cf.clear()
796 self.assertEqual(set(cf.sections()), set())
797 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
798
Łukasz Langa02101942012-12-31 13:55:11 +0100799 def test_setitem(self):
800 cf = self.fromstring("""
801 [section1]
802 name1 {0[0]} value1
803 [section2]
804 name2 {0[0]} value2
805 [section3]
806 name3 {0[0]} value3
807 """.format(self.delimiters), defaults={"nameD": "valueD"})
808 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
809 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
810 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
811 self.assertEqual(cf['section1']['name1'], 'value1')
812 self.assertEqual(cf['section2']['name2'], 'value2')
813 self.assertEqual(cf['section3']['name3'], 'value3')
Łukasz Langaa821f822013-01-01 22:33:19 +0100814 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100815 cf['section2'] = {'name22': 'value22'}
816 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
817 self.assertEqual(cf['section2']['name22'], 'value22')
818 self.assertNotIn('name2', cf['section2'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100819 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100820 cf['section3'] = {}
821 self.assertEqual(set(cf['section3'].keys()), {'named'})
822 self.assertNotIn('name3', cf['section3'])
Łukasz Langaa821f822013-01-01 22:33:19 +0100823 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100824 cf[self.default_section] = {}
825 self.assertEqual(set(cf[self.default_section].keys()), set())
826 self.assertEqual(set(cf['section1'].keys()), {'name1'})
827 self.assertEqual(set(cf['section2'].keys()), {'name22'})
828 self.assertEqual(set(cf['section3'].keys()), set())
Łukasz Langaa821f822013-01-01 22:33:19 +0100829 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
Łukasz Langa02101942012-12-31 13:55:11 +0100830
Fred Drake8ef67672000-09-27 22:45:25 +0000831
Ezio Melottidc1fa802013-01-11 06:30:57 +0200832class StrictTestCase(BasicTestCase, unittest.TestCase):
Fred Drakea4923622010-08-09 12:52:45 +0000833 config_class = configparser.RawConfigParser
834 strict = True
835
836
Ezio Melottidc1fa802013-01-11 06:30:57 +0200837class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000838 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000839
840 def test_interpolation(self):
841 cf = self.get_interpolation_config()
842 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000843 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
844 eq(cf.get("Foo", "bar9"),
845 "something with lots of interpolation (9 steps)")
846 eq(cf.get("Foo", "bar10"),
847 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000848 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000849 if self.interpolation == configparser._UNSET:
850 self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
851 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
852 self.assertEqual(e.args, ("bar11", "Foo",
853 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000854
Fred Drake54782192002-12-31 06:57:25 +0000855 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000856 cf = self.get_interpolation_config()
857 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000858 "Interpolation Error", "name")
859 self.assertEqual(e.reference, "reference")
860 self.assertEqual(e.section, "Interpolation Error")
861 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000862 if self.interpolation == configparser._UNSET:
863 self.assertEqual(e.args, ('name', 'Interpolation Error',
864 '', 'reference'))
865 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
866 self.assertEqual(e.args, ('name', 'Interpolation Error',
867 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000868
Fred Drake98e3b292002-10-25 20:42:44 +0000869 def test_items(self):
870 self.check_items_config([('default', '<default>'),
871 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000872 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000873 ('name', 'value'),
874 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000875
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000876 def test_safe_interpolation(self):
877 # See http://www.python.org/sf/511737
878 cf = self.fromstring("[section]\n"
879 "option1{eq}xxx\n"
880 "option2{eq}%(option1)s/xxx\n"
881 "ok{eq}%(option1)s/%%s\n"
882 "not_ok{eq}%(option2)s/%%s".format(
883 eq=self.delimiters[0]))
884 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
885 if self.interpolation == configparser._UNSET:
886 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
887 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
888 with self.assertRaises(TypeError):
889 cf.get("section", "not_ok")
890
891 def test_set_malformatted_interpolation(self):
892 cf = self.fromstring("[sect]\n"
893 "option1{eq}foo\n".format(eq=self.delimiters[0]))
894
895 self.assertEqual(cf.get('sect', "option1"), "foo")
896
897 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
898 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
899 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
900
901 self.assertEqual(cf.get('sect', "option1"), "foo")
902
903 # bug #5741: double percents are *not* malformed
904 cf.set("sect", "option2", "foo%%bar")
905 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
906
David Goodger1cbf2062004-10-03 15:55:09 +0000907 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000908 cf = self.fromstring("[sect]\n"
909 "option1{eq}foo\n".format(eq=self.delimiters[0]))
910 # Check that we get a TypeError when setting non-string values
911 # in an existing section:
912 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
913 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
914 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
915 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
916 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
917 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
918 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
919 self.assertRaises(TypeError, cf.add_section, 123)
920
921 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000922 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000923 self.assertRaises(ValueError, cf.add_section, self.default_section)
924
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200925
Ezio Melottidc1fa802013-01-11 06:30:57 +0200926class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200927 config_class = configparser.ConfigParser
928 interpolation = None
929 ini = textwrap.dedent("""
930 [numbers]
931 one = 1
932 two = %(one)s * 2
933 three = ${common:one} * 3
934
935 [hexen]
936 sixteen = ${numbers:two} * 8
937 """).strip()
938
939 def assertMatchesIni(self, cf):
940 self.assertEqual(cf['numbers']['one'], '1')
941 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
942 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
943 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
944
945 def test_no_interpolation(self):
946 cf = self.fromstring(self.ini)
947 self.assertMatchesIni(cf)
948
949 def test_empty_case(self):
950 cf = self.newconfig()
951 self.assertIsNone(cf.read_string(""))
952
953 def test_none_as_default_interpolation(self):
954 class CustomConfigParser(configparser.ConfigParser):
955 _DEFAULT_INTERPOLATION = None
956
957 cf = CustomConfigParser()
958 cf.read_string(self.ini)
959 self.assertMatchesIni(cf)
960
961
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000962class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
963 config_class = configparser.ConfigParser
964 interpolation = configparser.LegacyInterpolation()
965
966 def test_set_malformatted_interpolation(self):
967 cf = self.fromstring("[sect]\n"
968 "option1{eq}foo\n".format(eq=self.delimiters[0]))
969
970 self.assertEqual(cf.get('sect', "option1"), "foo")
971
972 cf.set("sect", "option1", "%foo")
973 self.assertEqual(cf.get('sect', "option1"), "%foo")
974 cf.set("sect", "option1", "foo%")
975 self.assertEqual(cf.get('sect', "option1"), "foo%")
976 cf.set("sect", "option1", "f%oo")
977 self.assertEqual(cf.get('sect', "option1"), "f%oo")
978
979 # bug #5741: double percents are *not* malformed
980 cf.set("sect", "option2", "foo%%bar")
981 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000982
Georg Brandl96a60ae2010-07-28 13:13:46 +0000983class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
984 delimiters = (':=', '$')
985 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000986 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000987
Łukasz Langac264c092010-11-20 16:15:37 +0000988class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
989 default_section = 'general'
990
Ezio Melottidc1fa802013-01-11 06:30:57 +0200991class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000992 config_class = configparser.ConfigParser
993 wonderful_spam = ("I'm having spam spam spam spam "
994 "spam spam spam beaked beans spam "
995 "spam spam and spam!").replace(' ', '\t\n')
996
997 def setUp(self):
998 cf = self.newconfig()
999 for i in range(100):
1000 s = 'section{}'.format(i)
1001 cf.add_section(s)
1002 for j in range(10):
1003 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1004 with open(support.TESTFN, 'w') as f:
1005 cf.write(f)
1006
1007 def tearDown(self):
1008 os.unlink(support.TESTFN)
1009
1010 def test_dominating_multiline_values(self):
1011 # We're reading from file because this is where the code changed
1012 # during performance updates in Python 3.2
1013 cf_from_file = self.newconfig()
1014 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001015 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001016 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1017 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001018
Ezio Melottidc1fa802013-01-11 06:30:57 +02001019class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001020 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001021
1022 def test_interpolation(self):
1023 cf = self.get_interpolation_config()
1024 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001025 eq(cf.get("Foo", "bar"),
1026 "something %(with1)s interpolation (1 step)")
1027 eq(cf.get("Foo", "bar9"),
1028 "something %(with9)s lots of interpolation (9 steps)")
1029 eq(cf.get("Foo", "bar10"),
1030 "something %(with10)s lots of interpolation (10 steps)")
1031 eq(cf.get("Foo", "bar11"),
1032 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001033
Fred Drake98e3b292002-10-25 20:42:44 +00001034 def test_items(self):
1035 self.check_items_config([('default', '<default>'),
1036 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001037 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001038 ('name', '%(value)s'),
1039 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001040
David Goodger1cbf2062004-10-03 15:55:09 +00001041 def test_set_nonstring_types(self):
1042 cf = self.newconfig()
1043 cf.add_section('non-string')
1044 cf.set('non-string', 'int', 1)
1045 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1046 cf.set('non-string', 'dict', {'pi': 3.14159})
1047 self.assertEqual(cf.get('non-string', 'int'), 1)
1048 self.assertEqual(cf.get('non-string', 'list'),
1049 [0, 1, 1, 2, 3, 5, 8, 13])
1050 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001051 cf.add_section(123)
1052 cf.set(123, 'this is sick', True)
1053 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001054 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001055 # would not work for SortedDict; only checking for the most common
1056 # default dictionary (OrderedDict)
1057 cf.optionxform = lambda x: x
1058 cf.set('non-string', 1, 1)
1059 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001060
Georg Brandl96a60ae2010-07-28 13:13:46 +00001061class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1062 delimiters = (':=', '$')
1063 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001064 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001065
Ezio Melottidc1fa802013-01-11 06:30:57 +02001066class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001067 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001068 comment_prefixes = ('#', ';', '----')
1069 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001070 empty_lines_in_values = False
1071
1072 def test_reading(self):
1073 smbconf = support.findfile("cfgparser.2")
1074 # check when we pass a mix of readable and non-readable files:
1075 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001076 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001077 self.assertEqual(parsed_files, [smbconf])
1078 sections = ['global', 'homes', 'printers',
1079 'print$', 'pdf-generator', 'tmp', 'Agustin']
1080 self.assertEqual(cf.sections(), sections)
1081 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1082 self.assertEqual(cf.getint("global", "max log size"), 50)
1083 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1084 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001085
Ezio Melottidc1fa802013-01-11 06:30:57 +02001086class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001087 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001088 interpolation = configparser.ExtendedInterpolation()
1089 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001090 strict = True
1091
1092 def fromstring(self, string, defaults=None, optionxform=None):
1093 cf = self.newconfig(defaults)
1094 if optionxform:
1095 cf.optionxform = optionxform
1096 cf.read_string(string)
1097 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001098
1099 def test_extended_interpolation(self):
1100 cf = self.fromstring(textwrap.dedent("""
1101 [common]
1102 favourite Beatle = Paul
1103 favourite color = green
1104
1105 [tom]
1106 favourite band = ${favourite color} day
1107 favourite pope = John ${favourite Beatle} II
1108 sequel = ${favourite pope}I
1109
1110 [ambv]
1111 favourite Beatle = George
1112 son of Edward VII = ${favourite Beatle} V
1113 son of George V = ${son of Edward VII}I
1114
1115 [stanley]
1116 favourite Beatle = ${ambv:favourite Beatle}
1117 favourite pope = ${tom:favourite pope}
1118 favourite color = black
1119 favourite state of mind = paranoid
1120 favourite movie = soylent ${common:favourite color}
1121 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1122 """).strip())
1123
1124 eq = self.assertEqual
1125 eq(cf['common']['favourite Beatle'], 'Paul')
1126 eq(cf['common']['favourite color'], 'green')
1127 eq(cf['tom']['favourite Beatle'], 'Paul')
1128 eq(cf['tom']['favourite color'], 'green')
1129 eq(cf['tom']['favourite band'], 'green day')
1130 eq(cf['tom']['favourite pope'], 'John Paul II')
1131 eq(cf['tom']['sequel'], 'John Paul III')
1132 eq(cf['ambv']['favourite Beatle'], 'George')
1133 eq(cf['ambv']['favourite color'], 'green')
1134 eq(cf['ambv']['son of Edward VII'], 'George V')
1135 eq(cf['ambv']['son of George V'], 'George VI')
1136 eq(cf['stanley']['favourite Beatle'], 'George')
1137 eq(cf['stanley']['favourite color'], 'black')
1138 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1139 eq(cf['stanley']['favourite movie'], 'soylent green')
1140 eq(cf['stanley']['favourite pope'], 'John Paul II')
1141 eq(cf['stanley']['favourite song'],
1142 'black sabbath - paranoid')
1143
1144 def test_endless_loop(self):
1145 cf = self.fromstring(textwrap.dedent("""
1146 [one for you]
1147 ping = ${one for me:pong}
1148
1149 [one for me]
1150 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001151
1152 [selfish]
1153 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001154 """).strip())
1155
1156 with self.assertRaises(configparser.InterpolationDepthError):
1157 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001158 with self.assertRaises(configparser.InterpolationDepthError):
1159 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001160
Łukasz Langa71b37a52010-12-17 21:56:32 +00001161 def test_strange_options(self):
1162 cf = self.fromstring("""
1163 [dollars]
1164 $var = $$value
1165 $var2 = ${$var}
1166 ${sick} = cannot interpolate me
1167
1168 [interpolated]
1169 $other = ${dollars:$var}
1170 $trying = ${dollars:${sick}}
1171 """)
1172
1173 self.assertEqual(cf['dollars']['$var'], '$value')
1174 self.assertEqual(cf['interpolated']['$other'], '$value')
1175 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1176 exception_class = configparser.InterpolationMissingOptionError
1177 with self.assertRaises(exception_class) as cm:
1178 cf['interpolated']['$trying']
1179 self.assertEqual(cm.exception.reference, 'dollars:${sick')
1180 self.assertEqual(cm.exception.args[2], '}') #rawval
1181
Łukasz Langae698cd52011-04-28 10:58:57 +02001182 def test_case_sensitivity_basic(self):
1183 ini = textwrap.dedent("""
1184 [common]
1185 optionlower = value
1186 OptionUpper = Value
1187
1188 [Common]
1189 optionlower = a better ${common:optionlower}
1190 OptionUpper = A Better ${common:OptionUpper}
1191
1192 [random]
1193 foolower = ${common:optionlower} redefined
1194 FooUpper = ${Common:OptionUpper} Redefined
1195 """).strip()
1196
1197 cf = self.fromstring(ini)
1198 eq = self.assertEqual
1199 eq(cf['common']['optionlower'], 'value')
1200 eq(cf['common']['OptionUpper'], 'Value')
1201 eq(cf['Common']['optionlower'], 'a better value')
1202 eq(cf['Common']['OptionUpper'], 'A Better Value')
1203 eq(cf['random']['foolower'], 'value redefined')
1204 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1205
1206 def test_case_sensitivity_conflicts(self):
1207 ini = textwrap.dedent("""
1208 [common]
1209 option = value
1210 Option = Value
1211
1212 [Common]
1213 option = a better ${common:option}
1214 Option = A Better ${common:Option}
1215
1216 [random]
1217 foo = ${common:option} redefined
1218 Foo = ${Common:Option} Redefined
1219 """).strip()
1220 with self.assertRaises(configparser.DuplicateOptionError):
1221 cf = self.fromstring(ini)
1222
1223 # raw options
1224 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1225 eq = self.assertEqual
1226 eq(cf['common']['option'], 'value')
1227 eq(cf['common']['Option'], 'Value')
1228 eq(cf['Common']['option'], 'a better value')
1229 eq(cf['Common']['Option'], 'A Better Value')
1230 eq(cf['random']['foo'], 'value redefined')
1231 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001232
1233 def test_other_errors(self):
1234 cf = self.fromstring("""
1235 [interpolation fail]
1236 case1 = ${where's the brace
1237 case2 = ${does_not_exist}
1238 case3 = ${wrong_section:wrong_value}
1239 case4 = ${i:like:colon:characters}
1240 case5 = $100 for Fail No 5!
1241 """)
1242
1243 with self.assertRaises(configparser.InterpolationSyntaxError):
1244 cf['interpolation fail']['case1']
1245 with self.assertRaises(configparser.InterpolationMissingOptionError):
1246 cf['interpolation fail']['case2']
1247 with self.assertRaises(configparser.InterpolationMissingOptionError):
1248 cf['interpolation fail']['case3']
1249 with self.assertRaises(configparser.InterpolationSyntaxError):
1250 cf['interpolation fail']['case4']
1251 with self.assertRaises(configparser.InterpolationSyntaxError):
1252 cf['interpolation fail']['case5']
1253 with self.assertRaises(ValueError):
1254 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001255
1256
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001257class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001258 allow_no_value = True
1259
Ezio Melottidc1fa802013-01-11 06:30:57 +02001260class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001261 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001262 delimiters = {'='}
1263 comment_prefixes = {'#'}
1264 allow_no_value = True
1265
1266 def test_cfgparser_dot_3(self):
1267 tricky = support.findfile("cfgparser.3")
1268 cf = self.newconfig()
1269 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1270 self.assertEqual(cf.sections(), ['strange',
1271 'corruption',
1272 'yeah, sections can be '
1273 'indented as well',
1274 'another one!',
1275 'no values here',
1276 'tricky interpolation',
1277 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001278 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001279 vars={'interpolate': '-1'}), -1)
1280 with self.assertRaises(ValueError):
1281 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001282 cf.getint(self.default_section, 'go', raw=True,
1283 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001284 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1285 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1286 longname = 'yeah, sections can be indented as well'
1287 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001288 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001289 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1290 # `go` from DEFAULT
1291 with self.assertRaises(configparser.InterpolationMissingOptionError):
1292 cf.items('no values here')
1293 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1294 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1295 cf.get('tricky interpolation', 'go'))
1296 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1297
1298 def test_unicode_failure(self):
1299 tricky = support.findfile("cfgparser.3")
1300 cf = self.newconfig()
1301 with self.assertRaises(UnicodeDecodeError):
1302 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001303
Fred Drake88444412010-09-03 04:22:36 +00001304
1305class Issue7005TestCase(unittest.TestCase):
1306 """Test output when None is set() as a value and allow_no_value == False.
1307
1308 http://bugs.python.org/issue7005
1309
1310 """
1311
1312 expected_output = "[section]\noption = None\n\n"
1313
1314 def prepare(self, config_class):
1315 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001316 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001317 cp.add_section("section")
1318 cp.set("section", "option", None)
1319 sio = io.StringIO()
1320 cp.write(sio)
1321 return sio.getvalue()
1322
1323 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001324 cp = configparser.ConfigParser(allow_no_value=False)
1325 cp.add_section("section")
1326 with self.assertRaises(TypeError):
1327 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001328
1329 def test_none_as_value_stringified_raw(self):
1330 output = self.prepare(configparser.RawConfigParser)
1331 self.assertEqual(output, self.expected_output)
1332
1333
Thomas Wouters89f507f2006-12-13 04:49:30 +00001334class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001335 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001336
1337 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001338 cf = self.fromstring("[b]\n"
1339 "o4=1\n"
1340 "o3=2\n"
1341 "o2=3\n"
1342 "o1=4\n"
1343 "[a]\n"
1344 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001345 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001346 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001347 self.assertEqual(output.getvalue(),
1348 "[a]\n"
1349 "k = v\n\n"
1350 "[b]\n"
1351 "o1 = 4\n"
1352 "o2 = 3\n"
1353 "o3 = 2\n"
1354 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001355
Fred Drake03c44a32010-02-19 06:08:41 +00001356
Ezio Melottidc1fa802013-01-11 06:30:57 +02001357class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001358 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001359 comment_prefixes = '#;'
1360 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001361
1362 def test_comment_handling(self):
1363 config_string = textwrap.dedent("""\
1364 [Commented Bar]
1365 baz=qwe ; a comment
1366 foo: bar # not a comment!
1367 # but this is a comment
1368 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001369 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001370 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001371 """)
1372 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001373 self.assertEqual(cf.get('Commented Bar', 'foo'),
1374 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001375 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001376 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1377 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001378
Ezio Melottidc1fa802013-01-11 06:30:57 +02001379class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001380 config_class = configparser.ConfigParser
1381
1382 def fromstring(self, string, defaults=None):
1383 cf = self.newconfig(defaults)
1384 cf.read_string(string)
1385 cf_copy = self.newconfig()
1386 cf_copy.read_dict(cf)
1387 # we have to clean up option duplicates that appeared because of
1388 # the magic DEFAULTSECT behaviour.
1389 for section in cf_copy.values():
1390 if section.name == self.default_section:
1391 continue
1392 for default, value in cf[self.default_section].items():
1393 if section[default] == value:
1394 del section[default]
1395 return cf_copy
1396
Łukasz Langadaab1c82011-04-27 18:10:05 +02001397
1398class FakeFile:
1399 def __init__(self):
1400 file_path = support.findfile("cfgparser.1")
1401 with open(file_path) as f:
1402 self.lines = f.readlines()
1403 self.lines.reverse()
1404
1405 def readline(self):
1406 if len(self.lines):
1407 return self.lines.pop()
1408 return ''
1409
1410
1411def readline_generator(f):
1412 """As advised in Doc/library/configparser.rst."""
1413 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001414 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001415 yield line
1416 line = f.readline()
1417
1418
1419class ReadFileTestCase(unittest.TestCase):
1420 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001421 file_paths = [support.findfile("cfgparser.1")]
1422 try:
1423 file_paths.append(file_paths[0].encode('utf8'))
1424 except UnicodeEncodeError:
1425 pass # unfortunately we can't test bytes on this path
1426 for file_path in file_paths:
1427 parser = configparser.ConfigParser()
1428 with open(file_path) as f:
1429 parser.read_file(f)
1430 self.assertIn("Foo Bar", parser)
1431 self.assertIn("foo", parser["Foo Bar"])
1432 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001433
1434 def test_iterable(self):
1435 lines = textwrap.dedent("""
1436 [Foo Bar]
1437 foo=newbar""").strip().split('\n')
1438 parser = configparser.ConfigParser()
1439 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001440 self.assertIn("Foo Bar", parser)
1441 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001442 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1443
1444 def test_readline_generator(self):
1445 """Issue #11670."""
1446 parser = configparser.ConfigParser()
1447 with self.assertRaises(TypeError):
1448 parser.read_file(FakeFile())
1449 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001450 self.assertIn("Foo Bar", parser)
1451 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001452 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1453
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001454 def test_source_as_bytes(self):
1455 """Issue #18260."""
1456 lines = textwrap.dedent("""
1457 [badbad]
1458 [badbad]""").strip().split('\n')
1459 parser = configparser.ConfigParser()
1460 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1461 parser.read_file(lines, source=b"badbad")
1462 self.assertEqual(
1463 str(dse.exception),
1464 "While reading from b'badbad' [line 2]: section 'badbad' "
1465 "already exists"
1466 )
1467 lines = textwrap.dedent("""
1468 [badbad]
1469 bad = bad
1470 bad = bad""").strip().split('\n')
1471 parser = configparser.ConfigParser()
1472 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1473 parser.read_file(lines, source=b"badbad")
1474 self.assertEqual(
1475 str(dse.exception),
1476 "While reading from b'badbad' [line 3]: option 'bad' in section "
1477 "'badbad' already exists"
1478 )
1479 lines = textwrap.dedent("""
1480 [badbad]
1481 = bad""").strip().split('\n')
1482 parser = configparser.ConfigParser()
1483 with self.assertRaises(configparser.ParsingError) as dse:
1484 parser.read_file(lines, source=b"badbad")
1485 self.assertEqual(
1486 str(dse.exception),
1487 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1488 )
1489 lines = textwrap.dedent("""
1490 [badbad
1491 bad = bad""").strip().split('\n')
1492 parser = configparser.ConfigParser()
1493 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1494 parser.read_file(lines, source=b"badbad")
1495 self.assertEqual(
1496 str(dse.exception),
1497 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1498 "'[badbad'"
1499 )
1500
Łukasz Langadaab1c82011-04-27 18:10:05 +02001501
Łukasz Langa71b37a52010-12-17 21:56:32 +00001502class CoverageOneHundredTestCase(unittest.TestCase):
1503 """Covers edge cases in the codebase."""
1504
1505 def test_duplicate_option_error(self):
1506 error = configparser.DuplicateOptionError('section', 'option')
1507 self.assertEqual(error.section, 'section')
1508 self.assertEqual(error.option, 'option')
1509 self.assertEqual(error.source, None)
1510 self.assertEqual(error.lineno, None)
1511 self.assertEqual(error.args, ('section', 'option', None, None))
1512 self.assertEqual(str(error), "Option 'option' in section 'section' "
1513 "already exists")
1514
1515 def test_interpolation_depth_error(self):
1516 error = configparser.InterpolationDepthError('option', 'section',
1517 'rawval')
1518 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1519 self.assertEqual(error.option, 'option')
1520 self.assertEqual(error.section, 'section')
1521
1522 def test_parsing_error(self):
1523 with self.assertRaises(ValueError) as cm:
1524 configparser.ParsingError()
1525 self.assertEqual(str(cm.exception), "Required argument `source' not "
1526 "given.")
1527 with self.assertRaises(ValueError) as cm:
1528 configparser.ParsingError(source='source', filename='filename')
1529 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1530 "and `source'. Use `source'.")
1531 error = configparser.ParsingError(filename='source')
1532 self.assertEqual(error.source, 'source')
1533 with warnings.catch_warnings(record=True) as w:
1534 warnings.simplefilter("always", DeprecationWarning)
1535 self.assertEqual(error.filename, 'source')
1536 error.filename = 'filename'
1537 self.assertEqual(error.source, 'filename')
1538 for warning in w:
1539 self.assertTrue(warning.category is DeprecationWarning)
1540
1541 def test_interpolation_validation(self):
1542 parser = configparser.ConfigParser()
1543 parser.read_string("""
1544 [section]
1545 invalid_percent = %
1546 invalid_reference = %(()
1547 invalid_variable = %(does_not_exist)s
1548 """)
1549 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1550 parser['section']['invalid_percent']
1551 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1552 "'(', found: '%'")
1553 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1554 parser['section']['invalid_reference']
1555 self.assertEqual(str(cm.exception), "bad interpolation variable "
1556 "reference '%(()'")
1557
1558 def test_readfp_deprecation(self):
1559 sio = io.StringIO("""
1560 [section]
1561 option = value
1562 """)
1563 parser = configparser.ConfigParser()
1564 with warnings.catch_warnings(record=True) as w:
1565 warnings.simplefilter("always", DeprecationWarning)
1566 parser.readfp(sio, filename='StringIO')
1567 for warning in w:
1568 self.assertTrue(warning.category is DeprecationWarning)
1569 self.assertEqual(len(parser), 2)
1570 self.assertEqual(parser['section']['option'], 'value')
1571
1572 def test_safeconfigparser_deprecation(self):
1573 with warnings.catch_warnings(record=True) as w:
1574 warnings.simplefilter("always", DeprecationWarning)
1575 parser = configparser.SafeConfigParser()
1576 for warning in w:
1577 self.assertTrue(warning.category is DeprecationWarning)
1578
1579 def test_sectionproxy_repr(self):
1580 parser = configparser.ConfigParser()
1581 parser.read_string("""
1582 [section]
1583 key = value
1584 """)
1585 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001586
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001587 def test_inconsistent_converters_state(self):
1588 parser = configparser.ConfigParser()
1589 import decimal
1590 parser.converters['decimal'] = decimal.Decimal
1591 parser.read_string("""
1592 [s1]
1593 one = 1
1594 [s2]
1595 two = 2
1596 """)
1597 self.assertIn('decimal', parser.converters)
1598 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1599 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1600 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1601 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1602 del parser.getdecimal
1603 with self.assertRaises(AttributeError):
1604 parser.getdecimal('s1', 'one')
1605 self.assertIn('decimal', parser.converters)
1606 del parser.converters['decimal']
1607 self.assertNotIn('decimal', parser.converters)
1608 with self.assertRaises(AttributeError):
1609 parser.getdecimal('s1', 'one')
1610 with self.assertRaises(AttributeError):
1611 parser['s1'].getdecimal('one')
1612 with self.assertRaises(AttributeError):
1613 parser['s2'].getdecimal('two')
1614
Łukasz Langae7851952012-01-20 14:57:55 +01001615
1616class ExceptionPicklingTestCase(unittest.TestCase):
1617 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1618
1619 def test_error(self):
1620 import pickle
1621 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001622 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1623 pickled = pickle.dumps(e1, proto)
1624 e2 = pickle.loads(pickled)
1625 self.assertEqual(e1.message, e2.message)
1626 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001627
1628 def test_nosectionerror(self):
1629 import pickle
1630 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001631 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1632 pickled = pickle.dumps(e1, proto)
1633 e2 = pickle.loads(pickled)
1634 self.assertEqual(e1.message, e2.message)
1635 self.assertEqual(e1.args, e2.args)
1636 self.assertEqual(e1.section, e2.section)
1637 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001638
1639 def test_nooptionerror(self):
1640 import pickle
1641 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001642 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1643 pickled = pickle.dumps(e1, proto)
1644 e2 = pickle.loads(pickled)
1645 self.assertEqual(e1.message, e2.message)
1646 self.assertEqual(e1.args, e2.args)
1647 self.assertEqual(e1.section, e2.section)
1648 self.assertEqual(e1.option, e2.option)
1649 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001650
1651 def test_duplicatesectionerror(self):
1652 import pickle
1653 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001654 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1655 pickled = pickle.dumps(e1, proto)
1656 e2 = pickle.loads(pickled)
1657 self.assertEqual(e1.message, e2.message)
1658 self.assertEqual(e1.args, e2.args)
1659 self.assertEqual(e1.section, e2.section)
1660 self.assertEqual(e1.source, e2.source)
1661 self.assertEqual(e1.lineno, e2.lineno)
1662 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001663
1664 def test_duplicateoptionerror(self):
1665 import pickle
1666 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1667 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001668 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1669 pickled = pickle.dumps(e1, proto)
1670 e2 = pickle.loads(pickled)
1671 self.assertEqual(e1.message, e2.message)
1672 self.assertEqual(e1.args, e2.args)
1673 self.assertEqual(e1.section, e2.section)
1674 self.assertEqual(e1.option, e2.option)
1675 self.assertEqual(e1.source, e2.source)
1676 self.assertEqual(e1.lineno, e2.lineno)
1677 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001678
1679 def test_interpolationerror(self):
1680 import pickle
1681 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001682 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1683 pickled = pickle.dumps(e1, proto)
1684 e2 = pickle.loads(pickled)
1685 self.assertEqual(e1.message, e2.message)
1686 self.assertEqual(e1.args, e2.args)
1687 self.assertEqual(e1.section, e2.section)
1688 self.assertEqual(e1.option, e2.option)
1689 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001690
1691 def test_interpolationmissingoptionerror(self):
1692 import pickle
1693 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1694 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001695 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1696 pickled = pickle.dumps(e1, proto)
1697 e2 = pickle.loads(pickled)
1698 self.assertEqual(e1.message, e2.message)
1699 self.assertEqual(e1.args, e2.args)
1700 self.assertEqual(e1.section, e2.section)
1701 self.assertEqual(e1.option, e2.option)
1702 self.assertEqual(e1.reference, e2.reference)
1703 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001704
1705 def test_interpolationsyntaxerror(self):
1706 import pickle
1707 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001708 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1709 pickled = pickle.dumps(e1, proto)
1710 e2 = pickle.loads(pickled)
1711 self.assertEqual(e1.message, e2.message)
1712 self.assertEqual(e1.args, e2.args)
1713 self.assertEqual(e1.section, e2.section)
1714 self.assertEqual(e1.option, e2.option)
1715 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001716
1717 def test_interpolationdeptherror(self):
1718 import pickle
1719 e1 = configparser.InterpolationDepthError('option', 'section',
1720 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001721 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1722 pickled = pickle.dumps(e1, proto)
1723 e2 = pickle.loads(pickled)
1724 self.assertEqual(e1.message, e2.message)
1725 self.assertEqual(e1.args, e2.args)
1726 self.assertEqual(e1.section, e2.section)
1727 self.assertEqual(e1.option, e2.option)
1728 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001729
1730 def test_parsingerror(self):
1731 import pickle
1732 e1 = configparser.ParsingError('source')
1733 e1.append(1, 'line1')
1734 e1.append(2, 'line2')
1735 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001736 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1737 pickled = pickle.dumps(e1, proto)
1738 e2 = pickle.loads(pickled)
1739 self.assertEqual(e1.message, e2.message)
1740 self.assertEqual(e1.args, e2.args)
1741 self.assertEqual(e1.source, e2.source)
1742 self.assertEqual(e1.errors, e2.errors)
1743 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001744 e1 = configparser.ParsingError(filename='filename')
1745 e1.append(1, 'line1')
1746 e1.append(2, 'line2')
1747 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001748 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1749 pickled = pickle.dumps(e1, proto)
1750 e2 = pickle.loads(pickled)
1751 self.assertEqual(e1.message, e2.message)
1752 self.assertEqual(e1.args, e2.args)
1753 self.assertEqual(e1.source, e2.source)
1754 self.assertEqual(e1.errors, e2.errors)
1755 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001756
1757 def test_missingsectionheadererror(self):
1758 import pickle
1759 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001760 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1761 pickled = pickle.dumps(e1, proto)
1762 e2 = pickle.loads(pickled)
1763 self.assertEqual(e1.message, e2.message)
1764 self.assertEqual(e1.args, e2.args)
1765 self.assertEqual(e1.line, e2.line)
1766 self.assertEqual(e1.source, e2.source)
1767 self.assertEqual(e1.lineno, e2.lineno)
1768 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001769
1770
Łukasz Langacba24322012-07-07 18:54:08 +02001771class InlineCommentStrippingTestCase(unittest.TestCase):
1772 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1773 delimiter occurs earlier without preceding space.."""
1774
1775 def test_stripping(self):
1776 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1777 '//'))
1778 cfg.read_string("""
1779 [section]
1780 k1 = v1;still v1
1781 k2 = v2 ;a comment
1782 k3 = v3 ; also a comment
1783 k4 = v4;still v4 ;a comment
1784 k5 = v5;still v5 ; also a comment
1785 k6 = v6;still v6; and still v6 ;a comment
1786 k7 = v7;still v7; and still v7 ; also a comment
1787
1788 [multiprefix]
1789 k1 = v1;still v1 #a comment ; yeah, pretty much
1790 k2 = v2 // this already is a comment ; continued
1791 k3 = v3;#//still v3# and still v3 ; a comment
1792 """)
1793 s = cfg['section']
1794 self.assertEqual(s['k1'], 'v1;still v1')
1795 self.assertEqual(s['k2'], 'v2')
1796 self.assertEqual(s['k3'], 'v3')
1797 self.assertEqual(s['k4'], 'v4;still v4')
1798 self.assertEqual(s['k5'], 'v5;still v5')
1799 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1800 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1801 s = cfg['multiprefix']
1802 self.assertEqual(s['k1'], 'v1;still v1')
1803 self.assertEqual(s['k2'], 'v2')
1804 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1805
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001806
Łukasz Langa949053b2014-09-04 01:36:33 -07001807class ExceptionContextTestCase(unittest.TestCase):
1808 """ Test that implementation details doesn't leak
1809 through raising exceptions. """
1810
1811 def test_get_basic_interpolation(self):
1812 parser = configparser.ConfigParser()
1813 parser.read_string("""
1814 [Paths]
1815 home_dir: /Users
1816 my_dir: %(home_dir1)s/lumberjack
1817 my_pictures: %(my_dir)s/Pictures
1818 """)
1819 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1820 with cm:
1821 parser.get('Paths', 'my_dir')
1822 self.assertIs(cm.exception.__suppress_context__, True)
1823
1824 def test_get_extended_interpolation(self):
1825 parser = configparser.ConfigParser(
1826 interpolation=configparser.ExtendedInterpolation())
1827 parser.read_string("""
1828 [Paths]
1829 home_dir: /Users
1830 my_dir: ${home_dir1}/lumberjack
1831 my_pictures: ${my_dir}/Pictures
1832 """)
1833 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1834 with cm:
1835 parser.get('Paths', 'my_dir')
1836 self.assertIs(cm.exception.__suppress_context__, True)
1837
1838 def test_missing_options(self):
1839 parser = configparser.ConfigParser()
1840 parser.read_string("""
1841 [Paths]
1842 home_dir: /Users
1843 """)
1844 with self.assertRaises(configparser.NoSectionError) as cm:
1845 parser.options('test')
1846 self.assertIs(cm.exception.__suppress_context__, True)
1847
1848 def test_missing_section(self):
1849 config = configparser.ConfigParser()
1850 with self.assertRaises(configparser.NoSectionError) as cm:
1851 config.set('Section1', 'an_int', '15')
1852 self.assertIs(cm.exception.__suppress_context__, True)
1853
1854 def test_remove_option(self):
1855 config = configparser.ConfigParser()
1856 with self.assertRaises(configparser.NoSectionError) as cm:
1857 config.remove_option('Section1', 'an_int')
1858 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001859
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001860
1861class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1862 """Introduced in 3.5, issue #18159."""
1863
1864 config_class = configparser.ConfigParser
1865
1866 def newconfig(self, defaults=None):
1867 instance = super().newconfig(defaults=defaults)
1868 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1869 if e.strip()]
1870 return instance
1871
1872 def test_converters(self):
1873 cfg = self.newconfig()
1874 self.assertIn('boolean', cfg.converters)
1875 self.assertIn('list', cfg.converters)
1876 self.assertIsNone(cfg.converters['int'])
1877 self.assertIsNone(cfg.converters['float'])
1878 self.assertIsNone(cfg.converters['boolean'])
1879 self.assertIsNotNone(cfg.converters['list'])
1880 self.assertEqual(len(cfg.converters), 4)
1881 with self.assertRaises(ValueError):
1882 cfg.converters[''] = lambda v: v
1883 with self.assertRaises(ValueError):
1884 cfg.converters[None] = lambda v: v
1885 cfg.read_string("""
1886 [s]
1887 str = string
1888 int = 1
1889 float = 0.5
1890 list = a b c d e f g
1891 bool = yes
1892 """)
1893 s = cfg['s']
1894 self.assertEqual(s['str'], 'string')
1895 self.assertEqual(s['int'], '1')
1896 self.assertEqual(s['float'], '0.5')
1897 self.assertEqual(s['list'], 'a b c d e f g')
1898 self.assertEqual(s['bool'], 'yes')
1899 self.assertEqual(cfg.get('s', 'str'), 'string')
1900 self.assertEqual(cfg.get('s', 'int'), '1')
1901 self.assertEqual(cfg.get('s', 'float'), '0.5')
1902 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1903 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1904 self.assertEqual(cfg.get('s', 'str'), 'string')
1905 self.assertEqual(cfg.getint('s', 'int'), 1)
1906 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1907 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1908 'e', 'f', 'g'])
1909 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1910 self.assertEqual(s.get('str'), 'string')
1911 self.assertEqual(s.getint('int'), 1)
1912 self.assertEqual(s.getfloat('float'), 0.5)
1913 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1914 'e', 'f', 'g'])
1915 self.assertEqual(s.getboolean('bool'), True)
1916 with self.assertRaises(AttributeError):
1917 cfg.getdecimal('s', 'float')
1918 with self.assertRaises(AttributeError):
1919 s.getdecimal('float')
1920 import decimal
1921 cfg.converters['decimal'] = decimal.Decimal
1922 self.assertIn('decimal', cfg.converters)
1923 self.assertIsNotNone(cfg.converters['decimal'])
1924 self.assertEqual(len(cfg.converters), 5)
1925 dec0_5 = decimal.Decimal('0.5')
1926 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1927 self.assertEqual(s.getdecimal('float'), dec0_5)
1928 del cfg.converters['decimal']
1929 self.assertNotIn('decimal', cfg.converters)
1930 self.assertEqual(len(cfg.converters), 4)
1931 with self.assertRaises(AttributeError):
1932 cfg.getdecimal('s', 'float')
1933 with self.assertRaises(AttributeError):
1934 s.getdecimal('float')
1935 with self.assertRaises(KeyError):
1936 del cfg.converters['decimal']
1937 with self.assertRaises(KeyError):
1938 del cfg.converters['']
1939 with self.assertRaises(KeyError):
1940 del cfg.converters[None]
1941
1942
1943class BlatantOverrideConvertersTestCase(unittest.TestCase):
1944 """What if somebody overrode a getboolean()? We want to make sure that in
1945 this case the automatic converters do not kick in."""
1946
1947 config = """
1948 [one]
1949 one = false
1950 two = false
1951 three = long story short
1952
1953 [two]
1954 one = false
1955 two = false
1956 three = four
1957 """
1958
1959 def test_converters_at_init(self):
1960 cfg = configparser.ConfigParser(converters={'len': len})
1961 cfg.read_string(self.config)
1962 self._test_len(cfg)
1963 self.assertIsNotNone(cfg.converters['len'])
1964
1965 def test_inheritance(self):
1966 class StrangeConfigParser(configparser.ConfigParser):
1967 gettysburg = 'a historic borough in south central Pennsylvania'
1968
1969 def getboolean(self, section, option, *, raw=False, vars=None,
1970 fallback=configparser._UNSET):
1971 if section == option:
1972 return True
1973 return super().getboolean(section, option, raw=raw, vars=vars,
1974 fallback=fallback)
1975 def getlen(self, section, option, *, raw=False, vars=None,
1976 fallback=configparser._UNSET):
1977 return self._get_conv(section, option, len, raw=raw, vars=vars,
1978 fallback=fallback)
1979
1980 cfg = StrangeConfigParser()
1981 cfg.read_string(self.config)
1982 self._test_len(cfg)
1983 self.assertIsNone(cfg.converters['len'])
1984 self.assertTrue(cfg.getboolean('one', 'one'))
1985 self.assertTrue(cfg.getboolean('two', 'two'))
1986 self.assertFalse(cfg.getboolean('one', 'two'))
1987 self.assertFalse(cfg.getboolean('two', 'one'))
1988 cfg.converters['boolean'] = cfg._convert_to_boolean
1989 self.assertFalse(cfg.getboolean('one', 'one'))
1990 self.assertFalse(cfg.getboolean('two', 'two'))
1991 self.assertFalse(cfg.getboolean('one', 'two'))
1992 self.assertFalse(cfg.getboolean('two', 'one'))
1993
1994 def _test_len(self, cfg):
1995 self.assertEqual(len(cfg.converters), 4)
1996 self.assertIn('boolean', cfg.converters)
1997 self.assertIn('len', cfg.converters)
1998 self.assertNotIn('tysburg', cfg.converters)
1999 self.assertIsNone(cfg.converters['int'])
2000 self.assertIsNone(cfg.converters['float'])
2001 self.assertIsNone(cfg.converters['boolean'])
2002 self.assertEqual(cfg.getlen('one', 'one'), 5)
2003 self.assertEqual(cfg.getlen('one', 'two'), 5)
2004 self.assertEqual(cfg.getlen('one', 'three'), 16)
2005 self.assertEqual(cfg.getlen('two', 'one'), 5)
2006 self.assertEqual(cfg.getlen('two', 'two'), 5)
2007 self.assertEqual(cfg.getlen('two', 'three'), 4)
2008 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2009 with self.assertRaises(configparser.NoOptionError):
2010 cfg.getlen('two', 'four')
2011 self.assertEqual(cfg['one'].getlen('one'), 5)
2012 self.assertEqual(cfg['one'].getlen('two'), 5)
2013 self.assertEqual(cfg['one'].getlen('three'), 16)
2014 self.assertEqual(cfg['two'].getlen('one'), 5)
2015 self.assertEqual(cfg['two'].getlen('two'), 5)
2016 self.assertEqual(cfg['two'].getlen('three'), 4)
2017 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2018 self.assertEqual(cfg['two'].getlen('four'), None)
2019
2020 def test_instance_assignment(self):
2021 cfg = configparser.ConfigParser()
2022 cfg.getboolean = lambda section, option: True
2023 cfg.getlen = lambda section, option: len(cfg[section][option])
2024 cfg.read_string(self.config)
2025 self.assertEqual(len(cfg.converters), 3)
2026 self.assertIn('boolean', cfg.converters)
2027 self.assertNotIn('len', cfg.converters)
2028 self.assertIsNone(cfg.converters['int'])
2029 self.assertIsNone(cfg.converters['float'])
2030 self.assertIsNone(cfg.converters['boolean'])
2031 self.assertTrue(cfg.getboolean('one', 'one'))
2032 self.assertTrue(cfg.getboolean('two', 'two'))
2033 self.assertTrue(cfg.getboolean('one', 'two'))
2034 self.assertTrue(cfg.getboolean('two', 'one'))
2035 cfg.converters['boolean'] = cfg._convert_to_boolean
2036 self.assertFalse(cfg.getboolean('one', 'one'))
2037 self.assertFalse(cfg.getboolean('two', 'two'))
2038 self.assertFalse(cfg.getboolean('one', 'two'))
2039 self.assertFalse(cfg.getboolean('two', 'one'))
2040 self.assertEqual(cfg.getlen('one', 'one'), 5)
2041 self.assertEqual(cfg.getlen('one', 'two'), 5)
2042 self.assertEqual(cfg.getlen('one', 'three'), 16)
2043 self.assertEqual(cfg.getlen('two', 'one'), 5)
2044 self.assertEqual(cfg.getlen('two', 'two'), 5)
2045 self.assertEqual(cfg.getlen('two', 'three'), 4)
2046 # If a getter impl is assigned straight to the instance, it won't
2047 # be available on the section proxies.
2048 with self.assertRaises(AttributeError):
2049 self.assertEqual(cfg['one'].getlen('one'), 5)
2050 with self.assertRaises(AttributeError):
2051 self.assertEqual(cfg['two'].getlen('one'), 5)
2052
2053
Ezio Melottidc1fa802013-01-11 06:30:57 +02002054if __name__ == '__main__':
2055 unittest.main()