blob: 03e400df29f64b8990ba8e998634f1d316b79030 [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
867class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
868 config_class = configparser.ConfigParser
869 interpolation = configparser.LegacyInterpolation()
870
871 def test_set_malformatted_interpolation(self):
872 cf = self.fromstring("[sect]\n"
873 "option1{eq}foo\n".format(eq=self.delimiters[0]))
874
875 self.assertEqual(cf.get('sect', "option1"), "foo")
876
877 cf.set("sect", "option1", "%foo")
878 self.assertEqual(cf.get('sect', "option1"), "%foo")
879 cf.set("sect", "option1", "foo%")
880 self.assertEqual(cf.get('sect', "option1"), "foo%")
881 cf.set("sect", "option1", "f%oo")
882 self.assertEqual(cf.get('sect', "option1"), "f%oo")
883
884 # bug #5741: double percents are *not* malformed
885 cf.set("sect", "option2", "foo%%bar")
886 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000887
Georg Brandl96a60ae2010-07-28 13:13:46 +0000888class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
889 delimiters = (':=', '$')
890 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000891 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000892
Łukasz Langac264c092010-11-20 16:15:37 +0000893class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
894 default_section = 'general'
895
Georg Brandl96a60ae2010-07-28 13:13:46 +0000896class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000897 config_class = configparser.ConfigParser
898 wonderful_spam = ("I'm having spam spam spam spam "
899 "spam spam spam beaked beans spam "
900 "spam spam and spam!").replace(' ', '\t\n')
901
902 def setUp(self):
903 cf = self.newconfig()
904 for i in range(100):
905 s = 'section{}'.format(i)
906 cf.add_section(s)
907 for j in range(10):
908 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
909 with open(support.TESTFN, 'w') as f:
910 cf.write(f)
911
912 def tearDown(self):
913 os.unlink(support.TESTFN)
914
915 def test_dominating_multiline_values(self):
916 # We're reading from file because this is where the code changed
917 # during performance updates in Python 3.2
918 cf_from_file = self.newconfig()
919 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +0000920 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000921 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
922 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +0000923
Georg Brandl96a60ae2010-07-28 13:13:46 +0000924class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000925 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000926
927 def test_interpolation(self):
928 cf = self.get_interpolation_config()
929 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000930 eq(cf.get("Foo", "bar"),
931 "something %(with1)s interpolation (1 step)")
932 eq(cf.get("Foo", "bar9"),
933 "something %(with9)s lots of interpolation (9 steps)")
934 eq(cf.get("Foo", "bar10"),
935 "something %(with10)s lots of interpolation (10 steps)")
936 eq(cf.get("Foo", "bar11"),
937 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +0000938
Fred Drake98e3b292002-10-25 20:42:44 +0000939 def test_items(self):
940 self.check_items_config([('default', '<default>'),
941 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000942 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000943 ('name', '%(value)s'),
944 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000945
David Goodger1cbf2062004-10-03 15:55:09 +0000946 def test_set_nonstring_types(self):
947 cf = self.newconfig()
948 cf.add_section('non-string')
949 cf.set('non-string', 'int', 1)
950 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
951 cf.set('non-string', 'dict', {'pi': 3.14159})
952 self.assertEqual(cf.get('non-string', 'int'), 1)
953 self.assertEqual(cf.get('non-string', 'list'),
954 [0, 1, 1, 2, 3, 5, 8, 13])
955 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +0000956 cf.add_section(123)
957 cf.set(123, 'this is sick', True)
958 self.assertEqual(cf.get(123, 'this is sick'), True)
959 if cf._dict.__class__ is configparser._default_dict:
960 # would not work for SortedDict; only checking for the most common
961 # default dictionary (OrderedDict)
962 cf.optionxform = lambda x: x
963 cf.set('non-string', 1, 1)
964 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +0000965
Georg Brandl96a60ae2010-07-28 13:13:46 +0000966class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
967 delimiters = (':=', '$')
968 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000969 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000970
Łukasz Langab25a7912010-12-17 01:32:29 +0000971class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
Georg Brandl96a60ae2010-07-28 13:13:46 +0000972 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +0000973 comment_prefixes = ('#', ';', '----')
974 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +0000975 empty_lines_in_values = False
976
977 def test_reading(self):
978 smbconf = support.findfile("cfgparser.2")
979 # check when we pass a mix of readable and non-readable files:
980 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +0000981 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000982 self.assertEqual(parsed_files, [smbconf])
983 sections = ['global', 'homes', 'printers',
984 'print$', 'pdf-generator', 'tmp', 'Agustin']
985 self.assertEqual(cf.sections(), sections)
986 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
987 self.assertEqual(cf.getint("global", "max log size"), 50)
988 self.assertEqual(cf.get("global", "hosts allow"), "127.")
989 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +0000990
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000991class ConfigParserTestCaseExtendedInterpolation(BasicTestCase):
992 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +0000993 interpolation = configparser.ExtendedInterpolation()
994 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +0200995 strict = True
996
997 def fromstring(self, string, defaults=None, optionxform=None):
998 cf = self.newconfig(defaults)
999 if optionxform:
1000 cf.optionxform = optionxform
1001 cf.read_string(string)
1002 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001003
1004 def test_extended_interpolation(self):
1005 cf = self.fromstring(textwrap.dedent("""
1006 [common]
1007 favourite Beatle = Paul
1008 favourite color = green
1009
1010 [tom]
1011 favourite band = ${favourite color} day
1012 favourite pope = John ${favourite Beatle} II
1013 sequel = ${favourite pope}I
1014
1015 [ambv]
1016 favourite Beatle = George
1017 son of Edward VII = ${favourite Beatle} V
1018 son of George V = ${son of Edward VII}I
1019
1020 [stanley]
1021 favourite Beatle = ${ambv:favourite Beatle}
1022 favourite pope = ${tom:favourite pope}
1023 favourite color = black
1024 favourite state of mind = paranoid
1025 favourite movie = soylent ${common:favourite color}
1026 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1027 """).strip())
1028
1029 eq = self.assertEqual
1030 eq(cf['common']['favourite Beatle'], 'Paul')
1031 eq(cf['common']['favourite color'], 'green')
1032 eq(cf['tom']['favourite Beatle'], 'Paul')
1033 eq(cf['tom']['favourite color'], 'green')
1034 eq(cf['tom']['favourite band'], 'green day')
1035 eq(cf['tom']['favourite pope'], 'John Paul II')
1036 eq(cf['tom']['sequel'], 'John Paul III')
1037 eq(cf['ambv']['favourite Beatle'], 'George')
1038 eq(cf['ambv']['favourite color'], 'green')
1039 eq(cf['ambv']['son of Edward VII'], 'George V')
1040 eq(cf['ambv']['son of George V'], 'George VI')
1041 eq(cf['stanley']['favourite Beatle'], 'George')
1042 eq(cf['stanley']['favourite color'], 'black')
1043 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1044 eq(cf['stanley']['favourite movie'], 'soylent green')
1045 eq(cf['stanley']['favourite pope'], 'John Paul II')
1046 eq(cf['stanley']['favourite song'],
1047 'black sabbath - paranoid')
1048
1049 def test_endless_loop(self):
1050 cf = self.fromstring(textwrap.dedent("""
1051 [one for you]
1052 ping = ${one for me:pong}
1053
1054 [one for me]
1055 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001056
1057 [selfish]
1058 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001059 """).strip())
1060
1061 with self.assertRaises(configparser.InterpolationDepthError):
1062 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001063 with self.assertRaises(configparser.InterpolationDepthError):
1064 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001065
Łukasz Langa71b37a52010-12-17 21:56:32 +00001066 def test_strange_options(self):
1067 cf = self.fromstring("""
1068 [dollars]
1069 $var = $$value
1070 $var2 = ${$var}
1071 ${sick} = cannot interpolate me
1072
1073 [interpolated]
1074 $other = ${dollars:$var}
1075 $trying = ${dollars:${sick}}
1076 """)
1077
1078 self.assertEqual(cf['dollars']['$var'], '$value')
1079 self.assertEqual(cf['interpolated']['$other'], '$value')
1080 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1081 exception_class = configparser.InterpolationMissingOptionError
1082 with self.assertRaises(exception_class) as cm:
1083 cf['interpolated']['$trying']
1084 self.assertEqual(cm.exception.reference, 'dollars:${sick')
1085 self.assertEqual(cm.exception.args[2], '}') #rawval
1086
Łukasz Langae698cd52011-04-28 10:58:57 +02001087 def test_case_sensitivity_basic(self):
1088 ini = textwrap.dedent("""
1089 [common]
1090 optionlower = value
1091 OptionUpper = Value
1092
1093 [Common]
1094 optionlower = a better ${common:optionlower}
1095 OptionUpper = A Better ${common:OptionUpper}
1096
1097 [random]
1098 foolower = ${common:optionlower} redefined
1099 FooUpper = ${Common:OptionUpper} Redefined
1100 """).strip()
1101
1102 cf = self.fromstring(ini)
1103 eq = self.assertEqual
1104 eq(cf['common']['optionlower'], 'value')
1105 eq(cf['common']['OptionUpper'], 'Value')
1106 eq(cf['Common']['optionlower'], 'a better value')
1107 eq(cf['Common']['OptionUpper'], 'A Better Value')
1108 eq(cf['random']['foolower'], 'value redefined')
1109 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1110
1111 def test_case_sensitivity_conflicts(self):
1112 ini = textwrap.dedent("""
1113 [common]
1114 option = value
1115 Option = Value
1116
1117 [Common]
1118 option = a better ${common:option}
1119 Option = A Better ${common:Option}
1120
1121 [random]
1122 foo = ${common:option} redefined
1123 Foo = ${Common:Option} Redefined
1124 """).strip()
1125 with self.assertRaises(configparser.DuplicateOptionError):
1126 cf = self.fromstring(ini)
1127
1128 # raw options
1129 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1130 eq = self.assertEqual
1131 eq(cf['common']['option'], 'value')
1132 eq(cf['common']['Option'], 'Value')
1133 eq(cf['Common']['option'], 'a better value')
1134 eq(cf['Common']['Option'], 'A Better Value')
1135 eq(cf['random']['foo'], 'value redefined')
1136 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001137
1138 def test_other_errors(self):
1139 cf = self.fromstring("""
1140 [interpolation fail]
1141 case1 = ${where's the brace
1142 case2 = ${does_not_exist}
1143 case3 = ${wrong_section:wrong_value}
1144 case4 = ${i:like:colon:characters}
1145 case5 = $100 for Fail No 5!
1146 """)
1147
1148 with self.assertRaises(configparser.InterpolationSyntaxError):
1149 cf['interpolation fail']['case1']
1150 with self.assertRaises(configparser.InterpolationMissingOptionError):
1151 cf['interpolation fail']['case2']
1152 with self.assertRaises(configparser.InterpolationMissingOptionError):
1153 cf['interpolation fail']['case3']
1154 with self.assertRaises(configparser.InterpolationSyntaxError):
1155 cf['interpolation fail']['case4']
1156 with self.assertRaises(configparser.InterpolationSyntaxError):
1157 cf['interpolation fail']['case5']
1158 with self.assertRaises(ValueError):
1159 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001160
1161
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001162class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001163 allow_no_value = True
1164
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001165class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
1166 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001167 delimiters = {'='}
1168 comment_prefixes = {'#'}
1169 allow_no_value = True
1170
1171 def test_cfgparser_dot_3(self):
1172 tricky = support.findfile("cfgparser.3")
1173 cf = self.newconfig()
1174 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1175 self.assertEqual(cf.sections(), ['strange',
1176 'corruption',
1177 'yeah, sections can be '
1178 'indented as well',
1179 'another one!',
1180 'no values here',
1181 'tricky interpolation',
1182 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001183 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001184 vars={'interpolate': '-1'}), -1)
1185 with self.assertRaises(ValueError):
1186 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001187 cf.getint(self.default_section, 'go', raw=True,
1188 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001189 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1190 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1191 longname = 'yeah, sections can be indented as well'
1192 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001193 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001194 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1195 # `go` from DEFAULT
1196 with self.assertRaises(configparser.InterpolationMissingOptionError):
1197 cf.items('no values here')
1198 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1199 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1200 cf.get('tricky interpolation', 'go'))
1201 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1202
1203 def test_unicode_failure(self):
1204 tricky = support.findfile("cfgparser.3")
1205 cf = self.newconfig()
1206 with self.assertRaises(UnicodeDecodeError):
1207 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001208
Fred Drake88444412010-09-03 04:22:36 +00001209
1210class Issue7005TestCase(unittest.TestCase):
1211 """Test output when None is set() as a value and allow_no_value == False.
1212
1213 http://bugs.python.org/issue7005
1214
1215 """
1216
1217 expected_output = "[section]\noption = None\n\n"
1218
1219 def prepare(self, config_class):
1220 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001221 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001222 cp.add_section("section")
1223 cp.set("section", "option", None)
1224 sio = io.StringIO()
1225 cp.write(sio)
1226 return sio.getvalue()
1227
1228 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001229 cp = configparser.ConfigParser(allow_no_value=False)
1230 cp.add_section("section")
1231 with self.assertRaises(TypeError):
1232 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001233
1234 def test_none_as_value_stringified_raw(self):
1235 output = self.prepare(configparser.RawConfigParser)
1236 self.assertEqual(output, self.expected_output)
1237
1238
Thomas Wouters89f507f2006-12-13 04:49:30 +00001239class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001240 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001241
1242 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001243 cf = self.fromstring("[b]\n"
1244 "o4=1\n"
1245 "o3=2\n"
1246 "o2=3\n"
1247 "o1=4\n"
1248 "[a]\n"
1249 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001250 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001251 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001252 self.assertEqual(output.getvalue(),
1253 "[a]\n"
1254 "k = v\n\n"
1255 "[b]\n"
1256 "o1 = 4\n"
1257 "o2 = 3\n"
1258 "o3 = 2\n"
1259 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001260
Fred Drake03c44a32010-02-19 06:08:41 +00001261
Georg Brandl96a60ae2010-07-28 13:13:46 +00001262class CompatibleTestCase(CfgParserTestCaseClass):
1263 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001264 comment_prefixes = '#;'
1265 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001266
1267 def test_comment_handling(self):
1268 config_string = textwrap.dedent("""\
1269 [Commented Bar]
1270 baz=qwe ; a comment
1271 foo: bar # not a comment!
1272 # but this is a comment
1273 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001274 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001275 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001276 """)
1277 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001278 self.assertEqual(cf.get('Commented Bar', 'foo'),
1279 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001280 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001281 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1282 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001283
Łukasz Langa71b37a52010-12-17 21:56:32 +00001284class CopyTestCase(BasicTestCase):
1285 config_class = configparser.ConfigParser
1286
1287 def fromstring(self, string, defaults=None):
1288 cf = self.newconfig(defaults)
1289 cf.read_string(string)
1290 cf_copy = self.newconfig()
1291 cf_copy.read_dict(cf)
1292 # we have to clean up option duplicates that appeared because of
1293 # the magic DEFAULTSECT behaviour.
1294 for section in cf_copy.values():
1295 if section.name == self.default_section:
1296 continue
1297 for default, value in cf[self.default_section].items():
1298 if section[default] == value:
1299 del section[default]
1300 return cf_copy
1301
Łukasz Langadaab1c82011-04-27 18:10:05 +02001302
1303class FakeFile:
1304 def __init__(self):
1305 file_path = support.findfile("cfgparser.1")
1306 with open(file_path) as f:
1307 self.lines = f.readlines()
1308 self.lines.reverse()
1309
1310 def readline(self):
1311 if len(self.lines):
1312 return self.lines.pop()
1313 return ''
1314
1315
1316def readline_generator(f):
1317 """As advised in Doc/library/configparser.rst."""
1318 line = f.readline()
1319 while line != '':
1320 yield line
1321 line = f.readline()
1322
1323
1324class ReadFileTestCase(unittest.TestCase):
1325 def test_file(self):
1326 file_path = support.findfile("cfgparser.1")
1327 parser = configparser.ConfigParser()
1328 with open(file_path) as f:
1329 parser.read_file(f)
1330 self.assertTrue("Foo Bar" in parser)
1331 self.assertTrue("foo" in parser["Foo Bar"])
1332 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1333
1334 def test_iterable(self):
1335 lines = textwrap.dedent("""
1336 [Foo Bar]
1337 foo=newbar""").strip().split('\n')
1338 parser = configparser.ConfigParser()
1339 parser.read_file(lines)
1340 self.assertTrue("Foo Bar" in parser)
1341 self.assertTrue("foo" in parser["Foo Bar"])
1342 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1343
1344 def test_readline_generator(self):
1345 """Issue #11670."""
1346 parser = configparser.ConfigParser()
1347 with self.assertRaises(TypeError):
1348 parser.read_file(FakeFile())
1349 parser.read_file(readline_generator(FakeFile()))
1350 self.assertTrue("Foo Bar" in parser)
1351 self.assertTrue("foo" in parser["Foo Bar"])
1352 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1353
1354
Łukasz Langa71b37a52010-12-17 21:56:32 +00001355class CoverageOneHundredTestCase(unittest.TestCase):
1356 """Covers edge cases in the codebase."""
1357
1358 def test_duplicate_option_error(self):
1359 error = configparser.DuplicateOptionError('section', 'option')
1360 self.assertEqual(error.section, 'section')
1361 self.assertEqual(error.option, 'option')
1362 self.assertEqual(error.source, None)
1363 self.assertEqual(error.lineno, None)
1364 self.assertEqual(error.args, ('section', 'option', None, None))
1365 self.assertEqual(str(error), "Option 'option' in section 'section' "
1366 "already exists")
1367
1368 def test_interpolation_depth_error(self):
1369 error = configparser.InterpolationDepthError('option', 'section',
1370 'rawval')
1371 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1372 self.assertEqual(error.option, 'option')
1373 self.assertEqual(error.section, 'section')
1374
1375 def test_parsing_error(self):
1376 with self.assertRaises(ValueError) as cm:
1377 configparser.ParsingError()
1378 self.assertEqual(str(cm.exception), "Required argument `source' not "
1379 "given.")
1380 with self.assertRaises(ValueError) as cm:
1381 configparser.ParsingError(source='source', filename='filename')
1382 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1383 "and `source'. Use `source'.")
1384 error = configparser.ParsingError(filename='source')
1385 self.assertEqual(error.source, 'source')
1386 with warnings.catch_warnings(record=True) as w:
1387 warnings.simplefilter("always", DeprecationWarning)
1388 self.assertEqual(error.filename, 'source')
1389 error.filename = 'filename'
1390 self.assertEqual(error.source, 'filename')
1391 for warning in w:
1392 self.assertTrue(warning.category is DeprecationWarning)
1393
1394 def test_interpolation_validation(self):
1395 parser = configparser.ConfigParser()
1396 parser.read_string("""
1397 [section]
1398 invalid_percent = %
1399 invalid_reference = %(()
1400 invalid_variable = %(does_not_exist)s
1401 """)
1402 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1403 parser['section']['invalid_percent']
1404 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1405 "'(', found: '%'")
1406 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1407 parser['section']['invalid_reference']
1408 self.assertEqual(str(cm.exception), "bad interpolation variable "
1409 "reference '%(()'")
1410
1411 def test_readfp_deprecation(self):
1412 sio = io.StringIO("""
1413 [section]
1414 option = value
1415 """)
1416 parser = configparser.ConfigParser()
1417 with warnings.catch_warnings(record=True) as w:
1418 warnings.simplefilter("always", DeprecationWarning)
1419 parser.readfp(sio, filename='StringIO')
1420 for warning in w:
1421 self.assertTrue(warning.category is DeprecationWarning)
1422 self.assertEqual(len(parser), 2)
1423 self.assertEqual(parser['section']['option'], 'value')
1424
1425 def test_safeconfigparser_deprecation(self):
1426 with warnings.catch_warnings(record=True) as w:
1427 warnings.simplefilter("always", DeprecationWarning)
1428 parser = configparser.SafeConfigParser()
1429 for warning in w:
1430 self.assertTrue(warning.category is DeprecationWarning)
1431
1432 def test_sectionproxy_repr(self):
1433 parser = configparser.ConfigParser()
1434 parser.read_string("""
1435 [section]
1436 key = value
1437 """)
1438 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001439
Fred Drakec6f28912002-10-25 19:40:49 +00001440def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001441 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001442 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001443 ConfigParserTestCaseNonStandardDelimiters,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001444 ConfigParserTestCaseNoValue,
1445 ConfigParserTestCaseExtendedInterpolation,
1446 ConfigParserTestCaseLegacyInterpolation,
1447 ConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001448 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001449 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001450 RawConfigParserTestCaseNonStandardDelimiters,
1451 RawConfigParserTestSambaConf,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001452 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001453 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001454 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001455 CompatibleTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001456 CopyTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001457 ConfigParserTestCaseNonStandardDefaultSection,
Łukasz Langadaab1c82011-04-27 18:10:05 +02001458 ReadFileTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001459 CoverageOneHundredTestCase,
Fred Drake03c44a32010-02-19 06:08:41 +00001460 )