blob: 8d82182d2861ce13b669281218cccb5c184dbed4 [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
Georg Brandl96a60ae2010-07-28 13:13:46 +000035class CfgParserTestCaseClass(unittest.TestCase):
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"
582 % (exc.__module__, exc.__name__))
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
629 self.assertEqual(str(e), "While reading from <foo-bar> [line 5]: "
630 "section 'Foo' already exists")
631 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
636 self.assertEqual(str(e), "While reading from <dict>: option 'opt' "
637 "in section 'Bar' already exists")
638 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] != '=':
710 # skip reading the file if we're using an incompatible format
711 return
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000712 file1 = support.findfile("cfgparser.1")
Fred Drake82903142004-05-18 04:24:02 +0000713 # check when we pass a mix of readable and non-readable files:
714 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000715 parsed_files = cf.read([file1, "nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000716 self.assertEqual(parsed_files, [file1])
717 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
718 # check when we pass only a filename:
719 cf = self.newconfig()
720 parsed_files = cf.read(file1)
721 self.assertEqual(parsed_files, [file1])
722 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
723 # check when we pass only missing files:
724 cf = self.newconfig()
Mark Dickinson934896d2009-02-21 20:59:32 +0000725 parsed_files = cf.read(["nonexistent-file"])
Fred Drake82903142004-05-18 04:24:02 +0000726 self.assertEqual(parsed_files, [])
727 # check when we pass no files:
728 cf = self.newconfig()
729 parsed_files = cf.read([])
730 self.assertEqual(parsed_files, [])
731
Fred Drakec6f28912002-10-25 19:40:49 +0000732 # shared by subclasses
733 def get_interpolation_config(self):
734 return self.fromstring(
735 "[Foo]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000736 "bar{equals}something %(with1)s interpolation (1 step)\n"
737 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
738 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
739 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
740 "with11{equals}%(with10)s\n"
741 "with10{equals}%(with9)s\n"
742 "with9{equals}%(with8)s\n"
743 "with8{equals}%(With7)s\n"
744 "with7{equals}%(WITH6)s\n"
745 "with6{equals}%(with5)s\n"
746 "With5{equals}%(with4)s\n"
747 "WITH4{equals}%(with3)s\n"
748 "with3{equals}%(with2)s\n"
749 "with2{equals}%(with1)s\n"
750 "with1{equals}with\n"
Fred Drakec6f28912002-10-25 19:40:49 +0000751 "\n"
752 "[Mutual Recursion]\n"
Georg Brandl96a60ae2010-07-28 13:13:46 +0000753 "foo{equals}%(bar)s\n"
754 "bar{equals}%(foo)s\n"
Fred Drake54782192002-12-31 06:57:25 +0000755 "\n"
756 "[Interpolation Error]\n"
Fred Drake54782192002-12-31 06:57:25 +0000757 # no definition for 'reference'
Łukasz Langa5c863392010-11-21 13:41:35 +0000758 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
Fred Drake95b96d32001-02-12 17:23:20 +0000759
Fred Drake98e3b292002-10-25 20:42:44 +0000760 def check_items_config(self, expected):
Łukasz Langa71b37a52010-12-17 21:56:32 +0000761 cf = self.fromstring("""
762 [section]
763 name {0[0]} %(value)s
764 key{0[1]} |%(name)s|
765 getdefault{0[1]} |%(default)s|
766 """.format(self.delimiters), defaults={"default": "<default>"})
767 L = list(cf.items("section", vars={'value': 'value'}))
Fred Drake98e3b292002-10-25 20:42:44 +0000768 L.sort()
769 self.assertEqual(L, expected)
Łukasz Langa71b37a52010-12-17 21:56:32 +0000770 with self.assertRaises(configparser.NoSectionError):
771 cf.items("no such section")
Fred Drake98e3b292002-10-25 20:42:44 +0000772
Fred Drake8ef67672000-09-27 22:45:25 +0000773
Fred Drakea4923622010-08-09 12:52:45 +0000774class StrictTestCase(BasicTestCase):
775 config_class = configparser.RawConfigParser
776 strict = True
777
778
Georg Brandl96a60ae2010-07-28 13:13:46 +0000779class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000780 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000781
782 def test_interpolation(self):
783 cf = self.get_interpolation_config()
784 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000785 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
786 eq(cf.get("Foo", "bar9"),
787 "something with lots of interpolation (9 steps)")
788 eq(cf.get("Foo", "bar10"),
789 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000790 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000791 if self.interpolation == configparser._UNSET:
792 self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
793 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
794 self.assertEqual(e.args, ("bar11", "Foo",
795 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000796
Fred Drake54782192002-12-31 06:57:25 +0000797 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000798 cf = self.get_interpolation_config()
799 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000800 "Interpolation Error", "name")
801 self.assertEqual(e.reference, "reference")
802 self.assertEqual(e.section, "Interpolation Error")
803 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000804 if self.interpolation == configparser._UNSET:
805 self.assertEqual(e.args, ('name', 'Interpolation Error',
806 '', 'reference'))
807 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
808 self.assertEqual(e.args, ('name', 'Interpolation Error',
809 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000810
Fred Drake98e3b292002-10-25 20:42:44 +0000811 def test_items(self):
812 self.check_items_config([('default', '<default>'),
813 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000814 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000815 ('name', 'value'),
816 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000817
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000818 def test_safe_interpolation(self):
819 # See http://www.python.org/sf/511737
820 cf = self.fromstring("[section]\n"
821 "option1{eq}xxx\n"
822 "option2{eq}%(option1)s/xxx\n"
823 "ok{eq}%(option1)s/%%s\n"
824 "not_ok{eq}%(option2)s/%%s".format(
825 eq=self.delimiters[0]))
826 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
827 if self.interpolation == configparser._UNSET:
828 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
829 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
830 with self.assertRaises(TypeError):
831 cf.get("section", "not_ok")
832
833 def test_set_malformatted_interpolation(self):
834 cf = self.fromstring("[sect]\n"
835 "option1{eq}foo\n".format(eq=self.delimiters[0]))
836
837 self.assertEqual(cf.get('sect', "option1"), "foo")
838
839 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
840 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
841 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
842
843 self.assertEqual(cf.get('sect', "option1"), "foo")
844
845 # bug #5741: double percents are *not* malformed
846 cf.set("sect", "option2", "foo%%bar")
847 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
848
David Goodger1cbf2062004-10-03 15:55:09 +0000849 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000850 cf = self.fromstring("[sect]\n"
851 "option1{eq}foo\n".format(eq=self.delimiters[0]))
852 # Check that we get a TypeError when setting non-string values
853 # in an existing section:
854 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
855 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
856 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
857 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
858 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
859 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
860 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
861 self.assertRaises(TypeError, cf.add_section, 123)
862
863 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000864 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000865 self.assertRaises(ValueError, cf.add_section, self.default_section)
866
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200867
868class ConfigParserTestCaseNoInterpolation(BasicTestCase):
869 config_class = configparser.ConfigParser
870 interpolation = None
871 ini = textwrap.dedent("""
872 [numbers]
873 one = 1
874 two = %(one)s * 2
875 three = ${common:one} * 3
876
877 [hexen]
878 sixteen = ${numbers:two} * 8
879 """).strip()
880
881 def assertMatchesIni(self, cf):
882 self.assertEqual(cf['numbers']['one'], '1')
883 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
884 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
885 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
886
887 def test_no_interpolation(self):
888 cf = self.fromstring(self.ini)
889 self.assertMatchesIni(cf)
890
891 def test_empty_case(self):
892 cf = self.newconfig()
893 self.assertIsNone(cf.read_string(""))
894
895 def test_none_as_default_interpolation(self):
896 class CustomConfigParser(configparser.ConfigParser):
897 _DEFAULT_INTERPOLATION = None
898
899 cf = CustomConfigParser()
900 cf.read_string(self.ini)
901 self.assertMatchesIni(cf)
902
903
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000904class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
905 config_class = configparser.ConfigParser
906 interpolation = configparser.LegacyInterpolation()
907
908 def test_set_malformatted_interpolation(self):
909 cf = self.fromstring("[sect]\n"
910 "option1{eq}foo\n".format(eq=self.delimiters[0]))
911
912 self.assertEqual(cf.get('sect', "option1"), "foo")
913
914 cf.set("sect", "option1", "%foo")
915 self.assertEqual(cf.get('sect', "option1"), "%foo")
916 cf.set("sect", "option1", "foo%")
917 self.assertEqual(cf.get('sect', "option1"), "foo%")
918 cf.set("sect", "option1", "f%oo")
919 self.assertEqual(cf.get('sect', "option1"), "f%oo")
920
921 # bug #5741: double percents are *not* malformed
922 cf.set("sect", "option2", "foo%%bar")
923 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000924
Georg Brandl96a60ae2010-07-28 13:13:46 +0000925class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
926 delimiters = (':=', '$')
927 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000928 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000929
Łukasz Langac264c092010-11-20 16:15:37 +0000930class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
931 default_section = 'general'
932
Georg Brandl96a60ae2010-07-28 13:13:46 +0000933class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000934 config_class = configparser.ConfigParser
935 wonderful_spam = ("I'm having spam spam spam spam "
936 "spam spam spam beaked beans spam "
937 "spam spam and spam!").replace(' ', '\t\n')
938
939 def setUp(self):
940 cf = self.newconfig()
941 for i in range(100):
942 s = 'section{}'.format(i)
943 cf.add_section(s)
944 for j in range(10):
945 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
946 with open(support.TESTFN, 'w') as f:
947 cf.write(f)
948
949 def tearDown(self):
950 os.unlink(support.TESTFN)
951
952 def test_dominating_multiline_values(self):
953 # We're reading from file because this is where the code changed
954 # during performance updates in Python 3.2
955 cf_from_file = self.newconfig()
956 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000957 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000958 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
959 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000960
Georg Brandl96a60ae2010-07-28 13:13:46 +0000961class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000962 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000963
964 def test_interpolation(self):
965 cf = self.get_interpolation_config()
966 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000967 eq(cf.get("Foo", "bar"),
968 "something %(with1)s interpolation (1 step)")
969 eq(cf.get("Foo", "bar9"),
970 "something %(with9)s lots of interpolation (9 steps)")
971 eq(cf.get("Foo", "bar10"),
972 "something %(with10)s lots of interpolation (10 steps)")
973 eq(cf.get("Foo", "bar11"),
974 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000975
Fred Drake98e3b292002-10-25 20:42:44 +0000976 def test_items(self):
977 self.check_items_config([('default', '<default>'),
978 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000979 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000980 ('name', '%(value)s'),
981 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000982
David Goodger1cbf2062004-10-03 15:55:09 +0000983 def test_set_nonstring_types(self):
984 cf = self.newconfig()
985 cf.add_section('non-string')
986 cf.set('non-string', 'int', 1)
987 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
988 cf.set('non-string', 'dict', {'pi': 3.14159})
989 self.assertEqual(cf.get('non-string', 'int'), 1)
990 self.assertEqual(cf.get('non-string', 'list'),
991 [0, 1, 1, 2, 3, 5, 8, 13])
992 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000993 cf.add_section(123)
994 cf.set(123, 'this is sick', True)
995 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +0200996 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000997 # would not work for SortedDict; only checking for the most common
998 # default dictionary (OrderedDict)
999 cf.optionxform = lambda x: x
1000 cf.set('non-string', 1, 1)
1001 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001002
Georg Brandl96a60ae2010-07-28 13:13:46 +00001003class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1004 delimiters = (':=', '$')
1005 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001006 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001007
Łukasz Langab25a7912010-12-17 01:32:29 +00001008class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001009 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001010 comment_prefixes = ('#', ';', '----')
1011 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001012 empty_lines_in_values = False
1013
1014 def test_reading(self):
1015 smbconf = support.findfile("cfgparser.2")
1016 # check when we pass a mix of readable and non-readable files:
1017 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001018 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001019 self.assertEqual(parsed_files, [smbconf])
1020 sections = ['global', 'homes', 'printers',
1021 'print$', 'pdf-generator', 'tmp', 'Agustin']
1022 self.assertEqual(cf.sections(), sections)
1023 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1024 self.assertEqual(cf.getint("global", "max log size"), 50)
1025 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1026 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001027
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001028class ConfigParserTestCaseExtendedInterpolation(BasicTestCase):
1029 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001030 interpolation = configparser.ExtendedInterpolation()
1031 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001032 strict = True
1033
1034 def fromstring(self, string, defaults=None, optionxform=None):
1035 cf = self.newconfig(defaults)
1036 if optionxform:
1037 cf.optionxform = optionxform
1038 cf.read_string(string)
1039 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001040
1041 def test_extended_interpolation(self):
1042 cf = self.fromstring(textwrap.dedent("""
1043 [common]
1044 favourite Beatle = Paul
1045 favourite color = green
1046
1047 [tom]
1048 favourite band = ${favourite color} day
1049 favourite pope = John ${favourite Beatle} II
1050 sequel = ${favourite pope}I
1051
1052 [ambv]
1053 favourite Beatle = George
1054 son of Edward VII = ${favourite Beatle} V
1055 son of George V = ${son of Edward VII}I
1056
1057 [stanley]
1058 favourite Beatle = ${ambv:favourite Beatle}
1059 favourite pope = ${tom:favourite pope}
1060 favourite color = black
1061 favourite state of mind = paranoid
1062 favourite movie = soylent ${common:favourite color}
1063 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1064 """).strip())
1065
1066 eq = self.assertEqual
1067 eq(cf['common']['favourite Beatle'], 'Paul')
1068 eq(cf['common']['favourite color'], 'green')
1069 eq(cf['tom']['favourite Beatle'], 'Paul')
1070 eq(cf['tom']['favourite color'], 'green')
1071 eq(cf['tom']['favourite band'], 'green day')
1072 eq(cf['tom']['favourite pope'], 'John Paul II')
1073 eq(cf['tom']['sequel'], 'John Paul III')
1074 eq(cf['ambv']['favourite Beatle'], 'George')
1075 eq(cf['ambv']['favourite color'], 'green')
1076 eq(cf['ambv']['son of Edward VII'], 'George V')
1077 eq(cf['ambv']['son of George V'], 'George VI')
1078 eq(cf['stanley']['favourite Beatle'], 'George')
1079 eq(cf['stanley']['favourite color'], 'black')
1080 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1081 eq(cf['stanley']['favourite movie'], 'soylent green')
1082 eq(cf['stanley']['favourite pope'], 'John Paul II')
1083 eq(cf['stanley']['favourite song'],
1084 'black sabbath - paranoid')
1085
1086 def test_endless_loop(self):
1087 cf = self.fromstring(textwrap.dedent("""
1088 [one for you]
1089 ping = ${one for me:pong}
1090
1091 [one for me]
1092 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001093
1094 [selfish]
1095 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001096 """).strip())
1097
1098 with self.assertRaises(configparser.InterpolationDepthError):
1099 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001100 with self.assertRaises(configparser.InterpolationDepthError):
1101 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001102
Łukasz Langa71b37a52010-12-17 21:56:32 +00001103 def test_strange_options(self):
1104 cf = self.fromstring("""
1105 [dollars]
1106 $var = $$value
1107 $var2 = ${$var}
1108 ${sick} = cannot interpolate me
1109
1110 [interpolated]
1111 $other = ${dollars:$var}
1112 $trying = ${dollars:${sick}}
1113 """)
1114
1115 self.assertEqual(cf['dollars']['$var'], '$value')
1116 self.assertEqual(cf['interpolated']['$other'], '$value')
1117 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1118 exception_class = configparser.InterpolationMissingOptionError
1119 with self.assertRaises(exception_class) as cm:
1120 cf['interpolated']['$trying']
1121 self.assertEqual(cm.exception.reference, 'dollars:${sick')
1122 self.assertEqual(cm.exception.args[2], '}') #rawval
1123
Łukasz Langae698cd52011-04-28 10:58:57 +02001124 def test_case_sensitivity_basic(self):
1125 ini = textwrap.dedent("""
1126 [common]
1127 optionlower = value
1128 OptionUpper = Value
1129
1130 [Common]
1131 optionlower = a better ${common:optionlower}
1132 OptionUpper = A Better ${common:OptionUpper}
1133
1134 [random]
1135 foolower = ${common:optionlower} redefined
1136 FooUpper = ${Common:OptionUpper} Redefined
1137 """).strip()
1138
1139 cf = self.fromstring(ini)
1140 eq = self.assertEqual
1141 eq(cf['common']['optionlower'], 'value')
1142 eq(cf['common']['OptionUpper'], 'Value')
1143 eq(cf['Common']['optionlower'], 'a better value')
1144 eq(cf['Common']['OptionUpper'], 'A Better Value')
1145 eq(cf['random']['foolower'], 'value redefined')
1146 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1147
1148 def test_case_sensitivity_conflicts(self):
1149 ini = textwrap.dedent("""
1150 [common]
1151 option = value
1152 Option = Value
1153
1154 [Common]
1155 option = a better ${common:option}
1156 Option = A Better ${common:Option}
1157
1158 [random]
1159 foo = ${common:option} redefined
1160 Foo = ${Common:Option} Redefined
1161 """).strip()
1162 with self.assertRaises(configparser.DuplicateOptionError):
1163 cf = self.fromstring(ini)
1164
1165 # raw options
1166 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1167 eq = self.assertEqual
1168 eq(cf['common']['option'], 'value')
1169 eq(cf['common']['Option'], 'Value')
1170 eq(cf['Common']['option'], 'a better value')
1171 eq(cf['Common']['Option'], 'A Better Value')
1172 eq(cf['random']['foo'], 'value redefined')
1173 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001174
1175 def test_other_errors(self):
1176 cf = self.fromstring("""
1177 [interpolation fail]
1178 case1 = ${where's the brace
1179 case2 = ${does_not_exist}
1180 case3 = ${wrong_section:wrong_value}
1181 case4 = ${i:like:colon:characters}
1182 case5 = $100 for Fail No 5!
1183 """)
1184
1185 with self.assertRaises(configparser.InterpolationSyntaxError):
1186 cf['interpolation fail']['case1']
1187 with self.assertRaises(configparser.InterpolationMissingOptionError):
1188 cf['interpolation fail']['case2']
1189 with self.assertRaises(configparser.InterpolationMissingOptionError):
1190 cf['interpolation fail']['case3']
1191 with self.assertRaises(configparser.InterpolationSyntaxError):
1192 cf['interpolation fail']['case4']
1193 with self.assertRaises(configparser.InterpolationSyntaxError):
1194 cf['interpolation fail']['case5']
1195 with self.assertRaises(ValueError):
1196 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001197
1198
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001199class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001200 allow_no_value = True
1201
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001202class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
1203 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001204 delimiters = {'='}
1205 comment_prefixes = {'#'}
1206 allow_no_value = True
1207
1208 def test_cfgparser_dot_3(self):
1209 tricky = support.findfile("cfgparser.3")
1210 cf = self.newconfig()
1211 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1212 self.assertEqual(cf.sections(), ['strange',
1213 'corruption',
1214 'yeah, sections can be '
1215 'indented as well',
1216 'another one!',
1217 'no values here',
1218 'tricky interpolation',
1219 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001220 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001221 vars={'interpolate': '-1'}), -1)
1222 with self.assertRaises(ValueError):
1223 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001224 cf.getint(self.default_section, 'go', raw=True,
1225 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001226 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1227 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1228 longname = 'yeah, sections can be indented as well'
1229 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001230 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001231 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1232 # `go` from DEFAULT
1233 with self.assertRaises(configparser.InterpolationMissingOptionError):
1234 cf.items('no values here')
1235 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1236 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1237 cf.get('tricky interpolation', 'go'))
1238 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1239
1240 def test_unicode_failure(self):
1241 tricky = support.findfile("cfgparser.3")
1242 cf = self.newconfig()
1243 with self.assertRaises(UnicodeDecodeError):
1244 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001245
Fred Drake88444412010-09-03 04:22:36 +00001246
1247class Issue7005TestCase(unittest.TestCase):
1248 """Test output when None is set() as a value and allow_no_value == False.
1249
1250 http://bugs.python.org/issue7005
1251
1252 """
1253
1254 expected_output = "[section]\noption = None\n\n"
1255
1256 def prepare(self, config_class):
1257 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001258 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001259 cp.add_section("section")
1260 cp.set("section", "option", None)
1261 sio = io.StringIO()
1262 cp.write(sio)
1263 return sio.getvalue()
1264
1265 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001266 cp = configparser.ConfigParser(allow_no_value=False)
1267 cp.add_section("section")
1268 with self.assertRaises(TypeError):
1269 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001270
1271 def test_none_as_value_stringified_raw(self):
1272 output = self.prepare(configparser.RawConfigParser)
1273 self.assertEqual(output, self.expected_output)
1274
1275
Thomas Wouters89f507f2006-12-13 04:49:30 +00001276class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001277 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001278
1279 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001280 cf = self.fromstring("[b]\n"
1281 "o4=1\n"
1282 "o3=2\n"
1283 "o2=3\n"
1284 "o1=4\n"
1285 "[a]\n"
1286 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001287 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001288 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001289 self.assertEqual(output.getvalue(),
1290 "[a]\n"
1291 "k = v\n\n"
1292 "[b]\n"
1293 "o1 = 4\n"
1294 "o2 = 3\n"
1295 "o3 = 2\n"
1296 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001297
Fred Drake03c44a32010-02-19 06:08:41 +00001298
Georg Brandl96a60ae2010-07-28 13:13:46 +00001299class CompatibleTestCase(CfgParserTestCaseClass):
1300 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001301 comment_prefixes = '#;'
1302 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001303
1304 def test_comment_handling(self):
1305 config_string = textwrap.dedent("""\
1306 [Commented Bar]
1307 baz=qwe ; a comment
1308 foo: bar # not a comment!
1309 # but this is a comment
1310 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001311 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001312 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001313 """)
1314 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001315 self.assertEqual(cf.get('Commented Bar', 'foo'),
1316 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001317 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001318 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1319 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001320
Łukasz Langa71b37a52010-12-17 21:56:32 +00001321class CopyTestCase(BasicTestCase):
1322 config_class = configparser.ConfigParser
1323
1324 def fromstring(self, string, defaults=None):
1325 cf = self.newconfig(defaults)
1326 cf.read_string(string)
1327 cf_copy = self.newconfig()
1328 cf_copy.read_dict(cf)
1329 # we have to clean up option duplicates that appeared because of
1330 # the magic DEFAULTSECT behaviour.
1331 for section in cf_copy.values():
1332 if section.name == self.default_section:
1333 continue
1334 for default, value in cf[self.default_section].items():
1335 if section[default] == value:
1336 del section[default]
1337 return cf_copy
1338
Łukasz Langadaab1c82011-04-27 18:10:05 +02001339
1340class FakeFile:
1341 def __init__(self):
1342 file_path = support.findfile("cfgparser.1")
1343 with open(file_path) as f:
1344 self.lines = f.readlines()
1345 self.lines.reverse()
1346
1347 def readline(self):
1348 if len(self.lines):
1349 return self.lines.pop()
1350 return ''
1351
1352
1353def readline_generator(f):
1354 """As advised in Doc/library/configparser.rst."""
1355 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001356 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001357 yield line
1358 line = f.readline()
1359
1360
1361class ReadFileTestCase(unittest.TestCase):
1362 def test_file(self):
1363 file_path = support.findfile("cfgparser.1")
1364 parser = configparser.ConfigParser()
1365 with open(file_path) as f:
1366 parser.read_file(f)
Łukasz Langaba702da2011-04-28 12:02:05 +02001367 self.assertIn("Foo Bar", parser)
1368 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001369 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1370
1371 def test_iterable(self):
1372 lines = textwrap.dedent("""
1373 [Foo Bar]
1374 foo=newbar""").strip().split('\n')
1375 parser = configparser.ConfigParser()
1376 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001377 self.assertIn("Foo Bar", parser)
1378 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001379 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1380
1381 def test_readline_generator(self):
1382 """Issue #11670."""
1383 parser = configparser.ConfigParser()
1384 with self.assertRaises(TypeError):
1385 parser.read_file(FakeFile())
1386 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001387 self.assertIn("Foo Bar", parser)
1388 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001389 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1390
1391
Łukasz Langa71b37a52010-12-17 21:56:32 +00001392class CoverageOneHundredTestCase(unittest.TestCase):
1393 """Covers edge cases in the codebase."""
1394
1395 def test_duplicate_option_error(self):
1396 error = configparser.DuplicateOptionError('section', 'option')
1397 self.assertEqual(error.section, 'section')
1398 self.assertEqual(error.option, 'option')
1399 self.assertEqual(error.source, None)
1400 self.assertEqual(error.lineno, None)
1401 self.assertEqual(error.args, ('section', 'option', None, None))
1402 self.assertEqual(str(error), "Option 'option' in section 'section' "
1403 "already exists")
1404
1405 def test_interpolation_depth_error(self):
1406 error = configparser.InterpolationDepthError('option', 'section',
1407 'rawval')
1408 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1409 self.assertEqual(error.option, 'option')
1410 self.assertEqual(error.section, 'section')
1411
1412 def test_parsing_error(self):
1413 with self.assertRaises(ValueError) as cm:
1414 configparser.ParsingError()
1415 self.assertEqual(str(cm.exception), "Required argument `source' not "
1416 "given.")
1417 with self.assertRaises(ValueError) as cm:
1418 configparser.ParsingError(source='source', filename='filename')
1419 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1420 "and `source'. Use `source'.")
1421 error = configparser.ParsingError(filename='source')
1422 self.assertEqual(error.source, 'source')
1423 with warnings.catch_warnings(record=True) as w:
1424 warnings.simplefilter("always", DeprecationWarning)
1425 self.assertEqual(error.filename, 'source')
1426 error.filename = 'filename'
1427 self.assertEqual(error.source, 'filename')
1428 for warning in w:
1429 self.assertTrue(warning.category is DeprecationWarning)
1430
1431 def test_interpolation_validation(self):
1432 parser = configparser.ConfigParser()
1433 parser.read_string("""
1434 [section]
1435 invalid_percent = %
1436 invalid_reference = %(()
1437 invalid_variable = %(does_not_exist)s
1438 """)
1439 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1440 parser['section']['invalid_percent']
1441 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1442 "'(', found: '%'")
1443 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1444 parser['section']['invalid_reference']
1445 self.assertEqual(str(cm.exception), "bad interpolation variable "
1446 "reference '%(()'")
1447
1448 def test_readfp_deprecation(self):
1449 sio = io.StringIO("""
1450 [section]
1451 option = value
1452 """)
1453 parser = configparser.ConfigParser()
1454 with warnings.catch_warnings(record=True) as w:
1455 warnings.simplefilter("always", DeprecationWarning)
1456 parser.readfp(sio, filename='StringIO')
1457 for warning in w:
1458 self.assertTrue(warning.category is DeprecationWarning)
1459 self.assertEqual(len(parser), 2)
1460 self.assertEqual(parser['section']['option'], 'value')
1461
1462 def test_safeconfigparser_deprecation(self):
1463 with warnings.catch_warnings(record=True) as w:
1464 warnings.simplefilter("always", DeprecationWarning)
1465 parser = configparser.SafeConfigParser()
1466 for warning in w:
1467 self.assertTrue(warning.category is DeprecationWarning)
1468
1469 def test_sectionproxy_repr(self):
1470 parser = configparser.ConfigParser()
1471 parser.read_string("""
1472 [section]
1473 key = value
1474 """)
1475 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001476
Łukasz Langae7851952012-01-20 14:57:55 +01001477
1478class ExceptionPicklingTestCase(unittest.TestCase):
1479 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1480
1481 def test_error(self):
1482 import pickle
1483 e1 = configparser.Error('value')
1484 pickled = pickle.dumps(e1)
1485 e2 = pickle.loads(pickled)
1486 self.assertEqual(e1.message, e2.message)
1487 self.assertEqual(repr(e1), repr(e2))
1488
1489 def test_nosectionerror(self):
1490 import pickle
1491 e1 = configparser.NoSectionError('section')
1492 pickled = pickle.dumps(e1)
1493 e2 = pickle.loads(pickled)
1494 self.assertEqual(e1.message, e2.message)
1495 self.assertEqual(e1.args, e2.args)
1496 self.assertEqual(e1.section, e2.section)
1497 self.assertEqual(repr(e1), repr(e2))
1498
1499 def test_nooptionerror(self):
1500 import pickle
1501 e1 = configparser.NoOptionError('option', 'section')
1502 pickled = pickle.dumps(e1)
1503 e2 = pickle.loads(pickled)
1504 self.assertEqual(e1.message, e2.message)
1505 self.assertEqual(e1.args, e2.args)
1506 self.assertEqual(e1.section, e2.section)
1507 self.assertEqual(e1.option, e2.option)
1508 self.assertEqual(repr(e1), repr(e2))
1509
1510 def test_duplicatesectionerror(self):
1511 import pickle
1512 e1 = configparser.DuplicateSectionError('section', 'source', 123)
1513 pickled = pickle.dumps(e1)
1514 e2 = pickle.loads(pickled)
1515 self.assertEqual(e1.message, e2.message)
1516 self.assertEqual(e1.args, e2.args)
1517 self.assertEqual(e1.section, e2.section)
1518 self.assertEqual(e1.source, e2.source)
1519 self.assertEqual(e1.lineno, e2.lineno)
1520 self.assertEqual(repr(e1), repr(e2))
1521
1522 def test_duplicateoptionerror(self):
1523 import pickle
1524 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1525 123)
1526 pickled = pickle.dumps(e1)
1527 e2 = pickle.loads(pickled)
1528 self.assertEqual(e1.message, e2.message)
1529 self.assertEqual(e1.args, e2.args)
1530 self.assertEqual(e1.section, e2.section)
1531 self.assertEqual(e1.option, e2.option)
1532 self.assertEqual(e1.source, e2.source)
1533 self.assertEqual(e1.lineno, e2.lineno)
1534 self.assertEqual(repr(e1), repr(e2))
1535
1536 def test_interpolationerror(self):
1537 import pickle
1538 e1 = configparser.InterpolationError('option', 'section', 'msg')
1539 pickled = pickle.dumps(e1)
1540 e2 = pickle.loads(pickled)
1541 self.assertEqual(e1.message, e2.message)
1542 self.assertEqual(e1.args, e2.args)
1543 self.assertEqual(e1.section, e2.section)
1544 self.assertEqual(e1.option, e2.option)
1545 self.assertEqual(repr(e1), repr(e2))
1546
1547 def test_interpolationmissingoptionerror(self):
1548 import pickle
1549 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1550 'rawval', 'reference')
1551 pickled = pickle.dumps(e1)
1552 e2 = pickle.loads(pickled)
1553 self.assertEqual(e1.message, e2.message)
1554 self.assertEqual(e1.args, e2.args)
1555 self.assertEqual(e1.section, e2.section)
1556 self.assertEqual(e1.option, e2.option)
1557 self.assertEqual(e1.reference, e2.reference)
1558 self.assertEqual(repr(e1), repr(e2))
1559
1560 def test_interpolationsyntaxerror(self):
1561 import pickle
1562 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
1563 pickled = pickle.dumps(e1)
1564 e2 = pickle.loads(pickled)
1565 self.assertEqual(e1.message, e2.message)
1566 self.assertEqual(e1.args, e2.args)
1567 self.assertEqual(e1.section, e2.section)
1568 self.assertEqual(e1.option, e2.option)
1569 self.assertEqual(repr(e1), repr(e2))
1570
1571 def test_interpolationdeptherror(self):
1572 import pickle
1573 e1 = configparser.InterpolationDepthError('option', 'section',
1574 'rawval')
1575 pickled = pickle.dumps(e1)
1576 e2 = pickle.loads(pickled)
1577 self.assertEqual(e1.message, e2.message)
1578 self.assertEqual(e1.args, e2.args)
1579 self.assertEqual(e1.section, e2.section)
1580 self.assertEqual(e1.option, e2.option)
1581 self.assertEqual(repr(e1), repr(e2))
1582
1583 def test_parsingerror(self):
1584 import pickle
1585 e1 = configparser.ParsingError('source')
1586 e1.append(1, 'line1')
1587 e1.append(2, 'line2')
1588 e1.append(3, 'line3')
1589 pickled = pickle.dumps(e1)
1590 e2 = pickle.loads(pickled)
1591 self.assertEqual(e1.message, e2.message)
1592 self.assertEqual(e1.args, e2.args)
1593 self.assertEqual(e1.source, e2.source)
1594 self.assertEqual(e1.errors, e2.errors)
1595 self.assertEqual(repr(e1), repr(e2))
1596 e1 = configparser.ParsingError(filename='filename')
1597 e1.append(1, 'line1')
1598 e1.append(2, 'line2')
1599 e1.append(3, 'line3')
1600 pickled = pickle.dumps(e1)
1601 e2 = pickle.loads(pickled)
1602 self.assertEqual(e1.message, e2.message)
1603 self.assertEqual(e1.args, e2.args)
1604 self.assertEqual(e1.source, e2.source)
1605 self.assertEqual(e1.errors, e2.errors)
1606 self.assertEqual(repr(e1), repr(e2))
1607
1608 def test_missingsectionheadererror(self):
1609 import pickle
1610 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
1611 pickled = pickle.dumps(e1)
1612 e2 = pickle.loads(pickled)
1613 self.assertEqual(e1.message, e2.message)
1614 self.assertEqual(e1.args, e2.args)
1615 self.assertEqual(e1.line, e2.line)
1616 self.assertEqual(e1.source, e2.source)
1617 self.assertEqual(e1.lineno, e2.lineno)
1618 self.assertEqual(repr(e1), repr(e2))
1619
1620
Łukasz Langacba24322012-07-07 18:54:08 +02001621class InlineCommentStrippingTestCase(unittest.TestCase):
1622 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1623 delimiter occurs earlier without preceding space.."""
1624
1625 def test_stripping(self):
1626 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1627 '//'))
1628 cfg.read_string("""
1629 [section]
1630 k1 = v1;still v1
1631 k2 = v2 ;a comment
1632 k3 = v3 ; also a comment
1633 k4 = v4;still v4 ;a comment
1634 k5 = v5;still v5 ; also a comment
1635 k6 = v6;still v6; and still v6 ;a comment
1636 k7 = v7;still v7; and still v7 ; also a comment
1637
1638 [multiprefix]
1639 k1 = v1;still v1 #a comment ; yeah, pretty much
1640 k2 = v2 // this already is a comment ; continued
1641 k3 = v3;#//still v3# and still v3 ; a comment
1642 """)
1643 s = cfg['section']
1644 self.assertEqual(s['k1'], 'v1;still v1')
1645 self.assertEqual(s['k2'], 'v2')
1646 self.assertEqual(s['k3'], 'v3')
1647 self.assertEqual(s['k4'], 'v4;still v4')
1648 self.assertEqual(s['k5'], 'v5;still v5')
1649 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1650 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1651 s = cfg['multiprefix']
1652 self.assertEqual(s['k1'], 'v1;still v1')
1653 self.assertEqual(s['k2'], 'v2')
1654 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1655
1656
Fred Drakec6f28912002-10-25 19:40:49 +00001657def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001658 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001659 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001660 ConfigParserTestCaseNonStandardDelimiters,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001661 ConfigParserTestCaseNoValue,
1662 ConfigParserTestCaseExtendedInterpolation,
1663 ConfigParserTestCaseLegacyInterpolation,
Łukasz Langa1aa422f2011-04-28 17:03:45 +02001664 ConfigParserTestCaseNoInterpolation,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001665 ConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001666 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001667 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001668 RawConfigParserTestCaseNonStandardDelimiters,
1669 RawConfigParserTestSambaConf,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001670 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001671 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001672 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001673 CompatibleTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001674 CopyTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001675 ConfigParserTestCaseNonStandardDefaultSection,
Łukasz Langadaab1c82011-04-27 18:10:05 +02001676 ReadFileTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001677 CoverageOneHundredTestCase,
Łukasz Langae7851952012-01-20 14:57:55 +01001678 ExceptionPicklingTestCase,
Łukasz Langacba24322012-07-07 18:54:08 +02001679 InlineCommentStrippingTestCase,
Fred Drake03c44a32010-02-19 06:08:41 +00001680 )