blob: 71a8f3f8d96db559751e4787eeb6402a4298c84b [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:
Robert Collinsac37ba02015-08-14 11:11:35 +1200850 self.assertEqual(e.args, ("bar11", "Foo",
851 "something %(with11)s lots of interpolation (11 steps)"))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000852 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
853 self.assertEqual(e.args, ("bar11", "Foo",
854 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000855
Fred Drake54782192002-12-31 06:57:25 +0000856 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000857 cf = self.get_interpolation_config()
858 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000859 "Interpolation Error", "name")
860 self.assertEqual(e.reference, "reference")
861 self.assertEqual(e.section, "Interpolation Error")
862 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000863 if self.interpolation == configparser._UNSET:
864 self.assertEqual(e.args, ('name', 'Interpolation Error',
Robert Collinsac37ba02015-08-14 11:11:35 +1200865 '%(reference)s', 'reference'))
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000866 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
867 self.assertEqual(e.args, ('name', 'Interpolation Error',
868 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000869
Fred Drake98e3b292002-10-25 20:42:44 +0000870 def test_items(self):
871 self.check_items_config([('default', '<default>'),
872 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000873 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000874 ('name', 'value'),
875 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000876
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000877 def test_safe_interpolation(self):
878 # See http://www.python.org/sf/511737
879 cf = self.fromstring("[section]\n"
880 "option1{eq}xxx\n"
881 "option2{eq}%(option1)s/xxx\n"
882 "ok{eq}%(option1)s/%%s\n"
883 "not_ok{eq}%(option2)s/%%s".format(
884 eq=self.delimiters[0]))
885 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
886 if self.interpolation == configparser._UNSET:
887 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
888 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
889 with self.assertRaises(TypeError):
890 cf.get("section", "not_ok")
891
892 def test_set_malformatted_interpolation(self):
893 cf = self.fromstring("[sect]\n"
894 "option1{eq}foo\n".format(eq=self.delimiters[0]))
895
896 self.assertEqual(cf.get('sect', "option1"), "foo")
897
898 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
899 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
900 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
901
902 self.assertEqual(cf.get('sect', "option1"), "foo")
903
904 # bug #5741: double percents are *not* malformed
905 cf.set("sect", "option2", "foo%%bar")
906 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
907
David Goodger1cbf2062004-10-03 15:55:09 +0000908 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000909 cf = self.fromstring("[sect]\n"
910 "option1{eq}foo\n".format(eq=self.delimiters[0]))
911 # Check that we get a TypeError when setting non-string values
912 # in an existing section:
913 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
914 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
915 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
916 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
917 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
918 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
919 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
920 self.assertRaises(TypeError, cf.add_section, 123)
921
922 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000923 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000924 self.assertRaises(ValueError, cf.add_section, self.default_section)
925
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200926
Ezio Melottidc1fa802013-01-11 06:30:57 +0200927class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200928 config_class = configparser.ConfigParser
929 interpolation = None
930 ini = textwrap.dedent("""
931 [numbers]
932 one = 1
933 two = %(one)s * 2
934 three = ${common:one} * 3
935
936 [hexen]
937 sixteen = ${numbers:two} * 8
938 """).strip()
939
940 def assertMatchesIni(self, cf):
941 self.assertEqual(cf['numbers']['one'], '1')
942 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
943 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
944 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
945
946 def test_no_interpolation(self):
947 cf = self.fromstring(self.ini)
948 self.assertMatchesIni(cf)
949
950 def test_empty_case(self):
951 cf = self.newconfig()
952 self.assertIsNone(cf.read_string(""))
953
954 def test_none_as_default_interpolation(self):
955 class CustomConfigParser(configparser.ConfigParser):
956 _DEFAULT_INTERPOLATION = None
957
958 cf = CustomConfigParser()
959 cf.read_string(self.ini)
960 self.assertMatchesIni(cf)
961
962
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000963class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
964 config_class = configparser.ConfigParser
965 interpolation = configparser.LegacyInterpolation()
966
967 def test_set_malformatted_interpolation(self):
968 cf = self.fromstring("[sect]\n"
969 "option1{eq}foo\n".format(eq=self.delimiters[0]))
970
971 self.assertEqual(cf.get('sect', "option1"), "foo")
972
973 cf.set("sect", "option1", "%foo")
974 self.assertEqual(cf.get('sect', "option1"), "%foo")
975 cf.set("sect", "option1", "foo%")
976 self.assertEqual(cf.get('sect', "option1"), "foo%")
977 cf.set("sect", "option1", "f%oo")
978 self.assertEqual(cf.get('sect', "option1"), "f%oo")
979
980 # bug #5741: double percents are *not* malformed
981 cf.set("sect", "option2", "foo%%bar")
982 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000983
Georg Brandl96a60ae2010-07-28 13:13:46 +0000984class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
985 delimiters = (':=', '$')
986 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000987 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000988
Łukasz Langac264c092010-11-20 16:15:37 +0000989class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
990 default_section = 'general'
991
Ezio Melottidc1fa802013-01-11 06:30:57 +0200992class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000993 config_class = configparser.ConfigParser
994 wonderful_spam = ("I'm having spam spam spam spam "
995 "spam spam spam beaked beans spam "
996 "spam spam and spam!").replace(' ', '\t\n')
997
998 def setUp(self):
999 cf = self.newconfig()
1000 for i in range(100):
1001 s = 'section{}'.format(i)
1002 cf.add_section(s)
1003 for j in range(10):
1004 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1005 with open(support.TESTFN, 'w') as f:
1006 cf.write(f)
1007
1008 def tearDown(self):
1009 os.unlink(support.TESTFN)
1010
1011 def test_dominating_multiline_values(self):
1012 # We're reading from file because this is where the code changed
1013 # during performance updates in Python 3.2
1014 cf_from_file = self.newconfig()
1015 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001016 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001017 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1018 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001019
Ezio Melottidc1fa802013-01-11 06:30:57 +02001020class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001021 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001022
1023 def test_interpolation(self):
1024 cf = self.get_interpolation_config()
1025 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001026 eq(cf.get("Foo", "bar"),
1027 "something %(with1)s interpolation (1 step)")
1028 eq(cf.get("Foo", "bar9"),
1029 "something %(with9)s lots of interpolation (9 steps)")
1030 eq(cf.get("Foo", "bar10"),
1031 "something %(with10)s lots of interpolation (10 steps)")
1032 eq(cf.get("Foo", "bar11"),
1033 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001034
Fred Drake98e3b292002-10-25 20:42:44 +00001035 def test_items(self):
1036 self.check_items_config([('default', '<default>'),
1037 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001038 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001039 ('name', '%(value)s'),
1040 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001041
David Goodger1cbf2062004-10-03 15:55:09 +00001042 def test_set_nonstring_types(self):
1043 cf = self.newconfig()
1044 cf.add_section('non-string')
1045 cf.set('non-string', 'int', 1)
1046 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1047 cf.set('non-string', 'dict', {'pi': 3.14159})
1048 self.assertEqual(cf.get('non-string', 'int'), 1)
1049 self.assertEqual(cf.get('non-string', 'list'),
1050 [0, 1, 1, 2, 3, 5, 8, 13])
1051 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001052 cf.add_section(123)
1053 cf.set(123, 'this is sick', True)
1054 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001055 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001056 # would not work for SortedDict; only checking for the most common
1057 # default dictionary (OrderedDict)
1058 cf.optionxform = lambda x: x
1059 cf.set('non-string', 1, 1)
1060 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001061
Georg Brandl96a60ae2010-07-28 13:13:46 +00001062class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1063 delimiters = (':=', '$')
1064 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001065 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001066
Ezio Melottidc1fa802013-01-11 06:30:57 +02001067class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001068 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001069 comment_prefixes = ('#', ';', '----')
1070 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001071 empty_lines_in_values = False
1072
1073 def test_reading(self):
1074 smbconf = support.findfile("cfgparser.2")
1075 # check when we pass a mix of readable and non-readable files:
1076 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001077 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001078 self.assertEqual(parsed_files, [smbconf])
1079 sections = ['global', 'homes', 'printers',
1080 'print$', 'pdf-generator', 'tmp', 'Agustin']
1081 self.assertEqual(cf.sections(), sections)
1082 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1083 self.assertEqual(cf.getint("global", "max log size"), 50)
1084 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1085 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001086
Ezio Melottidc1fa802013-01-11 06:30:57 +02001087class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001088 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001089 interpolation = configparser.ExtendedInterpolation()
1090 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001091 strict = True
1092
1093 def fromstring(self, string, defaults=None, optionxform=None):
1094 cf = self.newconfig(defaults)
1095 if optionxform:
1096 cf.optionxform = optionxform
1097 cf.read_string(string)
1098 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001099
1100 def test_extended_interpolation(self):
1101 cf = self.fromstring(textwrap.dedent("""
1102 [common]
1103 favourite Beatle = Paul
1104 favourite color = green
1105
1106 [tom]
1107 favourite band = ${favourite color} day
1108 favourite pope = John ${favourite Beatle} II
1109 sequel = ${favourite pope}I
1110
1111 [ambv]
1112 favourite Beatle = George
1113 son of Edward VII = ${favourite Beatle} V
1114 son of George V = ${son of Edward VII}I
1115
1116 [stanley]
1117 favourite Beatle = ${ambv:favourite Beatle}
1118 favourite pope = ${tom:favourite pope}
1119 favourite color = black
1120 favourite state of mind = paranoid
1121 favourite movie = soylent ${common:favourite color}
1122 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1123 """).strip())
1124
1125 eq = self.assertEqual
1126 eq(cf['common']['favourite Beatle'], 'Paul')
1127 eq(cf['common']['favourite color'], 'green')
1128 eq(cf['tom']['favourite Beatle'], 'Paul')
1129 eq(cf['tom']['favourite color'], 'green')
1130 eq(cf['tom']['favourite band'], 'green day')
1131 eq(cf['tom']['favourite pope'], 'John Paul II')
1132 eq(cf['tom']['sequel'], 'John Paul III')
1133 eq(cf['ambv']['favourite Beatle'], 'George')
1134 eq(cf['ambv']['favourite color'], 'green')
1135 eq(cf['ambv']['son of Edward VII'], 'George V')
1136 eq(cf['ambv']['son of George V'], 'George VI')
1137 eq(cf['stanley']['favourite Beatle'], 'George')
1138 eq(cf['stanley']['favourite color'], 'black')
1139 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1140 eq(cf['stanley']['favourite movie'], 'soylent green')
1141 eq(cf['stanley']['favourite pope'], 'John Paul II')
1142 eq(cf['stanley']['favourite song'],
1143 'black sabbath - paranoid')
1144
1145 def test_endless_loop(self):
1146 cf = self.fromstring(textwrap.dedent("""
1147 [one for you]
1148 ping = ${one for me:pong}
1149
1150 [one for me]
1151 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001152
1153 [selfish]
1154 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001155 """).strip())
1156
1157 with self.assertRaises(configparser.InterpolationDepthError):
1158 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001159 with self.assertRaises(configparser.InterpolationDepthError):
1160 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001161
Łukasz Langa71b37a52010-12-17 21:56:32 +00001162 def test_strange_options(self):
1163 cf = self.fromstring("""
1164 [dollars]
1165 $var = $$value
1166 $var2 = ${$var}
1167 ${sick} = cannot interpolate me
1168
1169 [interpolated]
1170 $other = ${dollars:$var}
1171 $trying = ${dollars:${sick}}
1172 """)
1173
1174 self.assertEqual(cf['dollars']['$var'], '$value')
1175 self.assertEqual(cf['interpolated']['$other'], '$value')
1176 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1177 exception_class = configparser.InterpolationMissingOptionError
1178 with self.assertRaises(exception_class) as cm:
1179 cf['interpolated']['$trying']
1180 self.assertEqual(cm.exception.reference, 'dollars:${sick')
Robert Collinsac37ba02015-08-14 11:11:35 +12001181 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
Łukasz Langa71b37a52010-12-17 21:56:32 +00001182
Łukasz Langae698cd52011-04-28 10:58:57 +02001183 def test_case_sensitivity_basic(self):
1184 ini = textwrap.dedent("""
1185 [common]
1186 optionlower = value
1187 OptionUpper = Value
1188
1189 [Common]
1190 optionlower = a better ${common:optionlower}
1191 OptionUpper = A Better ${common:OptionUpper}
1192
1193 [random]
1194 foolower = ${common:optionlower} redefined
1195 FooUpper = ${Common:OptionUpper} Redefined
1196 """).strip()
1197
1198 cf = self.fromstring(ini)
1199 eq = self.assertEqual
1200 eq(cf['common']['optionlower'], 'value')
1201 eq(cf['common']['OptionUpper'], 'Value')
1202 eq(cf['Common']['optionlower'], 'a better value')
1203 eq(cf['Common']['OptionUpper'], 'A Better Value')
1204 eq(cf['random']['foolower'], 'value redefined')
1205 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1206
1207 def test_case_sensitivity_conflicts(self):
1208 ini = textwrap.dedent("""
1209 [common]
1210 option = value
1211 Option = Value
1212
1213 [Common]
1214 option = a better ${common:option}
1215 Option = A Better ${common:Option}
1216
1217 [random]
1218 foo = ${common:option} redefined
1219 Foo = ${Common:Option} Redefined
1220 """).strip()
1221 with self.assertRaises(configparser.DuplicateOptionError):
1222 cf = self.fromstring(ini)
1223
1224 # raw options
1225 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1226 eq = self.assertEqual
1227 eq(cf['common']['option'], 'value')
1228 eq(cf['common']['Option'], 'Value')
1229 eq(cf['Common']['option'], 'a better value')
1230 eq(cf['Common']['Option'], 'A Better Value')
1231 eq(cf['random']['foo'], 'value redefined')
1232 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001233
1234 def test_other_errors(self):
1235 cf = self.fromstring("""
1236 [interpolation fail]
1237 case1 = ${where's the brace
1238 case2 = ${does_not_exist}
1239 case3 = ${wrong_section:wrong_value}
1240 case4 = ${i:like:colon:characters}
1241 case5 = $100 for Fail No 5!
1242 """)
1243
1244 with self.assertRaises(configparser.InterpolationSyntaxError):
1245 cf['interpolation fail']['case1']
1246 with self.assertRaises(configparser.InterpolationMissingOptionError):
1247 cf['interpolation fail']['case2']
1248 with self.assertRaises(configparser.InterpolationMissingOptionError):
1249 cf['interpolation fail']['case3']
1250 with self.assertRaises(configparser.InterpolationSyntaxError):
1251 cf['interpolation fail']['case4']
1252 with self.assertRaises(configparser.InterpolationSyntaxError):
1253 cf['interpolation fail']['case5']
1254 with self.assertRaises(ValueError):
1255 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001256
1257
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001258class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001259 allow_no_value = True
1260
Ezio Melottidc1fa802013-01-11 06:30:57 +02001261class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001262 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001263 delimiters = {'='}
1264 comment_prefixes = {'#'}
1265 allow_no_value = True
1266
1267 def test_cfgparser_dot_3(self):
1268 tricky = support.findfile("cfgparser.3")
1269 cf = self.newconfig()
1270 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1271 self.assertEqual(cf.sections(), ['strange',
1272 'corruption',
1273 'yeah, sections can be '
1274 'indented as well',
1275 'another one!',
1276 'no values here',
1277 'tricky interpolation',
1278 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001279 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001280 vars={'interpolate': '-1'}), -1)
1281 with self.assertRaises(ValueError):
1282 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001283 cf.getint(self.default_section, 'go', raw=True,
1284 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001285 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1286 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1287 longname = 'yeah, sections can be indented as well'
1288 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001289 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001290 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1291 # `go` from DEFAULT
1292 with self.assertRaises(configparser.InterpolationMissingOptionError):
1293 cf.items('no values here')
1294 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1295 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1296 cf.get('tricky interpolation', 'go'))
1297 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1298
1299 def test_unicode_failure(self):
1300 tricky = support.findfile("cfgparser.3")
1301 cf = self.newconfig()
1302 with self.assertRaises(UnicodeDecodeError):
1303 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001304
Fred Drake88444412010-09-03 04:22:36 +00001305
1306class Issue7005TestCase(unittest.TestCase):
1307 """Test output when None is set() as a value and allow_no_value == False.
1308
1309 http://bugs.python.org/issue7005
1310
1311 """
1312
1313 expected_output = "[section]\noption = None\n\n"
1314
1315 def prepare(self, config_class):
1316 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001317 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001318 cp.add_section("section")
1319 cp.set("section", "option", None)
1320 sio = io.StringIO()
1321 cp.write(sio)
1322 return sio.getvalue()
1323
1324 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001325 cp = configparser.ConfigParser(allow_no_value=False)
1326 cp.add_section("section")
1327 with self.assertRaises(TypeError):
1328 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001329
1330 def test_none_as_value_stringified_raw(self):
1331 output = self.prepare(configparser.RawConfigParser)
1332 self.assertEqual(output, self.expected_output)
1333
1334
Thomas Wouters89f507f2006-12-13 04:49:30 +00001335class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001336 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001337
1338 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001339 cf = self.fromstring("[b]\n"
1340 "o4=1\n"
1341 "o3=2\n"
1342 "o2=3\n"
1343 "o1=4\n"
1344 "[a]\n"
1345 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001346 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001347 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001348 self.assertEqual(output.getvalue(),
1349 "[a]\n"
1350 "k = v\n\n"
1351 "[b]\n"
1352 "o1 = 4\n"
1353 "o2 = 3\n"
1354 "o3 = 2\n"
1355 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001356
Fred Drake03c44a32010-02-19 06:08:41 +00001357
Ezio Melottidc1fa802013-01-11 06:30:57 +02001358class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001359 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001360 comment_prefixes = '#;'
1361 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001362
1363 def test_comment_handling(self):
1364 config_string = textwrap.dedent("""\
1365 [Commented Bar]
1366 baz=qwe ; a comment
1367 foo: bar # not a comment!
1368 # but this is a comment
1369 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001370 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001371 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001372 """)
1373 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001374 self.assertEqual(cf.get('Commented Bar', 'foo'),
1375 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001376 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001377 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1378 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001379
Ezio Melottidc1fa802013-01-11 06:30:57 +02001380class CopyTestCase(BasicTestCase, unittest.TestCase):
Łukasz Langa71b37a52010-12-17 21:56:32 +00001381 config_class = configparser.ConfigParser
1382
1383 def fromstring(self, string, defaults=None):
1384 cf = self.newconfig(defaults)
1385 cf.read_string(string)
1386 cf_copy = self.newconfig()
1387 cf_copy.read_dict(cf)
1388 # we have to clean up option duplicates that appeared because of
1389 # the magic DEFAULTSECT behaviour.
1390 for section in cf_copy.values():
1391 if section.name == self.default_section:
1392 continue
1393 for default, value in cf[self.default_section].items():
1394 if section[default] == value:
1395 del section[default]
1396 return cf_copy
1397
Łukasz Langadaab1c82011-04-27 18:10:05 +02001398
1399class FakeFile:
1400 def __init__(self):
1401 file_path = support.findfile("cfgparser.1")
1402 with open(file_path) as f:
1403 self.lines = f.readlines()
1404 self.lines.reverse()
1405
1406 def readline(self):
1407 if len(self.lines):
1408 return self.lines.pop()
1409 return ''
1410
1411
1412def readline_generator(f):
1413 """As advised in Doc/library/configparser.rst."""
1414 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001415 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001416 yield line
1417 line = f.readline()
1418
1419
1420class ReadFileTestCase(unittest.TestCase):
1421 def test_file(self):
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001422 file_paths = [support.findfile("cfgparser.1")]
1423 try:
1424 file_paths.append(file_paths[0].encode('utf8'))
1425 except UnicodeEncodeError:
1426 pass # unfortunately we can't test bytes on this path
1427 for file_path in file_paths:
1428 parser = configparser.ConfigParser()
1429 with open(file_path) as f:
1430 parser.read_file(f)
1431 self.assertIn("Foo Bar", parser)
1432 self.assertIn("foo", parser["Foo Bar"])
1433 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
Łukasz Langadaab1c82011-04-27 18:10:05 +02001434
1435 def test_iterable(self):
1436 lines = textwrap.dedent("""
1437 [Foo Bar]
1438 foo=newbar""").strip().split('\n')
1439 parser = configparser.ConfigParser()
1440 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001441 self.assertIn("Foo Bar", parser)
1442 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001443 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1444
1445 def test_readline_generator(self):
1446 """Issue #11670."""
1447 parser = configparser.ConfigParser()
1448 with self.assertRaises(TypeError):
1449 parser.read_file(FakeFile())
1450 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001451 self.assertIn("Foo Bar", parser)
1452 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001453 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1454
Łukasz Langaf9b4eb42013-06-23 19:10:25 +02001455 def test_source_as_bytes(self):
1456 """Issue #18260."""
1457 lines = textwrap.dedent("""
1458 [badbad]
1459 [badbad]""").strip().split('\n')
1460 parser = configparser.ConfigParser()
1461 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1462 parser.read_file(lines, source=b"badbad")
1463 self.assertEqual(
1464 str(dse.exception),
1465 "While reading from b'badbad' [line 2]: section 'badbad' "
1466 "already exists"
1467 )
1468 lines = textwrap.dedent("""
1469 [badbad]
1470 bad = bad
1471 bad = bad""").strip().split('\n')
1472 parser = configparser.ConfigParser()
1473 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1474 parser.read_file(lines, source=b"badbad")
1475 self.assertEqual(
1476 str(dse.exception),
1477 "While reading from b'badbad' [line 3]: option 'bad' in section "
1478 "'badbad' already exists"
1479 )
1480 lines = textwrap.dedent("""
1481 [badbad]
1482 = bad""").strip().split('\n')
1483 parser = configparser.ConfigParser()
1484 with self.assertRaises(configparser.ParsingError) as dse:
1485 parser.read_file(lines, source=b"badbad")
1486 self.assertEqual(
1487 str(dse.exception),
1488 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1489 )
1490 lines = textwrap.dedent("""
1491 [badbad
1492 bad = bad""").strip().split('\n')
1493 parser = configparser.ConfigParser()
1494 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1495 parser.read_file(lines, source=b"badbad")
1496 self.assertEqual(
1497 str(dse.exception),
1498 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1499 "'[badbad'"
1500 )
1501
Łukasz Langadaab1c82011-04-27 18:10:05 +02001502
Łukasz Langa71b37a52010-12-17 21:56:32 +00001503class CoverageOneHundredTestCase(unittest.TestCase):
1504 """Covers edge cases in the codebase."""
1505
1506 def test_duplicate_option_error(self):
1507 error = configparser.DuplicateOptionError('section', 'option')
1508 self.assertEqual(error.section, 'section')
1509 self.assertEqual(error.option, 'option')
1510 self.assertEqual(error.source, None)
1511 self.assertEqual(error.lineno, None)
1512 self.assertEqual(error.args, ('section', 'option', None, None))
1513 self.assertEqual(str(error), "Option 'option' in section 'section' "
1514 "already exists")
1515
1516 def test_interpolation_depth_error(self):
1517 error = configparser.InterpolationDepthError('option', 'section',
1518 'rawval')
1519 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1520 self.assertEqual(error.option, 'option')
1521 self.assertEqual(error.section, 'section')
1522
1523 def test_parsing_error(self):
1524 with self.assertRaises(ValueError) as cm:
1525 configparser.ParsingError()
1526 self.assertEqual(str(cm.exception), "Required argument `source' not "
1527 "given.")
1528 with self.assertRaises(ValueError) as cm:
1529 configparser.ParsingError(source='source', filename='filename')
1530 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1531 "and `source'. Use `source'.")
1532 error = configparser.ParsingError(filename='source')
1533 self.assertEqual(error.source, 'source')
1534 with warnings.catch_warnings(record=True) as w:
1535 warnings.simplefilter("always", DeprecationWarning)
1536 self.assertEqual(error.filename, 'source')
1537 error.filename = 'filename'
1538 self.assertEqual(error.source, 'filename')
1539 for warning in w:
1540 self.assertTrue(warning.category is DeprecationWarning)
1541
1542 def test_interpolation_validation(self):
1543 parser = configparser.ConfigParser()
1544 parser.read_string("""
1545 [section]
1546 invalid_percent = %
1547 invalid_reference = %(()
1548 invalid_variable = %(does_not_exist)s
1549 """)
1550 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1551 parser['section']['invalid_percent']
1552 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1553 "'(', found: '%'")
1554 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1555 parser['section']['invalid_reference']
1556 self.assertEqual(str(cm.exception), "bad interpolation variable "
1557 "reference '%(()'")
1558
1559 def test_readfp_deprecation(self):
1560 sio = io.StringIO("""
1561 [section]
1562 option = value
1563 """)
1564 parser = configparser.ConfigParser()
1565 with warnings.catch_warnings(record=True) as w:
1566 warnings.simplefilter("always", DeprecationWarning)
1567 parser.readfp(sio, filename='StringIO')
1568 for warning in w:
1569 self.assertTrue(warning.category is DeprecationWarning)
1570 self.assertEqual(len(parser), 2)
1571 self.assertEqual(parser['section']['option'], 'value')
1572
1573 def test_safeconfigparser_deprecation(self):
1574 with warnings.catch_warnings(record=True) as w:
1575 warnings.simplefilter("always", DeprecationWarning)
1576 parser = configparser.SafeConfigParser()
1577 for warning in w:
1578 self.assertTrue(warning.category is DeprecationWarning)
1579
1580 def test_sectionproxy_repr(self):
1581 parser = configparser.ConfigParser()
1582 parser.read_string("""
1583 [section]
1584 key = value
1585 """)
1586 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001587
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001588 def test_inconsistent_converters_state(self):
1589 parser = configparser.ConfigParser()
1590 import decimal
1591 parser.converters['decimal'] = decimal.Decimal
1592 parser.read_string("""
1593 [s1]
1594 one = 1
1595 [s2]
1596 two = 2
1597 """)
1598 self.assertIn('decimal', parser.converters)
1599 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1600 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1601 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1602 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1603 del parser.getdecimal
1604 with self.assertRaises(AttributeError):
1605 parser.getdecimal('s1', 'one')
1606 self.assertIn('decimal', parser.converters)
1607 del parser.converters['decimal']
1608 self.assertNotIn('decimal', parser.converters)
1609 with self.assertRaises(AttributeError):
1610 parser.getdecimal('s1', 'one')
1611 with self.assertRaises(AttributeError):
1612 parser['s1'].getdecimal('one')
1613 with self.assertRaises(AttributeError):
1614 parser['s2'].getdecimal('two')
1615
Łukasz Langae7851952012-01-20 14:57:55 +01001616
1617class ExceptionPicklingTestCase(unittest.TestCase):
1618 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1619
1620 def test_error(self):
1621 import pickle
1622 e1 = configparser.Error('value')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001623 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1624 pickled = pickle.dumps(e1, proto)
1625 e2 = pickle.loads(pickled)
1626 self.assertEqual(e1.message, e2.message)
1627 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001628
1629 def test_nosectionerror(self):
1630 import pickle
1631 e1 = configparser.NoSectionError('section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001632 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1633 pickled = pickle.dumps(e1, proto)
1634 e2 = pickle.loads(pickled)
1635 self.assertEqual(e1.message, e2.message)
1636 self.assertEqual(e1.args, e2.args)
1637 self.assertEqual(e1.section, e2.section)
1638 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001639
1640 def test_nooptionerror(self):
1641 import pickle
1642 e1 = configparser.NoOptionError('option', 'section')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001643 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1644 pickled = pickle.dumps(e1, proto)
1645 e2 = pickle.loads(pickled)
1646 self.assertEqual(e1.message, e2.message)
1647 self.assertEqual(e1.args, e2.args)
1648 self.assertEqual(e1.section, e2.section)
1649 self.assertEqual(e1.option, e2.option)
1650 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001651
1652 def test_duplicatesectionerror(self):
1653 import pickle
1654 e1 = configparser.DuplicateSectionError('section', 'source', 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001655 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1656 pickled = pickle.dumps(e1, proto)
1657 e2 = pickle.loads(pickled)
1658 self.assertEqual(e1.message, e2.message)
1659 self.assertEqual(e1.args, e2.args)
1660 self.assertEqual(e1.section, e2.section)
1661 self.assertEqual(e1.source, e2.source)
1662 self.assertEqual(e1.lineno, e2.lineno)
1663 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001664
1665 def test_duplicateoptionerror(self):
1666 import pickle
1667 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1668 123)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001669 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1670 pickled = pickle.dumps(e1, proto)
1671 e2 = pickle.loads(pickled)
1672 self.assertEqual(e1.message, e2.message)
1673 self.assertEqual(e1.args, e2.args)
1674 self.assertEqual(e1.section, e2.section)
1675 self.assertEqual(e1.option, e2.option)
1676 self.assertEqual(e1.source, e2.source)
1677 self.assertEqual(e1.lineno, e2.lineno)
1678 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001679
1680 def test_interpolationerror(self):
1681 import pickle
1682 e1 = configparser.InterpolationError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001683 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1684 pickled = pickle.dumps(e1, proto)
1685 e2 = pickle.loads(pickled)
1686 self.assertEqual(e1.message, e2.message)
1687 self.assertEqual(e1.args, e2.args)
1688 self.assertEqual(e1.section, e2.section)
1689 self.assertEqual(e1.option, e2.option)
1690 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001691
1692 def test_interpolationmissingoptionerror(self):
1693 import pickle
1694 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1695 'rawval', 'reference')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001696 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1697 pickled = pickle.dumps(e1, proto)
1698 e2 = pickle.loads(pickled)
1699 self.assertEqual(e1.message, e2.message)
1700 self.assertEqual(e1.args, e2.args)
1701 self.assertEqual(e1.section, e2.section)
1702 self.assertEqual(e1.option, e2.option)
1703 self.assertEqual(e1.reference, e2.reference)
1704 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001705
1706 def test_interpolationsyntaxerror(self):
1707 import pickle
1708 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001709 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1710 pickled = pickle.dumps(e1, proto)
1711 e2 = pickle.loads(pickled)
1712 self.assertEqual(e1.message, e2.message)
1713 self.assertEqual(e1.args, e2.args)
1714 self.assertEqual(e1.section, e2.section)
1715 self.assertEqual(e1.option, e2.option)
1716 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001717
1718 def test_interpolationdeptherror(self):
1719 import pickle
1720 e1 = configparser.InterpolationDepthError('option', 'section',
1721 'rawval')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001722 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1723 pickled = pickle.dumps(e1, proto)
1724 e2 = pickle.loads(pickled)
1725 self.assertEqual(e1.message, e2.message)
1726 self.assertEqual(e1.args, e2.args)
1727 self.assertEqual(e1.section, e2.section)
1728 self.assertEqual(e1.option, e2.option)
1729 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001730
1731 def test_parsingerror(self):
1732 import pickle
1733 e1 = configparser.ParsingError('source')
1734 e1.append(1, 'line1')
1735 e1.append(2, 'line2')
1736 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001737 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1738 pickled = pickle.dumps(e1, proto)
1739 e2 = pickle.loads(pickled)
1740 self.assertEqual(e1.message, e2.message)
1741 self.assertEqual(e1.args, e2.args)
1742 self.assertEqual(e1.source, e2.source)
1743 self.assertEqual(e1.errors, e2.errors)
1744 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001745 e1 = configparser.ParsingError(filename='filename')
1746 e1.append(1, 'line1')
1747 e1.append(2, 'line2')
1748 e1.append(3, 'line3')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001749 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1750 pickled = pickle.dumps(e1, proto)
1751 e2 = pickle.loads(pickled)
1752 self.assertEqual(e1.message, e2.message)
1753 self.assertEqual(e1.args, e2.args)
1754 self.assertEqual(e1.source, e2.source)
1755 self.assertEqual(e1.errors, e2.errors)
1756 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001757
1758 def test_missingsectionheadererror(self):
1759 import pickle
1760 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
Serhiy Storchakabad12572014-12-15 14:03:42 +02001761 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1762 pickled = pickle.dumps(e1, proto)
1763 e2 = pickle.loads(pickled)
1764 self.assertEqual(e1.message, e2.message)
1765 self.assertEqual(e1.args, e2.args)
1766 self.assertEqual(e1.line, e2.line)
1767 self.assertEqual(e1.source, e2.source)
1768 self.assertEqual(e1.lineno, e2.lineno)
1769 self.assertEqual(repr(e1), repr(e2))
Łukasz Langae7851952012-01-20 14:57:55 +01001770
1771
Łukasz Langacba24322012-07-07 18:54:08 +02001772class InlineCommentStrippingTestCase(unittest.TestCase):
1773 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1774 delimiter occurs earlier without preceding space.."""
1775
1776 def test_stripping(self):
1777 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1778 '//'))
1779 cfg.read_string("""
1780 [section]
1781 k1 = v1;still v1
1782 k2 = v2 ;a comment
1783 k3 = v3 ; also a comment
1784 k4 = v4;still v4 ;a comment
1785 k5 = v5;still v5 ; also a comment
1786 k6 = v6;still v6; and still v6 ;a comment
1787 k7 = v7;still v7; and still v7 ; also a comment
1788
1789 [multiprefix]
1790 k1 = v1;still v1 #a comment ; yeah, pretty much
1791 k2 = v2 // this already is a comment ; continued
1792 k3 = v3;#//still v3# and still v3 ; a comment
1793 """)
1794 s = cfg['section']
1795 self.assertEqual(s['k1'], 'v1;still v1')
1796 self.assertEqual(s['k2'], 'v2')
1797 self.assertEqual(s['k3'], 'v3')
1798 self.assertEqual(s['k4'], 'v4;still v4')
1799 self.assertEqual(s['k5'], 'v5;still v5')
1800 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1801 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1802 s = cfg['multiprefix']
1803 self.assertEqual(s['k1'], 'v1;still v1')
1804 self.assertEqual(s['k2'], 'v2')
1805 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1806
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001807
Łukasz Langa949053b2014-09-04 01:36:33 -07001808class ExceptionContextTestCase(unittest.TestCase):
1809 """ Test that implementation details doesn't leak
1810 through raising exceptions. """
1811
1812 def test_get_basic_interpolation(self):
1813 parser = configparser.ConfigParser()
1814 parser.read_string("""
1815 [Paths]
1816 home_dir: /Users
1817 my_dir: %(home_dir1)s/lumberjack
1818 my_pictures: %(my_dir)s/Pictures
1819 """)
1820 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1821 with cm:
1822 parser.get('Paths', 'my_dir')
1823 self.assertIs(cm.exception.__suppress_context__, True)
1824
1825 def test_get_extended_interpolation(self):
1826 parser = configparser.ConfigParser(
1827 interpolation=configparser.ExtendedInterpolation())
1828 parser.read_string("""
1829 [Paths]
1830 home_dir: /Users
1831 my_dir: ${home_dir1}/lumberjack
1832 my_pictures: ${my_dir}/Pictures
1833 """)
1834 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1835 with cm:
1836 parser.get('Paths', 'my_dir')
1837 self.assertIs(cm.exception.__suppress_context__, True)
1838
1839 def test_missing_options(self):
1840 parser = configparser.ConfigParser()
1841 parser.read_string("""
1842 [Paths]
1843 home_dir: /Users
1844 """)
1845 with self.assertRaises(configparser.NoSectionError) as cm:
1846 parser.options('test')
1847 self.assertIs(cm.exception.__suppress_context__, True)
1848
1849 def test_missing_section(self):
1850 config = configparser.ConfigParser()
1851 with self.assertRaises(configparser.NoSectionError) as cm:
1852 config.set('Section1', 'an_int', '15')
1853 self.assertIs(cm.exception.__suppress_context__, True)
1854
1855 def test_remove_option(self):
1856 config = configparser.ConfigParser()
1857 with self.assertRaises(configparser.NoSectionError) as cm:
1858 config.remove_option('Section1', 'an_int')
1859 self.assertIs(cm.exception.__suppress_context__, True)
Łukasz Langacba24322012-07-07 18:54:08 +02001860
Łukasz Langadfdd2f72014-09-15 02:08:41 -07001861
1862class ConvertersTestCase(BasicTestCase, unittest.TestCase):
1863 """Introduced in 3.5, issue #18159."""
1864
1865 config_class = configparser.ConfigParser
1866
1867 def newconfig(self, defaults=None):
1868 instance = super().newconfig(defaults=defaults)
1869 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1870 if e.strip()]
1871 return instance
1872
1873 def test_converters(self):
1874 cfg = self.newconfig()
1875 self.assertIn('boolean', cfg.converters)
1876 self.assertIn('list', cfg.converters)
1877 self.assertIsNone(cfg.converters['int'])
1878 self.assertIsNone(cfg.converters['float'])
1879 self.assertIsNone(cfg.converters['boolean'])
1880 self.assertIsNotNone(cfg.converters['list'])
1881 self.assertEqual(len(cfg.converters), 4)
1882 with self.assertRaises(ValueError):
1883 cfg.converters[''] = lambda v: v
1884 with self.assertRaises(ValueError):
1885 cfg.converters[None] = lambda v: v
1886 cfg.read_string("""
1887 [s]
1888 str = string
1889 int = 1
1890 float = 0.5
1891 list = a b c d e f g
1892 bool = yes
1893 """)
1894 s = cfg['s']
1895 self.assertEqual(s['str'], 'string')
1896 self.assertEqual(s['int'], '1')
1897 self.assertEqual(s['float'], '0.5')
1898 self.assertEqual(s['list'], 'a b c d e f g')
1899 self.assertEqual(s['bool'], 'yes')
1900 self.assertEqual(cfg.get('s', 'str'), 'string')
1901 self.assertEqual(cfg.get('s', 'int'), '1')
1902 self.assertEqual(cfg.get('s', 'float'), '0.5')
1903 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1904 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1905 self.assertEqual(cfg.get('s', 'str'), 'string')
1906 self.assertEqual(cfg.getint('s', 'int'), 1)
1907 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1908 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1909 'e', 'f', 'g'])
1910 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1911 self.assertEqual(s.get('str'), 'string')
1912 self.assertEqual(s.getint('int'), 1)
1913 self.assertEqual(s.getfloat('float'), 0.5)
1914 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1915 'e', 'f', 'g'])
1916 self.assertEqual(s.getboolean('bool'), True)
1917 with self.assertRaises(AttributeError):
1918 cfg.getdecimal('s', 'float')
1919 with self.assertRaises(AttributeError):
1920 s.getdecimal('float')
1921 import decimal
1922 cfg.converters['decimal'] = decimal.Decimal
1923 self.assertIn('decimal', cfg.converters)
1924 self.assertIsNotNone(cfg.converters['decimal'])
1925 self.assertEqual(len(cfg.converters), 5)
1926 dec0_5 = decimal.Decimal('0.5')
1927 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1928 self.assertEqual(s.getdecimal('float'), dec0_5)
1929 del cfg.converters['decimal']
1930 self.assertNotIn('decimal', cfg.converters)
1931 self.assertEqual(len(cfg.converters), 4)
1932 with self.assertRaises(AttributeError):
1933 cfg.getdecimal('s', 'float')
1934 with self.assertRaises(AttributeError):
1935 s.getdecimal('float')
1936 with self.assertRaises(KeyError):
1937 del cfg.converters['decimal']
1938 with self.assertRaises(KeyError):
1939 del cfg.converters['']
1940 with self.assertRaises(KeyError):
1941 del cfg.converters[None]
1942
1943
1944class BlatantOverrideConvertersTestCase(unittest.TestCase):
1945 """What if somebody overrode a getboolean()? We want to make sure that in
1946 this case the automatic converters do not kick in."""
1947
1948 config = """
1949 [one]
1950 one = false
1951 two = false
1952 three = long story short
1953
1954 [two]
1955 one = false
1956 two = false
1957 three = four
1958 """
1959
1960 def test_converters_at_init(self):
1961 cfg = configparser.ConfigParser(converters={'len': len})
1962 cfg.read_string(self.config)
1963 self._test_len(cfg)
1964 self.assertIsNotNone(cfg.converters['len'])
1965
1966 def test_inheritance(self):
1967 class StrangeConfigParser(configparser.ConfigParser):
1968 gettysburg = 'a historic borough in south central Pennsylvania'
1969
1970 def getboolean(self, section, option, *, raw=False, vars=None,
1971 fallback=configparser._UNSET):
1972 if section == option:
1973 return True
1974 return super().getboolean(section, option, raw=raw, vars=vars,
1975 fallback=fallback)
1976 def getlen(self, section, option, *, raw=False, vars=None,
1977 fallback=configparser._UNSET):
1978 return self._get_conv(section, option, len, raw=raw, vars=vars,
1979 fallback=fallback)
1980
1981 cfg = StrangeConfigParser()
1982 cfg.read_string(self.config)
1983 self._test_len(cfg)
1984 self.assertIsNone(cfg.converters['len'])
1985 self.assertTrue(cfg.getboolean('one', 'one'))
1986 self.assertTrue(cfg.getboolean('two', 'two'))
1987 self.assertFalse(cfg.getboolean('one', 'two'))
1988 self.assertFalse(cfg.getboolean('two', 'one'))
1989 cfg.converters['boolean'] = cfg._convert_to_boolean
1990 self.assertFalse(cfg.getboolean('one', 'one'))
1991 self.assertFalse(cfg.getboolean('two', 'two'))
1992 self.assertFalse(cfg.getboolean('one', 'two'))
1993 self.assertFalse(cfg.getboolean('two', 'one'))
1994
1995 def _test_len(self, cfg):
1996 self.assertEqual(len(cfg.converters), 4)
1997 self.assertIn('boolean', cfg.converters)
1998 self.assertIn('len', cfg.converters)
1999 self.assertNotIn('tysburg', cfg.converters)
2000 self.assertIsNone(cfg.converters['int'])
2001 self.assertIsNone(cfg.converters['float'])
2002 self.assertIsNone(cfg.converters['boolean'])
2003 self.assertEqual(cfg.getlen('one', 'one'), 5)
2004 self.assertEqual(cfg.getlen('one', 'two'), 5)
2005 self.assertEqual(cfg.getlen('one', 'three'), 16)
2006 self.assertEqual(cfg.getlen('two', 'one'), 5)
2007 self.assertEqual(cfg.getlen('two', 'two'), 5)
2008 self.assertEqual(cfg.getlen('two', 'three'), 4)
2009 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2010 with self.assertRaises(configparser.NoOptionError):
2011 cfg.getlen('two', 'four')
2012 self.assertEqual(cfg['one'].getlen('one'), 5)
2013 self.assertEqual(cfg['one'].getlen('two'), 5)
2014 self.assertEqual(cfg['one'].getlen('three'), 16)
2015 self.assertEqual(cfg['two'].getlen('one'), 5)
2016 self.assertEqual(cfg['two'].getlen('two'), 5)
2017 self.assertEqual(cfg['two'].getlen('three'), 4)
2018 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2019 self.assertEqual(cfg['two'].getlen('four'), None)
2020
2021 def test_instance_assignment(self):
2022 cfg = configparser.ConfigParser()
2023 cfg.getboolean = lambda section, option: True
2024 cfg.getlen = lambda section, option: len(cfg[section][option])
2025 cfg.read_string(self.config)
2026 self.assertEqual(len(cfg.converters), 3)
2027 self.assertIn('boolean', cfg.converters)
2028 self.assertNotIn('len', cfg.converters)
2029 self.assertIsNone(cfg.converters['int'])
2030 self.assertIsNone(cfg.converters['float'])
2031 self.assertIsNone(cfg.converters['boolean'])
2032 self.assertTrue(cfg.getboolean('one', 'one'))
2033 self.assertTrue(cfg.getboolean('two', 'two'))
2034 self.assertTrue(cfg.getboolean('one', 'two'))
2035 self.assertTrue(cfg.getboolean('two', 'one'))
2036 cfg.converters['boolean'] = cfg._convert_to_boolean
2037 self.assertFalse(cfg.getboolean('one', 'one'))
2038 self.assertFalse(cfg.getboolean('two', 'two'))
2039 self.assertFalse(cfg.getboolean('one', 'two'))
2040 self.assertFalse(cfg.getboolean('two', 'one'))
2041 self.assertEqual(cfg.getlen('one', 'one'), 5)
2042 self.assertEqual(cfg.getlen('one', 'two'), 5)
2043 self.assertEqual(cfg.getlen('one', 'three'), 16)
2044 self.assertEqual(cfg.getlen('two', 'one'), 5)
2045 self.assertEqual(cfg.getlen('two', 'two'), 5)
2046 self.assertEqual(cfg.getlen('two', 'three'), 4)
2047 # If a getter impl is assigned straight to the instance, it won't
2048 # be available on the section proxies.
2049 with self.assertRaises(AttributeError):
2050 self.assertEqual(cfg['one'].getlen('one'), 5)
2051 with self.assertRaises(AttributeError):
2052 self.assertEqual(cfg['two'].getlen('one'), 5)
2053
2054
Ezio Melottidc1fa802013-01-11 06:30:57 +02002055if __name__ == '__main__':
2056 unittest.main()