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