blob: 3823641f56e53867d9a5d6813646fbac1a9b4061 [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
Łukasz Langa3a8479a2012-12-31 03:38:39 +0100773 def test_popitem(self):
774 cf = self.fromstring("""
775 [section1]
776 name1 {0[0]} value1
777 [section2]
778 name2 {0[0]} value2
779 [section3]
780 name3 {0[0]} value3
781 """.format(self.delimiters), defaults={"default": "<default>"})
782 self.assertEqual(cf.popitem()[0], 'section1')
783 self.assertEqual(cf.popitem()[0], 'section2')
784 self.assertEqual(cf.popitem()[0], 'section3')
785 with self.assertRaises(KeyError):
786 cf.popitem()
787
788 def test_clear(self):
789 cf = self.newconfig({"foo": "Bar"})
790 self.assertEqual(
791 cf.get(self.default_section, "Foo"), "Bar",
792 "could not locate option, expecting case-insensitive option names")
793 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
794 self.assertEqual(cf.sections(), ['zing'])
795 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
796 cf.clear()
797 self.assertEqual(set(cf.sections()), set())
798 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
799
Łukasz Langa02101942012-12-31 13:55:11 +0100800 def test_setitem(self):
801 cf = self.fromstring("""
802 [section1]
803 name1 {0[0]} value1
804 [section2]
805 name2 {0[0]} value2
806 [section3]
807 name3 {0[0]} value3
808 """.format(self.delimiters), defaults={"nameD": "valueD"})
809 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
810 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
811 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
812 self.assertEqual(cf['section1']['name1'], 'value1')
813 self.assertEqual(cf['section2']['name2'], 'value2')
814 self.assertEqual(cf['section3']['name3'], 'value3')
815 cf['section2'] = {'name22': 'value22'}
816 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
817 self.assertEqual(cf['section2']['name22'], 'value22')
818 self.assertNotIn('name2', cf['section2'])
819 cf['section3'] = {}
820 self.assertEqual(set(cf['section3'].keys()), {'named'})
821 self.assertNotIn('name3', cf['section3'])
822 cf[self.default_section] = {}
823 self.assertEqual(set(cf[self.default_section].keys()), set())
824 self.assertEqual(set(cf['section1'].keys()), {'name1'})
825 self.assertEqual(set(cf['section2'].keys()), {'name22'})
826 self.assertEqual(set(cf['section3'].keys()), set())
827
Fred Drake8ef67672000-09-27 22:45:25 +0000828
Fred Drakea4923622010-08-09 12:52:45 +0000829class StrictTestCase(BasicTestCase):
830 config_class = configparser.RawConfigParser
831 strict = True
832
833
Georg Brandl96a60ae2010-07-28 13:13:46 +0000834class ConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +0000835 config_class = configparser.ConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +0000836
837 def test_interpolation(self):
838 cf = self.get_interpolation_config()
839 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +0000840 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
841 eq(cf.get("Foo", "bar9"),
842 "something with lots of interpolation (9 steps)")
843 eq(cf.get("Foo", "bar10"),
844 "something with lots of interpolation (10 steps)")
Fred Drakea4923622010-08-09 12:52:45 +0000845 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000846 if self.interpolation == configparser._UNSET:
847 self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
848 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
849 self.assertEqual(e.args, ("bar11", "Foo",
850 "something %(with11)s lots of interpolation (11 steps)"))
Fred Drake95b96d32001-02-12 17:23:20 +0000851
Fred Drake54782192002-12-31 06:57:25 +0000852 def test_interpolation_missing_value(self):
Fred Drakea4923622010-08-09 12:52:45 +0000853 cf = self.get_interpolation_config()
854 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
Fred Drake54782192002-12-31 06:57:25 +0000855 "Interpolation Error", "name")
856 self.assertEqual(e.reference, "reference")
857 self.assertEqual(e.section, "Interpolation Error")
858 self.assertEqual(e.option, "name")
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000859 if self.interpolation == configparser._UNSET:
860 self.assertEqual(e.args, ('name', 'Interpolation Error',
861 '', 'reference'))
862 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
863 self.assertEqual(e.args, ('name', 'Interpolation Error',
864 '%(reference)s', 'reference'))
Fred Drake54782192002-12-31 06:57:25 +0000865
Fred Drake98e3b292002-10-25 20:42:44 +0000866 def test_items(self):
867 self.check_items_config([('default', '<default>'),
868 ('getdefault', '|<default>|'),
Fred Drake98e3b292002-10-25 20:42:44 +0000869 ('key', '|value|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +0000870 ('name', 'value'),
871 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +0000872
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000873 def test_safe_interpolation(self):
874 # See http://www.python.org/sf/511737
875 cf = self.fromstring("[section]\n"
876 "option1{eq}xxx\n"
877 "option2{eq}%(option1)s/xxx\n"
878 "ok{eq}%(option1)s/%%s\n"
879 "not_ok{eq}%(option2)s/%%s".format(
880 eq=self.delimiters[0]))
881 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
882 if self.interpolation == configparser._UNSET:
883 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
884 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
885 with self.assertRaises(TypeError):
886 cf.get("section", "not_ok")
887
888 def test_set_malformatted_interpolation(self):
889 cf = self.fromstring("[sect]\n"
890 "option1{eq}foo\n".format(eq=self.delimiters[0]))
891
892 self.assertEqual(cf.get('sect', "option1"), "foo")
893
894 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
895 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
896 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
897
898 self.assertEqual(cf.get('sect', "option1"), "foo")
899
900 # bug #5741: double percents are *not* malformed
901 cf.set("sect", "option2", "foo%%bar")
902 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
903
David Goodger1cbf2062004-10-03 15:55:09 +0000904 def test_set_nonstring_types(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000905 cf = self.fromstring("[sect]\n"
906 "option1{eq}foo\n".format(eq=self.delimiters[0]))
907 # Check that we get a TypeError when setting non-string values
908 # in an existing section:
909 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
910 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
911 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
912 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
913 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
914 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
915 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
916 self.assertRaises(TypeError, cf.add_section, 123)
917
918 def test_add_section_default(self):
David Goodger1cbf2062004-10-03 15:55:09 +0000919 cf = self.newconfig()
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000920 self.assertRaises(ValueError, cf.add_section, self.default_section)
921
Łukasz Langa1aa422f2011-04-28 17:03:45 +0200922
923class ConfigParserTestCaseNoInterpolation(BasicTestCase):
924 config_class = configparser.ConfigParser
925 interpolation = None
926 ini = textwrap.dedent("""
927 [numbers]
928 one = 1
929 two = %(one)s * 2
930 three = ${common:one} * 3
931
932 [hexen]
933 sixteen = ${numbers:two} * 8
934 """).strip()
935
936 def assertMatchesIni(self, cf):
937 self.assertEqual(cf['numbers']['one'], '1')
938 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
939 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
940 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
941
942 def test_no_interpolation(self):
943 cf = self.fromstring(self.ini)
944 self.assertMatchesIni(cf)
945
946 def test_empty_case(self):
947 cf = self.newconfig()
948 self.assertIsNone(cf.read_string(""))
949
950 def test_none_as_default_interpolation(self):
951 class CustomConfigParser(configparser.ConfigParser):
952 _DEFAULT_INTERPOLATION = None
953
954 cf = CustomConfigParser()
955 cf.read_string(self.ini)
956 self.assertMatchesIni(cf)
957
958
Łukasz Langa7f64c8a2010-12-16 01:16:22 +0000959class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
960 config_class = configparser.ConfigParser
961 interpolation = configparser.LegacyInterpolation()
962
963 def test_set_malformatted_interpolation(self):
964 cf = self.fromstring("[sect]\n"
965 "option1{eq}foo\n".format(eq=self.delimiters[0]))
966
967 self.assertEqual(cf.get('sect', "option1"), "foo")
968
969 cf.set("sect", "option1", "%foo")
970 self.assertEqual(cf.get('sect', "option1"), "%foo")
971 cf.set("sect", "option1", "foo%")
972 self.assertEqual(cf.get('sect', "option1"), "foo%")
973 cf.set("sect", "option1", "f%oo")
974 self.assertEqual(cf.get('sect', "option1"), "f%oo")
975
976 # bug #5741: double percents are *not* malformed
977 cf.set("sect", "option2", "foo%%bar")
978 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
David Goodger1cbf2062004-10-03 15:55:09 +0000979
Georg Brandl96a60ae2010-07-28 13:13:46 +0000980class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
981 delimiters = (':=', '$')
982 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +0000983 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +0000984
Łukasz Langac264c092010-11-20 16:15:37 +0000985class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
986 default_section = 'general'
987
Georg Brandl96a60ae2010-07-28 13:13:46 +0000988class MultilineValuesTestCase(BasicTestCase):
Brian Curtin9a27b0c2010-07-26 00:27:10 +0000989 config_class = configparser.ConfigParser
990 wonderful_spam = ("I'm having spam spam spam spam "
991 "spam spam spam beaked beans spam "
992 "spam spam and spam!").replace(' ', '\t\n')
993
994 def setUp(self):
995 cf = self.newconfig()
996 for i in range(100):
997 s = 'section{}'.format(i)
998 cf.add_section(s)
999 for j in range(10):
1000 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1001 with open(support.TESTFN, 'w') as f:
1002 cf.write(f)
1003
1004 def tearDown(self):
1005 os.unlink(support.TESTFN)
1006
1007 def test_dominating_multiline_values(self):
1008 # We're reading from file because this is where the code changed
1009 # during performance updates in Python 3.2
1010 cf_from_file = self.newconfig()
1011 with open(support.TESTFN) as f:
Fred Drakea4923622010-08-09 12:52:45 +00001012 cf_from_file.read_file(f)
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001013 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1014 self.wonderful_spam.replace('\t\n', '\n'))
Fred Drake8ef67672000-09-27 22:45:25 +00001015
Georg Brandl96a60ae2010-07-28 13:13:46 +00001016class RawConfigParserTestCase(BasicTestCase):
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +00001017 config_class = configparser.RawConfigParser
Fred Drakec6f28912002-10-25 19:40:49 +00001018
1019 def test_interpolation(self):
1020 cf = self.get_interpolation_config()
1021 eq = self.assertEqual
Fred Drakec6f28912002-10-25 19:40:49 +00001022 eq(cf.get("Foo", "bar"),
1023 "something %(with1)s interpolation (1 step)")
1024 eq(cf.get("Foo", "bar9"),
1025 "something %(with9)s lots of interpolation (9 steps)")
1026 eq(cf.get("Foo", "bar10"),
1027 "something %(with10)s lots of interpolation (10 steps)")
1028 eq(cf.get("Foo", "bar11"),
1029 "something %(with11)s lots of interpolation (11 steps)")
Fred Drake95b96d32001-02-12 17:23:20 +00001030
Fred Drake98e3b292002-10-25 20:42:44 +00001031 def test_items(self):
1032 self.check_items_config([('default', '<default>'),
1033 ('getdefault', '|%(default)s|'),
Fred Drake98e3b292002-10-25 20:42:44 +00001034 ('key', '|%(name)s|'),
Łukasz Langa71b37a52010-12-17 21:56:32 +00001035 ('name', '%(value)s'),
1036 ('value', 'value')])
Fred Drake98e3b292002-10-25 20:42:44 +00001037
David Goodger1cbf2062004-10-03 15:55:09 +00001038 def test_set_nonstring_types(self):
1039 cf = self.newconfig()
1040 cf.add_section('non-string')
1041 cf.set('non-string', 'int', 1)
1042 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1043 cf.set('non-string', 'dict', {'pi': 3.14159})
1044 self.assertEqual(cf.get('non-string', 'int'), 1)
1045 self.assertEqual(cf.get('non-string', 'list'),
1046 [0, 1, 1, 2, 3, 5, 8, 13])
1047 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001048 cf.add_section(123)
1049 cf.set(123, 'this is sick', True)
1050 self.assertEqual(cf.get(123, 'this is sick'), True)
Łukasz Langa4d27d9e2011-04-29 16:15:41 +02001051 if cf._dict is configparser._default_dict:
Łukasz Langa2cf9ddb2010-12-04 12:46:01 +00001052 # would not work for SortedDict; only checking for the most common
1053 # default dictionary (OrderedDict)
1054 cf.optionxform = lambda x: x
1055 cf.set('non-string', 1, 1)
1056 self.assertEqual(cf.get('non-string', 1), 1)
Tim Petersab9b32c2004-10-03 18:35:19 +00001057
Georg Brandl96a60ae2010-07-28 13:13:46 +00001058class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1059 delimiters = (':=', '$')
1060 comment_prefixes = ('//', '"')
Łukasz Langab25a7912010-12-17 01:32:29 +00001061 inline_comment_prefixes = ('//', '"')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001062
Łukasz Langab25a7912010-12-17 01:32:29 +00001063class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001064 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001065 comment_prefixes = ('#', ';', '----')
1066 inline_comment_prefixes = ('//',)
Georg Brandl96a60ae2010-07-28 13:13:46 +00001067 empty_lines_in_values = False
1068
1069 def test_reading(self):
1070 smbconf = support.findfile("cfgparser.2")
1071 # check when we pass a mix of readable and non-readable files:
1072 cf = self.newconfig()
Georg Brandl8dcaa732010-07-29 12:17:40 +00001073 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001074 self.assertEqual(parsed_files, [smbconf])
1075 sections = ['global', 'homes', 'printers',
1076 'print$', 'pdf-generator', 'tmp', 'Agustin']
1077 self.assertEqual(cf.sections(), sections)
1078 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1079 self.assertEqual(cf.getint("global", "max log size"), 50)
1080 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1081 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
Fred Drake8ef67672000-09-27 22:45:25 +00001082
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001083class ConfigParserTestCaseExtendedInterpolation(BasicTestCase):
1084 config_class = configparser.ConfigParser
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001085 interpolation = configparser.ExtendedInterpolation()
1086 default_section = 'common'
Łukasz Langae698cd52011-04-28 10:58:57 +02001087 strict = True
1088
1089 def fromstring(self, string, defaults=None, optionxform=None):
1090 cf = self.newconfig(defaults)
1091 if optionxform:
1092 cf.optionxform = optionxform
1093 cf.read_string(string)
1094 return cf
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001095
1096 def test_extended_interpolation(self):
1097 cf = self.fromstring(textwrap.dedent("""
1098 [common]
1099 favourite Beatle = Paul
1100 favourite color = green
1101
1102 [tom]
1103 favourite band = ${favourite color} day
1104 favourite pope = John ${favourite Beatle} II
1105 sequel = ${favourite pope}I
1106
1107 [ambv]
1108 favourite Beatle = George
1109 son of Edward VII = ${favourite Beatle} V
1110 son of George V = ${son of Edward VII}I
1111
1112 [stanley]
1113 favourite Beatle = ${ambv:favourite Beatle}
1114 favourite pope = ${tom:favourite pope}
1115 favourite color = black
1116 favourite state of mind = paranoid
1117 favourite movie = soylent ${common:favourite color}
1118 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1119 """).strip())
1120
1121 eq = self.assertEqual
1122 eq(cf['common']['favourite Beatle'], 'Paul')
1123 eq(cf['common']['favourite color'], 'green')
1124 eq(cf['tom']['favourite Beatle'], 'Paul')
1125 eq(cf['tom']['favourite color'], 'green')
1126 eq(cf['tom']['favourite band'], 'green day')
1127 eq(cf['tom']['favourite pope'], 'John Paul II')
1128 eq(cf['tom']['sequel'], 'John Paul III')
1129 eq(cf['ambv']['favourite Beatle'], 'George')
1130 eq(cf['ambv']['favourite color'], 'green')
1131 eq(cf['ambv']['son of Edward VII'], 'George V')
1132 eq(cf['ambv']['son of George V'], 'George VI')
1133 eq(cf['stanley']['favourite Beatle'], 'George')
1134 eq(cf['stanley']['favourite color'], 'black')
1135 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1136 eq(cf['stanley']['favourite movie'], 'soylent green')
1137 eq(cf['stanley']['favourite pope'], 'John Paul II')
1138 eq(cf['stanley']['favourite song'],
1139 'black sabbath - paranoid')
1140
1141 def test_endless_loop(self):
1142 cf = self.fromstring(textwrap.dedent("""
1143 [one for you]
1144 ping = ${one for me:pong}
1145
1146 [one for me]
1147 pong = ${one for you:ping}
Łukasz Langa71b37a52010-12-17 21:56:32 +00001148
1149 [selfish]
1150 me = ${me}
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001151 """).strip())
1152
1153 with self.assertRaises(configparser.InterpolationDepthError):
1154 cf['one for you']['ping']
Łukasz Langa71b37a52010-12-17 21:56:32 +00001155 with self.assertRaises(configparser.InterpolationDepthError):
1156 cf['selfish']['me']
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001157
Łukasz Langa71b37a52010-12-17 21:56:32 +00001158 def test_strange_options(self):
1159 cf = self.fromstring("""
1160 [dollars]
1161 $var = $$value
1162 $var2 = ${$var}
1163 ${sick} = cannot interpolate me
1164
1165 [interpolated]
1166 $other = ${dollars:$var}
1167 $trying = ${dollars:${sick}}
1168 """)
1169
1170 self.assertEqual(cf['dollars']['$var'], '$value')
1171 self.assertEqual(cf['interpolated']['$other'], '$value')
1172 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1173 exception_class = configparser.InterpolationMissingOptionError
1174 with self.assertRaises(exception_class) as cm:
1175 cf['interpolated']['$trying']
1176 self.assertEqual(cm.exception.reference, 'dollars:${sick')
1177 self.assertEqual(cm.exception.args[2], '}') #rawval
1178
Łukasz Langae698cd52011-04-28 10:58:57 +02001179 def test_case_sensitivity_basic(self):
1180 ini = textwrap.dedent("""
1181 [common]
1182 optionlower = value
1183 OptionUpper = Value
1184
1185 [Common]
1186 optionlower = a better ${common:optionlower}
1187 OptionUpper = A Better ${common:OptionUpper}
1188
1189 [random]
1190 foolower = ${common:optionlower} redefined
1191 FooUpper = ${Common:OptionUpper} Redefined
1192 """).strip()
1193
1194 cf = self.fromstring(ini)
1195 eq = self.assertEqual
1196 eq(cf['common']['optionlower'], 'value')
1197 eq(cf['common']['OptionUpper'], 'Value')
1198 eq(cf['Common']['optionlower'], 'a better value')
1199 eq(cf['Common']['OptionUpper'], 'A Better Value')
1200 eq(cf['random']['foolower'], 'value redefined')
1201 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1202
1203 def test_case_sensitivity_conflicts(self):
1204 ini = textwrap.dedent("""
1205 [common]
1206 option = value
1207 Option = Value
1208
1209 [Common]
1210 option = a better ${common:option}
1211 Option = A Better ${common:Option}
1212
1213 [random]
1214 foo = ${common:option} redefined
1215 Foo = ${Common:Option} Redefined
1216 """).strip()
1217 with self.assertRaises(configparser.DuplicateOptionError):
1218 cf = self.fromstring(ini)
1219
1220 # raw options
1221 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1222 eq = self.assertEqual
1223 eq(cf['common']['option'], 'value')
1224 eq(cf['common']['Option'], 'Value')
1225 eq(cf['Common']['option'], 'a better value')
1226 eq(cf['Common']['Option'], 'A Better Value')
1227 eq(cf['random']['foo'], 'value redefined')
1228 eq(cf['random']['Foo'], 'A Better Value Redefined')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001229
1230 def test_other_errors(self):
1231 cf = self.fromstring("""
1232 [interpolation fail]
1233 case1 = ${where's the brace
1234 case2 = ${does_not_exist}
1235 case3 = ${wrong_section:wrong_value}
1236 case4 = ${i:like:colon:characters}
1237 case5 = $100 for Fail No 5!
1238 """)
1239
1240 with self.assertRaises(configparser.InterpolationSyntaxError):
1241 cf['interpolation fail']['case1']
1242 with self.assertRaises(configparser.InterpolationMissingOptionError):
1243 cf['interpolation fail']['case2']
1244 with self.assertRaises(configparser.InterpolationMissingOptionError):
1245 cf['interpolation fail']['case3']
1246 with self.assertRaises(configparser.InterpolationSyntaxError):
1247 cf['interpolation fail']['case4']
1248 with self.assertRaises(configparser.InterpolationSyntaxError):
1249 cf['interpolation fail']['case5']
1250 with self.assertRaises(ValueError):
1251 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
Łukasz Langab6a6f5f2010-12-03 16:28:00 +00001252
1253
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001254class ConfigParserTestCaseNoValue(ConfigParserTestCase):
Fred Drake03c44a32010-02-19 06:08:41 +00001255 allow_no_value = True
1256
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001257class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass):
1258 config_class = configparser.ConfigParser
Georg Brandl8dcaa732010-07-29 12:17:40 +00001259 delimiters = {'='}
1260 comment_prefixes = {'#'}
1261 allow_no_value = True
1262
1263 def test_cfgparser_dot_3(self):
1264 tricky = support.findfile("cfgparser.3")
1265 cf = self.newconfig()
1266 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1267 self.assertEqual(cf.sections(), ['strange',
1268 'corruption',
1269 'yeah, sections can be '
1270 'indented as well',
1271 'another one!',
1272 'no values here',
1273 'tricky interpolation',
1274 'more interpolation'])
Łukasz Langac264c092010-11-20 16:15:37 +00001275 self.assertEqual(cf.getint(self.default_section, 'go',
Fred Drakecc645b92010-09-04 04:35:34 +00001276 vars={'interpolate': '-1'}), -1)
1277 with self.assertRaises(ValueError):
1278 # no interpolation will happen
Łukasz Langac264c092010-11-20 16:15:37 +00001279 cf.getint(self.default_section, 'go', raw=True,
1280 vars={'interpolate': '-1'})
Georg Brandl8dcaa732010-07-29 12:17:40 +00001281 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1282 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1283 longname = 'yeah, sections can be indented as well'
1284 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
Ezio Melottib3aedd42010-11-20 19:04:17 +00001285 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
Georg Brandl8dcaa732010-07-29 12:17:40 +00001286 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1287 # `go` from DEFAULT
1288 with self.assertRaises(configparser.InterpolationMissingOptionError):
1289 cf.items('no values here')
1290 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1291 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1292 cf.get('tricky interpolation', 'go'))
1293 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1294
1295 def test_unicode_failure(self):
1296 tricky = support.findfile("cfgparser.3")
1297 cf = self.newconfig()
1298 with self.assertRaises(UnicodeDecodeError):
1299 cf.read(tricky, encoding='ascii')
Fred Drake03c44a32010-02-19 06:08:41 +00001300
Fred Drake88444412010-09-03 04:22:36 +00001301
1302class Issue7005TestCase(unittest.TestCase):
1303 """Test output when None is set() as a value and allow_no_value == False.
1304
1305 http://bugs.python.org/issue7005
1306
1307 """
1308
1309 expected_output = "[section]\noption = None\n\n"
1310
1311 def prepare(self, config_class):
1312 # This is the default, but that's the point.
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001313 cp = config_class(allow_no_value=False)
Fred Drake88444412010-09-03 04:22:36 +00001314 cp.add_section("section")
1315 cp.set("section", "option", None)
1316 sio = io.StringIO()
1317 cp.write(sio)
1318 return sio.getvalue()
1319
1320 def test_none_as_value_stringified(self):
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001321 cp = configparser.ConfigParser(allow_no_value=False)
1322 cp.add_section("section")
1323 with self.assertRaises(TypeError):
1324 cp.set("section", "option", None)
Fred Drake88444412010-09-03 04:22:36 +00001325
1326 def test_none_as_value_stringified_raw(self):
1327 output = self.prepare(configparser.RawConfigParser)
1328 self.assertEqual(output, self.expected_output)
1329
1330
Thomas Wouters89f507f2006-12-13 04:49:30 +00001331class SortedTestCase(RawConfigParserTestCase):
Georg Brandl96a60ae2010-07-28 13:13:46 +00001332 dict_type = SortedDict
Thomas Wouters89f507f2006-12-13 04:49:30 +00001333
1334 def test_sorted(self):
Fred Drakea4923622010-08-09 12:52:45 +00001335 cf = self.fromstring("[b]\n"
1336 "o4=1\n"
1337 "o3=2\n"
1338 "o2=3\n"
1339 "o1=4\n"
1340 "[a]\n"
1341 "k=v\n")
Guido van Rossum34d19282007-08-09 01:03:29 +00001342 output = io.StringIO()
Fred Drakea4923622010-08-09 12:52:45 +00001343 cf.write(output)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001344 self.assertEqual(output.getvalue(),
1345 "[a]\n"
1346 "k = v\n\n"
1347 "[b]\n"
1348 "o1 = 4\n"
1349 "o2 = 3\n"
1350 "o3 = 2\n"
1351 "o4 = 1\n\n")
Fred Drake0eebd5c2002-10-25 21:52:00 +00001352
Fred Drake03c44a32010-02-19 06:08:41 +00001353
Georg Brandl96a60ae2010-07-28 13:13:46 +00001354class CompatibleTestCase(CfgParserTestCaseClass):
1355 config_class = configparser.RawConfigParser
Łukasz Langab25a7912010-12-17 01:32:29 +00001356 comment_prefixes = '#;'
1357 inline_comment_prefixes = ';'
Georg Brandl96a60ae2010-07-28 13:13:46 +00001358
1359 def test_comment_handling(self):
1360 config_string = textwrap.dedent("""\
1361 [Commented Bar]
1362 baz=qwe ; a comment
1363 foo: bar # not a comment!
1364 # but this is a comment
1365 ; another comment
Georg Brandl8dcaa732010-07-29 12:17:40 +00001366 quirk: this;is not a comment
Georg Brandl7b280e92010-07-31 20:13:44 +00001367 ; a space must precede an inline comment
Georg Brandl96a60ae2010-07-28 13:13:46 +00001368 """)
1369 cf = self.fromstring(config_string)
Łukasz Langa71b37a52010-12-17 21:56:32 +00001370 self.assertEqual(cf.get('Commented Bar', 'foo'),
1371 'bar # not a comment!')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001372 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
Łukasz Langa71b37a52010-12-17 21:56:32 +00001373 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1374 'this;is not a comment')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001375
Łukasz Langa71b37a52010-12-17 21:56:32 +00001376class CopyTestCase(BasicTestCase):
1377 config_class = configparser.ConfigParser
1378
1379 def fromstring(self, string, defaults=None):
1380 cf = self.newconfig(defaults)
1381 cf.read_string(string)
1382 cf_copy = self.newconfig()
1383 cf_copy.read_dict(cf)
1384 # we have to clean up option duplicates that appeared because of
1385 # the magic DEFAULTSECT behaviour.
1386 for section in cf_copy.values():
1387 if section.name == self.default_section:
1388 continue
1389 for default, value in cf[self.default_section].items():
1390 if section[default] == value:
1391 del section[default]
1392 return cf_copy
1393
Łukasz Langadaab1c82011-04-27 18:10:05 +02001394
1395class FakeFile:
1396 def __init__(self):
1397 file_path = support.findfile("cfgparser.1")
1398 with open(file_path) as f:
1399 self.lines = f.readlines()
1400 self.lines.reverse()
1401
1402 def readline(self):
1403 if len(self.lines):
1404 return self.lines.pop()
1405 return ''
1406
1407
1408def readline_generator(f):
1409 """As advised in Doc/library/configparser.rst."""
1410 line = f.readline()
Łukasz Langaba702da2011-04-28 12:02:05 +02001411 while line:
Łukasz Langadaab1c82011-04-27 18:10:05 +02001412 yield line
1413 line = f.readline()
1414
1415
1416class ReadFileTestCase(unittest.TestCase):
1417 def test_file(self):
1418 file_path = support.findfile("cfgparser.1")
1419 parser = configparser.ConfigParser()
1420 with open(file_path) as f:
1421 parser.read_file(f)
Łukasz Langaba702da2011-04-28 12:02:05 +02001422 self.assertIn("Foo Bar", parser)
1423 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001424 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1425
1426 def test_iterable(self):
1427 lines = textwrap.dedent("""
1428 [Foo Bar]
1429 foo=newbar""").strip().split('\n')
1430 parser = configparser.ConfigParser()
1431 parser.read_file(lines)
Łukasz Langaba702da2011-04-28 12:02:05 +02001432 self.assertIn("Foo Bar", parser)
1433 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001434 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1435
1436 def test_readline_generator(self):
1437 """Issue #11670."""
1438 parser = configparser.ConfigParser()
1439 with self.assertRaises(TypeError):
1440 parser.read_file(FakeFile())
1441 parser.read_file(readline_generator(FakeFile()))
Łukasz Langaba702da2011-04-28 12:02:05 +02001442 self.assertIn("Foo Bar", parser)
1443 self.assertIn("foo", parser["Foo Bar"])
Łukasz Langadaab1c82011-04-27 18:10:05 +02001444 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1445
1446
Łukasz Langa71b37a52010-12-17 21:56:32 +00001447class CoverageOneHundredTestCase(unittest.TestCase):
1448 """Covers edge cases in the codebase."""
1449
1450 def test_duplicate_option_error(self):
1451 error = configparser.DuplicateOptionError('section', 'option')
1452 self.assertEqual(error.section, 'section')
1453 self.assertEqual(error.option, 'option')
1454 self.assertEqual(error.source, None)
1455 self.assertEqual(error.lineno, None)
1456 self.assertEqual(error.args, ('section', 'option', None, None))
1457 self.assertEqual(str(error), "Option 'option' in section 'section' "
1458 "already exists")
1459
1460 def test_interpolation_depth_error(self):
1461 error = configparser.InterpolationDepthError('option', 'section',
1462 'rawval')
1463 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1464 self.assertEqual(error.option, 'option')
1465 self.assertEqual(error.section, 'section')
1466
1467 def test_parsing_error(self):
1468 with self.assertRaises(ValueError) as cm:
1469 configparser.ParsingError()
1470 self.assertEqual(str(cm.exception), "Required argument `source' not "
1471 "given.")
1472 with self.assertRaises(ValueError) as cm:
1473 configparser.ParsingError(source='source', filename='filename')
1474 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1475 "and `source'. Use `source'.")
1476 error = configparser.ParsingError(filename='source')
1477 self.assertEqual(error.source, 'source')
1478 with warnings.catch_warnings(record=True) as w:
1479 warnings.simplefilter("always", DeprecationWarning)
1480 self.assertEqual(error.filename, 'source')
1481 error.filename = 'filename'
1482 self.assertEqual(error.source, 'filename')
1483 for warning in w:
1484 self.assertTrue(warning.category is DeprecationWarning)
1485
1486 def test_interpolation_validation(self):
1487 parser = configparser.ConfigParser()
1488 parser.read_string("""
1489 [section]
1490 invalid_percent = %
1491 invalid_reference = %(()
1492 invalid_variable = %(does_not_exist)s
1493 """)
1494 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1495 parser['section']['invalid_percent']
1496 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1497 "'(', found: '%'")
1498 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1499 parser['section']['invalid_reference']
1500 self.assertEqual(str(cm.exception), "bad interpolation variable "
1501 "reference '%(()'")
1502
1503 def test_readfp_deprecation(self):
1504 sio = io.StringIO("""
1505 [section]
1506 option = value
1507 """)
1508 parser = configparser.ConfigParser()
1509 with warnings.catch_warnings(record=True) as w:
1510 warnings.simplefilter("always", DeprecationWarning)
1511 parser.readfp(sio, filename='StringIO')
1512 for warning in w:
1513 self.assertTrue(warning.category is DeprecationWarning)
1514 self.assertEqual(len(parser), 2)
1515 self.assertEqual(parser['section']['option'], 'value')
1516
1517 def test_safeconfigparser_deprecation(self):
1518 with warnings.catch_warnings(record=True) as w:
1519 warnings.simplefilter("always", DeprecationWarning)
1520 parser = configparser.SafeConfigParser()
1521 for warning in w:
1522 self.assertTrue(warning.category is DeprecationWarning)
1523
1524 def test_sectionproxy_repr(self):
1525 parser = configparser.ConfigParser()
1526 parser.read_string("""
1527 [section]
1528 key = value
1529 """)
1530 self.assertEqual(repr(parser['section']), '<Section: section>')
Georg Brandl96a60ae2010-07-28 13:13:46 +00001531
Łukasz Langae7851952012-01-20 14:57:55 +01001532
1533class ExceptionPicklingTestCase(unittest.TestCase):
1534 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1535
1536 def test_error(self):
1537 import pickle
1538 e1 = configparser.Error('value')
1539 pickled = pickle.dumps(e1)
1540 e2 = pickle.loads(pickled)
1541 self.assertEqual(e1.message, e2.message)
1542 self.assertEqual(repr(e1), repr(e2))
1543
1544 def test_nosectionerror(self):
1545 import pickle
1546 e1 = configparser.NoSectionError('section')
1547 pickled = pickle.dumps(e1)
1548 e2 = pickle.loads(pickled)
1549 self.assertEqual(e1.message, e2.message)
1550 self.assertEqual(e1.args, e2.args)
1551 self.assertEqual(e1.section, e2.section)
1552 self.assertEqual(repr(e1), repr(e2))
1553
1554 def test_nooptionerror(self):
1555 import pickle
1556 e1 = configparser.NoOptionError('option', 'section')
1557 pickled = pickle.dumps(e1)
1558 e2 = pickle.loads(pickled)
1559 self.assertEqual(e1.message, e2.message)
1560 self.assertEqual(e1.args, e2.args)
1561 self.assertEqual(e1.section, e2.section)
1562 self.assertEqual(e1.option, e2.option)
1563 self.assertEqual(repr(e1), repr(e2))
1564
1565 def test_duplicatesectionerror(self):
1566 import pickle
1567 e1 = configparser.DuplicateSectionError('section', 'source', 123)
1568 pickled = pickle.dumps(e1)
1569 e2 = pickle.loads(pickled)
1570 self.assertEqual(e1.message, e2.message)
1571 self.assertEqual(e1.args, e2.args)
1572 self.assertEqual(e1.section, e2.section)
1573 self.assertEqual(e1.source, e2.source)
1574 self.assertEqual(e1.lineno, e2.lineno)
1575 self.assertEqual(repr(e1), repr(e2))
1576
1577 def test_duplicateoptionerror(self):
1578 import pickle
1579 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1580 123)
1581 pickled = pickle.dumps(e1)
1582 e2 = pickle.loads(pickled)
1583 self.assertEqual(e1.message, e2.message)
1584 self.assertEqual(e1.args, e2.args)
1585 self.assertEqual(e1.section, e2.section)
1586 self.assertEqual(e1.option, e2.option)
1587 self.assertEqual(e1.source, e2.source)
1588 self.assertEqual(e1.lineno, e2.lineno)
1589 self.assertEqual(repr(e1), repr(e2))
1590
1591 def test_interpolationerror(self):
1592 import pickle
1593 e1 = configparser.InterpolationError('option', 'section', 'msg')
1594 pickled = pickle.dumps(e1)
1595 e2 = pickle.loads(pickled)
1596 self.assertEqual(e1.message, e2.message)
1597 self.assertEqual(e1.args, e2.args)
1598 self.assertEqual(e1.section, e2.section)
1599 self.assertEqual(e1.option, e2.option)
1600 self.assertEqual(repr(e1), repr(e2))
1601
1602 def test_interpolationmissingoptionerror(self):
1603 import pickle
1604 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1605 'rawval', 'reference')
1606 pickled = pickle.dumps(e1)
1607 e2 = pickle.loads(pickled)
1608 self.assertEqual(e1.message, e2.message)
1609 self.assertEqual(e1.args, e2.args)
1610 self.assertEqual(e1.section, e2.section)
1611 self.assertEqual(e1.option, e2.option)
1612 self.assertEqual(e1.reference, e2.reference)
1613 self.assertEqual(repr(e1), repr(e2))
1614
1615 def test_interpolationsyntaxerror(self):
1616 import pickle
1617 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
1618 pickled = pickle.dumps(e1)
1619 e2 = pickle.loads(pickled)
1620 self.assertEqual(e1.message, e2.message)
1621 self.assertEqual(e1.args, e2.args)
1622 self.assertEqual(e1.section, e2.section)
1623 self.assertEqual(e1.option, e2.option)
1624 self.assertEqual(repr(e1), repr(e2))
1625
1626 def test_interpolationdeptherror(self):
1627 import pickle
1628 e1 = configparser.InterpolationDepthError('option', 'section',
1629 'rawval')
1630 pickled = pickle.dumps(e1)
1631 e2 = pickle.loads(pickled)
1632 self.assertEqual(e1.message, e2.message)
1633 self.assertEqual(e1.args, e2.args)
1634 self.assertEqual(e1.section, e2.section)
1635 self.assertEqual(e1.option, e2.option)
1636 self.assertEqual(repr(e1), repr(e2))
1637
1638 def test_parsingerror(self):
1639 import pickle
1640 e1 = configparser.ParsingError('source')
1641 e1.append(1, 'line1')
1642 e1.append(2, 'line2')
1643 e1.append(3, 'line3')
1644 pickled = pickle.dumps(e1)
1645 e2 = pickle.loads(pickled)
1646 self.assertEqual(e1.message, e2.message)
1647 self.assertEqual(e1.args, e2.args)
1648 self.assertEqual(e1.source, e2.source)
1649 self.assertEqual(e1.errors, e2.errors)
1650 self.assertEqual(repr(e1), repr(e2))
1651 e1 = configparser.ParsingError(filename='filename')
1652 e1.append(1, 'line1')
1653 e1.append(2, 'line2')
1654 e1.append(3, 'line3')
1655 pickled = pickle.dumps(e1)
1656 e2 = pickle.loads(pickled)
1657 self.assertEqual(e1.message, e2.message)
1658 self.assertEqual(e1.args, e2.args)
1659 self.assertEqual(e1.source, e2.source)
1660 self.assertEqual(e1.errors, e2.errors)
1661 self.assertEqual(repr(e1), repr(e2))
1662
1663 def test_missingsectionheadererror(self):
1664 import pickle
1665 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
1666 pickled = pickle.dumps(e1)
1667 e2 = pickle.loads(pickled)
1668 self.assertEqual(e1.message, e2.message)
1669 self.assertEqual(e1.args, e2.args)
1670 self.assertEqual(e1.line, e2.line)
1671 self.assertEqual(e1.source, e2.source)
1672 self.assertEqual(e1.lineno, e2.lineno)
1673 self.assertEqual(repr(e1), repr(e2))
1674
1675
Łukasz Langacba24322012-07-07 18:54:08 +02001676class InlineCommentStrippingTestCase(unittest.TestCase):
1677 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1678 delimiter occurs earlier without preceding space.."""
1679
1680 def test_stripping(self):
1681 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1682 '//'))
1683 cfg.read_string("""
1684 [section]
1685 k1 = v1;still v1
1686 k2 = v2 ;a comment
1687 k3 = v3 ; also a comment
1688 k4 = v4;still v4 ;a comment
1689 k5 = v5;still v5 ; also a comment
1690 k6 = v6;still v6; and still v6 ;a comment
1691 k7 = v7;still v7; and still v7 ; also a comment
1692
1693 [multiprefix]
1694 k1 = v1;still v1 #a comment ; yeah, pretty much
1695 k2 = v2 // this already is a comment ; continued
1696 k3 = v3;#//still v3# and still v3 ; a comment
1697 """)
1698 s = cfg['section']
1699 self.assertEqual(s['k1'], 'v1;still v1')
1700 self.assertEqual(s['k2'], 'v2')
1701 self.assertEqual(s['k3'], 'v3')
1702 self.assertEqual(s['k4'], 'v4;still v4')
1703 self.assertEqual(s['k5'], 'v5;still v5')
1704 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1705 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1706 s = cfg['multiprefix']
1707 self.assertEqual(s['k1'], 'v1;still v1')
1708 self.assertEqual(s['k2'], 'v2')
1709 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1710
1711
Fred Drakec6f28912002-10-25 19:40:49 +00001712def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001713 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +00001714 ConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001715 ConfigParserTestCaseNonStandardDelimiters,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001716 ConfigParserTestCaseNoValue,
1717 ConfigParserTestCaseExtendedInterpolation,
1718 ConfigParserTestCaseLegacyInterpolation,
Łukasz Langa1aa422f2011-04-28 17:03:45 +02001719 ConfigParserTestCaseNoInterpolation,
Łukasz Langa7f64c8a2010-12-16 01:16:22 +00001720 ConfigParserTestCaseTrickyFile,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001721 MultilineValuesTestCase,
Walter Dörwald21d3a322003-05-01 17:45:56 +00001722 RawConfigParserTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001723 RawConfigParserTestCaseNonStandardDelimiters,
1724 RawConfigParserTestSambaConf,
Brian Curtin9a27b0c2010-07-26 00:27:10 +00001725 SortedTestCase,
Fred Drake88444412010-09-03 04:22:36 +00001726 Issue7005TestCase,
Fred Drakea4923622010-08-09 12:52:45 +00001727 StrictTestCase,
Georg Brandl96a60ae2010-07-28 13:13:46 +00001728 CompatibleTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001729 CopyTestCase,
Łukasz Langac264c092010-11-20 16:15:37 +00001730 ConfigParserTestCaseNonStandardDefaultSection,
Łukasz Langadaab1c82011-04-27 18:10:05 +02001731 ReadFileTestCase,
Łukasz Langa71b37a52010-12-17 21:56:32 +00001732 CoverageOneHundredTestCase,
Łukasz Langae7851952012-01-20 14:57:55 +01001733 ExceptionPicklingTestCase,
Łukasz Langacba24322012-07-07 18:54:08 +02001734 InlineCommentStrippingTestCase,
Fred Drake03c44a32010-02-19 06:08:41 +00001735 )