1 import collections
2 import configparser
3 import io
4 import os
5 import pathlib
6 import textwrap
7 import unittest
8 import warnings
9
10 from test import support
11 from test.support import os_helper
12
13
14 class ESC[4;38;5;81mSortedDict(ESC[4;38;5;149mcollectionsESC[4;38;5;149m.ESC[4;38;5;149mUserDict):
15
16 def items(self):
17 return sorted(self.data.items())
18
19 def keys(self):
20 return sorted(self.data.keys())
21
22 def values(self):
23 return [i[1] for i in self.items()]
24
25 def iteritems(self):
26 return iter(self.items())
27
28 def iterkeys(self):
29 return iter(self.keys())
30
31 def itervalues(self):
32 return iter(self.values())
33
34 __iter__ = iterkeys
35
36
37 class ESC[4;38;5;81mCfgParserTestCaseClass:
38 allow_no_value = False
39 delimiters = ('=', ':')
40 comment_prefixes = (';', '#')
41 inline_comment_prefixes = (';', '#')
42 empty_lines_in_values = True
43 dict_type = configparser._default_dict
44 strict = False
45 default_section = configparser.DEFAULTSECT
46 interpolation = configparser._UNSET
47
48 def newconfig(self, defaults=None):
49 arguments = dict(
50 defaults=defaults,
51 allow_no_value=self.allow_no_value,
52 delimiters=self.delimiters,
53 comment_prefixes=self.comment_prefixes,
54 inline_comment_prefixes=self.inline_comment_prefixes,
55 empty_lines_in_values=self.empty_lines_in_values,
56 dict_type=self.dict_type,
57 strict=self.strict,
58 default_section=self.default_section,
59 interpolation=self.interpolation,
60 )
61 instance = self.config_class(**arguments)
62 return instance
63
64 def fromstring(self, string, defaults=None):
65 cf = self.newconfig(defaults)
66 cf.read_string(string)
67 return cf
68
69
70 class ESC[4;38;5;81mBasicTestCase(ESC[4;38;5;149mCfgParserTestCaseClass):
71
72 def basic_test(self, cf):
73 E = ['Commented Bar',
74 'Foo Bar',
75 'Internationalized Stuff',
76 'Long Line',
77 'Section\\with$weird%characters[\t',
78 'Spaces',
79 'Spacey Bar',
80 'Spacey Bar From The Beginning',
81 'Types',
82 'This One Has A ] In It',
83 ]
84
85 if self.allow_no_value:
86 E.append('NoValue')
87 E.sort()
88 F = [('baz', 'qwe'), ('foo', 'bar3')]
89
90 # API access
91 L = cf.sections()
92 L.sort()
93 eq = self.assertEqual
94 eq(L, E)
95 L = cf.items('Spacey Bar From The Beginning')
96 L.sort()
97 eq(L, F)
98
99 # mapping access
100 L = [section for section in cf]
101 L.sort()
102 E.append(self.default_section)
103 E.sort()
104 eq(L, E)
105 L = cf['Spacey Bar From The Beginning'].items()
106 L = sorted(list(L))
107 eq(L, F)
108 L = cf.items()
109 L = sorted(list(L))
110 self.assertEqual(len(L), len(E))
111 for name, section in L:
112 eq(name, section.name)
113 eq(cf.defaults(), cf[self.default_section])
114
115 # The use of spaces in the section names serves as a
116 # regression test for SourceForge bug #583248:
117 # http://www.python.org/sf/583248
118
119 # API access
120 eq(cf.get('Foo Bar', 'foo'), 'bar1')
121 eq(cf.get('Spacey Bar', 'foo'), 'bar2')
122 eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
123 eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
124 eq(cf.get('Commented Bar', 'foo'), 'bar4')
125 eq(cf.get('Commented Bar', 'baz'), 'qwe')
126 eq(cf.get('Spaces', 'key with spaces'), 'value')
127 eq(cf.get('Spaces', 'another with spaces'), 'splat!')
128 eq(cf.getint('Types', 'int'), 42)
129 eq(cf.get('Types', 'int'), "42")
130 self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
131 eq(cf.get('Types', 'float'), "0.44")
132 eq(cf.getboolean('Types', 'boolean'), False)
133 eq(cf.get('Types', '123'), 'strange but acceptable')
134 eq(cf.get('This One Has A ] In It', 'forks'), 'spoons')
135 if self.allow_no_value:
136 eq(cf.get('NoValue', 'option-without-value'), None)
137
138 # test vars= and fallback=
139 eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
140 eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
141 with self.assertRaises(configparser.NoSectionError):
142 cf.get('No Such Foo Bar', 'foo')
143 with self.assertRaises(configparser.NoOptionError):
144 cf.get('Foo Bar', 'no-such-foo')
145 eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
146 eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
147 eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
148 eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
149 eq(cf.getint('Types', 'int', fallback=18), 42)
150 eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
151 eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
152 with self.assertRaises(configparser.NoOptionError):
153 cf.getint('Types', 'no-such-int')
154 self.assertAlmostEqual(cf.getfloat('Types', 'float',
155 fallback=0.0), 0.44)
156 self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
157 fallback=0.0), 0.0)
158 eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
159 with self.assertRaises(configparser.NoOptionError):
160 cf.getfloat('Types', 'no-such-float')
161 eq(cf.getboolean('Types', 'boolean', fallback=True), False)
162 eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
163 "yes") # sic!
164 eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
165 with self.assertRaises(configparser.NoOptionError):
166 cf.getboolean('Types', 'no-such-boolean')
167 eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
168 if self.allow_no_value:
169 eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
170 eq(cf.get('NoValue', 'no-such-option-without-value',
171 fallback=False), False)
172
173 # mapping access
174 eq(cf['Foo Bar']['foo'], 'bar1')
175 eq(cf['Spacey Bar']['foo'], 'bar2')
176 section = cf['Spacey Bar From The Beginning']
177 eq(section.name, 'Spacey Bar From The Beginning')
178 self.assertIs(section.parser, cf)
179 with self.assertRaises(AttributeError):
180 section.name = 'Name is read-only'
181 with self.assertRaises(AttributeError):
182 section.parser = 'Parser is read-only'
183 eq(section['foo'], 'bar3')
184 eq(section['baz'], 'qwe')
185 eq(cf['Commented Bar']['foo'], 'bar4')
186 eq(cf['Commented Bar']['baz'], 'qwe')
187 eq(cf['Spaces']['key with spaces'], 'value')
188 eq(cf['Spaces']['another with spaces'], 'splat!')
189 eq(cf['Long Line']['foo'],
190 'this line is much, much longer than my editor\nlikes it.')
191 if self.allow_no_value:
192 eq(cf['NoValue']['option-without-value'], None)
193 # test vars= and fallback=
194 eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
195 eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
196 eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
197 with self.assertRaises(KeyError):
198 cf['No Such Foo Bar']['foo']
199 with self.assertRaises(KeyError):
200 cf['Foo Bar']['no-such-foo']
201 with self.assertRaises(KeyError):
202 cf['No Such Foo Bar'].get('foo', fallback='baz')
203 eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
204 eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
205 eq(cf['Foo Bar'].get('no-such-foo'), None)
206 eq(cf['Spacey Bar'].get('foo', None), 'bar2')
207 eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
208 with self.assertRaises(KeyError):
209 cf['No Such Spacey Bar'].get('foo', None)
210 eq(cf['Types'].getint('int', 18), 42)
211 eq(cf['Types'].getint('int', fallback=18), 42)
212 eq(cf['Types'].getint('no-such-int', 18), 18)
213 eq(cf['Types'].getint('no-such-int', fallback=18), 18)
214 eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
215 eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
216 eq(cf['Types'].getint('no-such-int'), None)
217 self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
218 self.assertAlmostEqual(cf['Types'].getfloat('float',
219 fallback=0.0), 0.44)
220 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
221 self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
222 fallback=0.0), 0.0)
223 eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
224 eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
225 eq(cf['Types'].getfloat('no-such-float'), None)
226 eq(cf['Types'].getboolean('boolean', True), False)
227 eq(cf['Types'].getboolean('boolean', fallback=True), False)
228 eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
229 eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
230 "yes") # sic!
231 eq(cf['Types'].getboolean('no-such-boolean', True), True)
232 eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
233 eq(cf['Types'].getboolean('no-such-boolean'), None)
234 if self.allow_no_value:
235 eq(cf['NoValue'].get('option-without-value', False), None)
236 eq(cf['NoValue'].get('option-without-value', fallback=False), None)
237 eq(cf['NoValue'].get('no-such-option-without-value', False), False)
238 eq(cf['NoValue'].get('no-such-option-without-value',
239 fallback=False), False)
240
241 # Make sure the right things happen for remove_section() and
242 # remove_option(); added to include check for SourceForge bug #123324.
243
244 cf[self.default_section]['this_value'] = '1'
245 cf[self.default_section]['that_value'] = '2'
246
247 # API access
248 self.assertTrue(cf.remove_section('Spaces'))
249 self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
250 self.assertFalse(cf.remove_section('Spaces'))
251 self.assertFalse(cf.remove_section(self.default_section))
252 self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
253 "remove_option() failed to report existence of option")
254 self.assertFalse(cf.has_option('Foo Bar', 'foo'),
255 "remove_option() failed to remove option")
256 self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
257 "remove_option() failed to report non-existence of option"
258 " that was removed")
259 self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
260 self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
261 self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
262 self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
263 self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
264
265 with self.assertRaises(configparser.NoSectionError) as cm:
266 cf.remove_option('No Such Section', 'foo')
267 self.assertEqual(cm.exception.args, ('No Such Section',))
268
269 eq(cf.get('Long Line', 'foo'),
270 'this line is much, much longer than my editor\nlikes it.')
271
272 # mapping access
273 del cf['Types']
274 self.assertFalse('Types' in cf)
275 with self.assertRaises(KeyError):
276 del cf['Types']
277 with self.assertRaises(ValueError):
278 del cf[self.default_section]
279 del cf['Spacey Bar']['foo']
280 self.assertFalse('foo' in cf['Spacey Bar'])
281 with self.assertRaises(KeyError):
282 del cf['Spacey Bar']['foo']
283 self.assertTrue('that_value' in cf['Spacey Bar'])
284 with self.assertRaises(KeyError):
285 del cf['Spacey Bar']['that_value']
286 del cf[self.default_section]['that_value']
287 self.assertFalse('that_value' in cf['Spacey Bar'])
288 with self.assertRaises(KeyError):
289 del cf[self.default_section]['that_value']
290 with self.assertRaises(KeyError):
291 del cf['No Such Section']['foo']
292
293 # Don't add new asserts below in this method as most of the options
294 # and sections are now removed.
295
296 def test_basic(self):
297 config_string = """\
298 [Foo Bar]
299 foo{0[0]}bar1
300 [Spacey Bar]
301 foo {0[0]} bar2
302 [Spacey Bar From The Beginning]
303 foo {0[0]} bar3
304 baz {0[0]} qwe
305 [Commented Bar]
306 foo{0[1]} bar4 {1[1]} comment
307 baz{0[0]}qwe {1[0]}another one
308 [Long Line]
309 foo{0[1]} this line is much, much longer than my editor
310 likes it.
311 [Section\\with$weird%characters[\t]
312 [Internationalized Stuff]
313 foo[bg]{0[1]} Bulgarian
314 foo{0[0]}Default
315 foo[en]{0[0]}English
316 foo[de]{0[0]}Deutsch
317 [Spaces]
318 key with spaces {0[1]} value
319 another with spaces {0[0]} splat!
320 [Types]
321 int {0[1]} 42
322 float {0[0]} 0.44
323 boolean {0[0]} NO
324 123 {0[1]} strange but acceptable
325 [This One Has A ] In It]
326 forks {0[0]} spoons
327 """.format(self.delimiters, self.comment_prefixes)
328 if self.allow_no_value:
329 config_string += (
330 "[NoValue]\n"
331 "option-without-value\n"
332 )
333 cf = self.fromstring(config_string)
334 self.basic_test(cf)
335 if self.strict:
336 with self.assertRaises(configparser.DuplicateOptionError):
337 cf.read_string(textwrap.dedent("""\
338 [Duplicate Options Here]
339 option {0[0]} with a value
340 option {0[1]} with another value
341 """.format(self.delimiters)))
342 with self.assertRaises(configparser.DuplicateSectionError):
343 cf.read_string(textwrap.dedent("""\
344 [And Now For Something]
345 completely different {0[0]} True
346 [And Now For Something]
347 the larch {0[1]} 1
348 """.format(self.delimiters)))
349 else:
350 cf.read_string(textwrap.dedent("""\
351 [Duplicate Options Here]
352 option {0[0]} with a value
353 option {0[1]} with another value
354 """.format(self.delimiters)))
355
356 cf.read_string(textwrap.dedent("""\
357 [And Now For Something]
358 completely different {0[0]} True
359 [And Now For Something]
360 the larch {0[1]} 1
361 """.format(self.delimiters)))
362
363 def test_basic_from_dict(self):
364 config = {
365 "Foo Bar": {
366 "foo": "bar1",
367 },
368 "Spacey Bar": {
369 "foo": "bar2",
370 },
371 "Spacey Bar From The Beginning": {
372 "foo": "bar3",
373 "baz": "qwe",
374 },
375 "Commented Bar": {
376 "foo": "bar4",
377 "baz": "qwe",
378 },
379 "Long Line": {
380 "foo": "this line is much, much longer than my editor\nlikes "
381 "it.",
382 },
383 "Section\\with$weird%characters[\t": {
384 },
385 "Internationalized Stuff": {
386 "foo[bg]": "Bulgarian",
387 "foo": "Default",
388 "foo[en]": "English",
389 "foo[de]": "Deutsch",
390 },
391 "Spaces": {
392 "key with spaces": "value",
393 "another with spaces": "splat!",
394 },
395 "Types": {
396 "int": 42,
397 "float": 0.44,
398 "boolean": False,
399 123: "strange but acceptable",
400 },
401 "This One Has A ] In It": {
402 "forks": "spoons"
403 },
404 }
405 if self.allow_no_value:
406 config.update({
407 "NoValue": {
408 "option-without-value": None,
409 }
410 })
411 cf = self.newconfig()
412 cf.read_dict(config)
413 self.basic_test(cf)
414 if self.strict:
415 with self.assertRaises(configparser.DuplicateSectionError):
416 cf.read_dict({
417 '1': {'key': 'value'},
418 1: {'key2': 'value2'},
419 })
420 with self.assertRaises(configparser.DuplicateOptionError):
421 cf.read_dict({
422 "Duplicate Options Here": {
423 'option': 'with a value',
424 'OPTION': 'with another value',
425 },
426 })
427 else:
428 cf.read_dict({
429 'section': {'key': 'value'},
430 'SECTION': {'key2': 'value2'},
431 })
432 cf.read_dict({
433 "Duplicate Options Here": {
434 'option': 'with a value',
435 'OPTION': 'with another value',
436 },
437 })
438
439 def test_case_sensitivity(self):
440 cf = self.newconfig()
441 cf.add_section("A")
442 cf.add_section("a")
443 cf.add_section("B")
444 L = cf.sections()
445 L.sort()
446 eq = self.assertEqual
447 eq(L, ["A", "B", "a"])
448 cf.set("a", "B", "value")
449 eq(cf.options("a"), ["b"])
450 eq(cf.get("a", "b"), "value",
451 "could not locate option, expecting case-insensitive option names")
452 with self.assertRaises(configparser.NoSectionError):
453 # section names are case-sensitive
454 cf.set("b", "A", "value")
455 self.assertTrue(cf.has_option("a", "b"))
456 self.assertFalse(cf.has_option("b", "b"))
457 cf.set("A", "A-B", "A-B value")
458 for opt in ("a-b", "A-b", "a-B", "A-B"):
459 self.assertTrue(
460 cf.has_option("A", opt),
461 "has_option() returned false for option which should exist")
462 eq(cf.options("A"), ["a-b"])
463 eq(cf.options("a"), ["b"])
464 cf.remove_option("a", "B")
465 eq(cf.options("a"), [])
466
467 # SF bug #432369:
468 cf = self.fromstring(
469 "[MySection]\nOption{} first line \n\tsecond line \n".format(
470 self.delimiters[0]))
471 eq(cf.options("MySection"), ["option"])
472 eq(cf.get("MySection", "Option"), "first line\nsecond line")
473
474 # SF bug #561822:
475 cf = self.fromstring("[section]\n"
476 "nekey{}nevalue\n".format(self.delimiters[0]),
477 defaults={"key":"value"})
478 self.assertTrue(cf.has_option("section", "Key"))
479
480
481 def test_case_sensitivity_mapping_access(self):
482 cf = self.newconfig()
483 cf["A"] = {}
484 cf["a"] = {"B": "value"}
485 cf["B"] = {}
486 L = [section for section in cf]
487 L.sort()
488 eq = self.assertEqual
489 elem_eq = self.assertCountEqual
490 eq(L, sorted(["A", "B", self.default_section, "a"]))
491 eq(cf["a"].keys(), {"b"})
492 eq(cf["a"]["b"], "value",
493 "could not locate option, expecting case-insensitive option names")
494 with self.assertRaises(KeyError):
495 # section names are case-sensitive
496 cf["b"]["A"] = "value"
497 self.assertTrue("b" in cf["a"])
498 cf["A"]["A-B"] = "A-B value"
499 for opt in ("a-b", "A-b", "a-B", "A-B"):
500 self.assertTrue(
501 opt in cf["A"],
502 "has_option() returned false for option which should exist")
503 eq(cf["A"].keys(), {"a-b"})
504 eq(cf["a"].keys(), {"b"})
505 del cf["a"]["B"]
506 elem_eq(cf["a"].keys(), {})
507
508 # SF bug #432369:
509 cf = self.fromstring(
510 "[MySection]\nOption{} first line \n\tsecond line \n".format(
511 self.delimiters[0]))
512 eq(cf["MySection"].keys(), {"option"})
513 eq(cf["MySection"]["Option"], "first line\nsecond line")
514
515 # SF bug #561822:
516 cf = self.fromstring("[section]\n"
517 "nekey{}nevalue\n".format(self.delimiters[0]),
518 defaults={"key":"value"})
519 self.assertTrue("Key" in cf["section"])
520
521 def test_default_case_sensitivity(self):
522 cf = self.newconfig({"foo": "Bar"})
523 self.assertEqual(
524 cf.get(self.default_section, "Foo"), "Bar",
525 "could not locate option, expecting case-insensitive option names")
526 cf = self.newconfig({"Foo": "Bar"})
527 self.assertEqual(
528 cf.get(self.default_section, "Foo"), "Bar",
529 "could not locate option, expecting case-insensitive defaults")
530
531 def test_parse_errors(self):
532 cf = self.newconfig()
533 self.parse_error(cf, configparser.ParsingError,
534 "[Foo]\n"
535 "{}val-without-opt-name\n".format(self.delimiters[0]))
536 self.parse_error(cf, configparser.ParsingError,
537 "[Foo]\n"
538 "{}val-without-opt-name\n".format(self.delimiters[1]))
539 e = self.parse_error(cf, configparser.MissingSectionHeaderError,
540 "No Section!\n")
541 self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
542 if not self.allow_no_value:
543 e = self.parse_error(cf, configparser.ParsingError,
544 "[Foo]\n wrong-indent\n")
545 self.assertEqual(e.args, ('<???>',))
546 # read_file on a real file
547 tricky = support.findfile("cfgparser.3", subdir="configdata")
548 if self.delimiters[0] == '=':
549 error = configparser.ParsingError
550 expected = (tricky,)
551 else:
552 error = configparser.MissingSectionHeaderError
553 expected = (tricky, 1,
554 ' # INI with as many tricky parts as possible\n')
555 with open(tricky, encoding='utf-8') as f:
556 e = self.parse_error(cf, error, f)
557 self.assertEqual(e.args, expected)
558
559 def parse_error(self, cf, exc, src):
560 if hasattr(src, 'readline'):
561 sio = src
562 else:
563 sio = io.StringIO(src)
564 with self.assertRaises(exc) as cm:
565 cf.read_file(sio)
566 return cm.exception
567
568 def test_query_errors(self):
569 cf = self.newconfig()
570 self.assertEqual(cf.sections(), [],
571 "new ConfigParser should have no defined sections")
572 self.assertFalse(cf.has_section("Foo"),
573 "new ConfigParser should have no acknowledged "
574 "sections")
575 with self.assertRaises(configparser.NoSectionError):
576 cf.options("Foo")
577 with self.assertRaises(configparser.NoSectionError):
578 cf.set("foo", "bar", "value")
579 e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
580 self.assertEqual(e.args, ("foo",))
581 cf.add_section("foo")
582 e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
583 self.assertEqual(e.args, ("bar", "foo"))
584
585 def get_error(self, cf, exc, section, option):
586 try:
587 cf.get(section, option)
588 except exc as e:
589 return e
590 else:
591 self.fail("expected exception type %s.%s"
592 % (exc.__module__, exc.__qualname__))
593
594 def test_boolean(self):
595 cf = self.fromstring(
596 "[BOOLTEST]\n"
597 "T1{equals}1\n"
598 "T2{equals}TRUE\n"
599 "T3{equals}True\n"
600 "T4{equals}oN\n"
601 "T5{equals}yes\n"
602 "F1{equals}0\n"
603 "F2{equals}FALSE\n"
604 "F3{equals}False\n"
605 "F4{equals}oFF\n"
606 "F5{equals}nO\n"
607 "E1{equals}2\n"
608 "E2{equals}foo\n"
609 "E3{equals}-1\n"
610 "E4{equals}0.1\n"
611 "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
612 )
613 for x in range(1, 5):
614 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
615 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
616 self.assertRaises(ValueError,
617 cf.getboolean, 'BOOLTEST', 'e%d' % x)
618
619 def test_weird_errors(self):
620 cf = self.newconfig()
621 cf.add_section("Foo")
622 with self.assertRaises(configparser.DuplicateSectionError) as cm:
623 cf.add_section("Foo")
624 e = cm.exception
625 self.assertEqual(str(e), "Section 'Foo' already exists")
626 self.assertEqual(e.args, ("Foo", None, None))
627
628 if self.strict:
629 with self.assertRaises(configparser.DuplicateSectionError) as cm:
630 cf.read_string(textwrap.dedent("""\
631 [Foo]
632 will this be added{equals}True
633 [Bar]
634 what about this{equals}True
635 [Foo]
636 oops{equals}this won't
637 """.format(equals=self.delimiters[0])), source='<foo-bar>')
638 e = cm.exception
639 self.assertEqual(str(e), "While reading from '<foo-bar>' "
640 "[line 5]: section 'Foo' already exists")
641 self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
642
643 with self.assertRaises(configparser.DuplicateOptionError) as cm:
644 cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
645 e = cm.exception
646 self.assertEqual(str(e), "While reading from '<dict>': option "
647 "'opt' in section 'Bar' already exists")
648 self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
649
650 def test_write(self):
651 config_string = (
652 "[Long Line]\n"
653 "foo{0[0]} this line is much, much longer than my editor\n"
654 " likes it.\n"
655 "[{default_section}]\n"
656 "foo{0[1]} another very\n"
657 " long line\n"
658 "[Long Line - With Comments!]\n"
659 "test {0[1]} we {comment} can\n"
660 " also {comment} place\n"
661 " comments {comment} in\n"
662 " multiline {comment} values"
663 "\n".format(self.delimiters, comment=self.comment_prefixes[0],
664 default_section=self.default_section)
665 )
666 if self.allow_no_value:
667 config_string += (
668 "[Valueless]\n"
669 "option-without-value\n"
670 )
671
672 cf = self.fromstring(config_string)
673 for space_around_delimiters in (True, False):
674 output = io.StringIO()
675 cf.write(output, space_around_delimiters=space_around_delimiters)
676 delimiter = self.delimiters[0]
677 if space_around_delimiters:
678 delimiter = " {} ".format(delimiter)
679 expect_string = (
680 "[{default_section}]\n"
681 "foo{equals}another very\n"
682 "\tlong line\n"
683 "\n"
684 "[Long Line]\n"
685 "foo{equals}this line is much, much longer than my editor\n"
686 "\tlikes it.\n"
687 "\n"
688 "[Long Line - With Comments!]\n"
689 "test{equals}we\n"
690 "\talso\n"
691 "\tcomments\n"
692 "\tmultiline\n"
693 "\n".format(equals=delimiter,
694 default_section=self.default_section)
695 )
696 if self.allow_no_value:
697 expect_string += (
698 "[Valueless]\n"
699 "option-without-value\n"
700 "\n"
701 )
702 self.assertEqual(output.getvalue(), expect_string)
703
704 def test_set_string_types(self):
705 cf = self.fromstring("[sect]\n"
706 "option1{eq}foo\n".format(eq=self.delimiters[0]))
707 # Check that we don't get an exception when setting values in
708 # an existing section using strings:
709 class ESC[4;38;5;81mmystr(ESC[4;38;5;149mstr):
710 pass
711 cf.set("sect", "option1", "splat")
712 cf.set("sect", "option1", mystr("splat"))
713 cf.set("sect", "option2", "splat")
714 cf.set("sect", "option2", mystr("splat"))
715 cf.set("sect", "option1", "splat")
716 cf.set("sect", "option2", "splat")
717
718 def test_read_returns_file_list(self):
719 if self.delimiters[0] != '=':
720 self.skipTest('incompatible format')
721 file1 = support.findfile("cfgparser.1", subdir="configdata")
722 # check when we pass a mix of readable and non-readable files:
723 cf = self.newconfig()
724 parsed_files = cf.read([file1, "nonexistent-file"], encoding="utf-8")
725 self.assertEqual(parsed_files, [file1])
726 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
727 # check when we pass only a filename:
728 cf = self.newconfig()
729 parsed_files = cf.read(file1, encoding="utf-8")
730 self.assertEqual(parsed_files, [file1])
731 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
732 # check when we pass only a Path object:
733 cf = self.newconfig()
734 parsed_files = cf.read(pathlib.Path(file1), encoding="utf-8")
735 self.assertEqual(parsed_files, [file1])
736 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
737 # check when we passed both a filename and a Path object:
738 cf = self.newconfig()
739 parsed_files = cf.read([pathlib.Path(file1), file1], encoding="utf-8")
740 self.assertEqual(parsed_files, [file1, file1])
741 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
742 # check when we pass only missing files:
743 cf = self.newconfig()
744 parsed_files = cf.read(["nonexistent-file"], encoding="utf-8")
745 self.assertEqual(parsed_files, [])
746 # check when we pass no files:
747 cf = self.newconfig()
748 parsed_files = cf.read([], encoding="utf-8")
749 self.assertEqual(parsed_files, [])
750
751 def test_read_returns_file_list_with_bytestring_path(self):
752 if self.delimiters[0] != '=':
753 self.skipTest('incompatible format')
754 file1_bytestring = support.findfile("cfgparser.1", subdir="configdata").encode()
755 # check when passing an existing bytestring path
756 cf = self.newconfig()
757 parsed_files = cf.read(file1_bytestring, encoding="utf-8")
758 self.assertEqual(parsed_files, [file1_bytestring])
759 # check when passing an non-existing bytestring path
760 cf = self.newconfig()
761 parsed_files = cf.read(b'nonexistent-file', encoding="utf-8")
762 self.assertEqual(parsed_files, [])
763 # check when passing both an existing and non-existing bytestring path
764 cf = self.newconfig()
765 parsed_files = cf.read([file1_bytestring, b'nonexistent-file'], encoding="utf-8")
766 self.assertEqual(parsed_files, [file1_bytestring])
767
768 # shared by subclasses
769 def get_interpolation_config(self):
770 return self.fromstring(
771 "[Foo]\n"
772 "bar{equals}something %(with1)s interpolation (1 step)\n"
773 "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
774 "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
775 "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
776 "with11{equals}%(with10)s\n"
777 "with10{equals}%(with9)s\n"
778 "with9{equals}%(with8)s\n"
779 "with8{equals}%(With7)s\n"
780 "with7{equals}%(WITH6)s\n"
781 "with6{equals}%(with5)s\n"
782 "With5{equals}%(with4)s\n"
783 "WITH4{equals}%(with3)s\n"
784 "with3{equals}%(with2)s\n"
785 "with2{equals}%(with1)s\n"
786 "with1{equals}with\n"
787 "\n"
788 "[Mutual Recursion]\n"
789 "foo{equals}%(bar)s\n"
790 "bar{equals}%(foo)s\n"
791 "\n"
792 "[Interpolation Error]\n"
793 # no definition for 'reference'
794 "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
795
796 def check_items_config(self, expected):
797 cf = self.fromstring("""
798 [section]
799 name {0[0]} %(value)s
800 key{0[1]} |%(name)s|
801 getdefault{0[1]} |%(default)s|
802 """.format(self.delimiters), defaults={"default": "<default>"})
803 L = list(cf.items("section", vars={'value': 'value'}))
804 L.sort()
805 self.assertEqual(L, expected)
806 with self.assertRaises(configparser.NoSectionError):
807 cf.items("no such section")
808
809 def test_popitem(self):
810 cf = self.fromstring("""
811 [section1]
812 name1 {0[0]} value1
813 [section2]
814 name2 {0[0]} value2
815 [section3]
816 name3 {0[0]} value3
817 """.format(self.delimiters), defaults={"default": "<default>"})
818 self.assertEqual(cf.popitem()[0], 'section1')
819 self.assertEqual(cf.popitem()[0], 'section2')
820 self.assertEqual(cf.popitem()[0], 'section3')
821 with self.assertRaises(KeyError):
822 cf.popitem()
823
824 def test_clear(self):
825 cf = self.newconfig({"foo": "Bar"})
826 self.assertEqual(
827 cf.get(self.default_section, "Foo"), "Bar",
828 "could not locate option, expecting case-insensitive option names")
829 cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
830 self.assertEqual(cf.sections(), ['zing'])
831 self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
832 cf.clear()
833 self.assertEqual(set(cf.sections()), set())
834 self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
835
836 def test_setitem(self):
837 cf = self.fromstring("""
838 [section1]
839 name1 {0[0]} value1
840 [section2]
841 name2 {0[0]} value2
842 [section3]
843 name3 {0[0]} value3
844 """.format(self.delimiters), defaults={"nameD": "valueD"})
845 self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
846 self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
847 self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
848 self.assertEqual(cf['section1']['name1'], 'value1')
849 self.assertEqual(cf['section2']['name2'], 'value2')
850 self.assertEqual(cf['section3']['name3'], 'value3')
851 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
852 cf['section2'] = {'name22': 'value22'}
853 self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
854 self.assertEqual(cf['section2']['name22'], 'value22')
855 self.assertNotIn('name2', cf['section2'])
856 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
857 cf['section3'] = {}
858 self.assertEqual(set(cf['section3'].keys()), {'named'})
859 self.assertNotIn('name3', cf['section3'])
860 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
861 # For bpo-32108, assigning default_section to itself.
862 cf[self.default_section] = cf[self.default_section]
863 self.assertNotEqual(set(cf[self.default_section].keys()), set())
864 cf[self.default_section] = {}
865 self.assertEqual(set(cf[self.default_section].keys()), set())
866 self.assertEqual(set(cf['section1'].keys()), {'name1'})
867 self.assertEqual(set(cf['section2'].keys()), {'name22'})
868 self.assertEqual(set(cf['section3'].keys()), set())
869 self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
870 # For bpo-32108, assigning section to itself.
871 cf['section2'] = cf['section2']
872 self.assertEqual(set(cf['section2'].keys()), {'name22'})
873
874 def test_invalid_multiline_value(self):
875 if self.allow_no_value:
876 self.skipTest('if no_value is allowed, ParsingError is not raised')
877
878 invalid = textwrap.dedent("""\
879 [DEFAULT]
880 test {0} test
881 invalid""".format(self.delimiters[0])
882 )
883 cf = self.newconfig()
884 with self.assertRaises(configparser.ParsingError):
885 cf.read_string(invalid)
886 self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
887 self.assertEqual(cf['DEFAULT']['test'], 'test')
888
889
890 class ESC[4;38;5;81mStrictTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
891 config_class = configparser.RawConfigParser
892 strict = True
893
894
895 class ESC[4;38;5;81mConfigParserTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
896 config_class = configparser.ConfigParser
897
898 def test_interpolation(self):
899 cf = self.get_interpolation_config()
900 eq = self.assertEqual
901 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
902 eq(cf.get("Foo", "bar9"),
903 "something with lots of interpolation (9 steps)")
904 eq(cf.get("Foo", "bar10"),
905 "something with lots of interpolation (10 steps)")
906 e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
907 if self.interpolation == configparser._UNSET:
908 self.assertEqual(e.args, ("bar11", "Foo",
909 "something %(with11)s lots of interpolation (11 steps)"))
910 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
911 self.assertEqual(e.args, ("bar11", "Foo",
912 "something %(with11)s lots of interpolation (11 steps)"))
913
914 def test_interpolation_missing_value(self):
915 cf = self.get_interpolation_config()
916 e = self.get_error(cf, configparser.InterpolationMissingOptionError,
917 "Interpolation Error", "name")
918 self.assertEqual(e.reference, "reference")
919 self.assertEqual(e.section, "Interpolation Error")
920 self.assertEqual(e.option, "name")
921 if self.interpolation == configparser._UNSET:
922 self.assertEqual(e.args, ('name', 'Interpolation Error',
923 '%(reference)s', 'reference'))
924 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
925 self.assertEqual(e.args, ('name', 'Interpolation Error',
926 '%(reference)s', 'reference'))
927
928 def test_items(self):
929 self.check_items_config([('default', '<default>'),
930 ('getdefault', '|<default>|'),
931 ('key', '|value|'),
932 ('name', 'value')])
933
934 def test_safe_interpolation(self):
935 # See http://www.python.org/sf/511737
936 cf = self.fromstring("[section]\n"
937 "option1{eq}xxx\n"
938 "option2{eq}%(option1)s/xxx\n"
939 "ok{eq}%(option1)s/%%s\n"
940 "not_ok{eq}%(option2)s/%%s".format(
941 eq=self.delimiters[0]))
942 self.assertEqual(cf.get("section", "ok"), "xxx/%s")
943 if self.interpolation == configparser._UNSET:
944 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
945 elif isinstance(self.interpolation, configparser.LegacyInterpolation):
946 with self.assertRaises(TypeError):
947 cf.get("section", "not_ok")
948
949 def test_set_malformatted_interpolation(self):
950 cf = self.fromstring("[sect]\n"
951 "option1{eq}foo\n".format(eq=self.delimiters[0]))
952
953 self.assertEqual(cf.get('sect', "option1"), "foo")
954
955 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
956 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
957 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
958
959 self.assertEqual(cf.get('sect', "option1"), "foo")
960
961 # bug #5741: double percents are *not* malformed
962 cf.set("sect", "option2", "foo%%bar")
963 self.assertEqual(cf.get("sect", "option2"), "foo%bar")
964
965 def test_set_nonstring_types(self):
966 cf = self.fromstring("[sect]\n"
967 "option1{eq}foo\n".format(eq=self.delimiters[0]))
968 # Check that we get a TypeError when setting non-string values
969 # in an existing section:
970 self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
971 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
972 self.assertRaises(TypeError, cf.set, "sect", "option1", object())
973 self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
974 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
975 self.assertRaises(TypeError, cf.set, "sect", "option2", object())
976 self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
977 self.assertRaises(TypeError, cf.add_section, 123)
978
979 def test_add_section_default(self):
980 cf = self.newconfig()
981 self.assertRaises(ValueError, cf.add_section, self.default_section)
982
983 def test_defaults_keyword(self):
984 """bpo-23835 fix for ConfigParser"""
985 cf = self.newconfig(defaults={1: 2.4})
986 self.assertEqual(cf[self.default_section]['1'], '2.4')
987 self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
988 cf = self.newconfig(defaults={"A": 5.2})
989 self.assertEqual(cf[self.default_section]['a'], '5.2')
990 self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
991
992
993 class ESC[4;38;5;81mConfigParserTestCaseNoInterpolation(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
994 config_class = configparser.ConfigParser
995 interpolation = None
996 ini = textwrap.dedent("""
997 [numbers]
998 one = 1
999 two = %(one)s * 2
1000 three = ${common:one} * 3
1001
1002 [hexen]
1003 sixteen = ${numbers:two} * 8
1004 """).strip()
1005
1006 def assertMatchesIni(self, cf):
1007 self.assertEqual(cf['numbers']['one'], '1')
1008 self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
1009 self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
1010 self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
1011
1012 def test_no_interpolation(self):
1013 cf = self.fromstring(self.ini)
1014 self.assertMatchesIni(cf)
1015
1016 def test_empty_case(self):
1017 cf = self.newconfig()
1018 self.assertIsNone(cf.read_string(""))
1019
1020 def test_none_as_default_interpolation(self):
1021 class ESC[4;38;5;81mCustomConfigParser(ESC[4;38;5;149mconfigparserESC[4;38;5;149m.ESC[4;38;5;149mConfigParser):
1022 _DEFAULT_INTERPOLATION = None
1023
1024 cf = CustomConfigParser()
1025 cf.read_string(self.ini)
1026 self.assertMatchesIni(cf)
1027
1028
1029 class ESC[4;38;5;81mConfigParserTestCaseLegacyInterpolation(ESC[4;38;5;149mConfigParserTestCase):
1030 config_class = configparser.ConfigParser
1031 with warnings.catch_warnings():
1032 warnings.simplefilter("ignore", DeprecationWarning)
1033 interpolation = configparser.LegacyInterpolation()
1034
1035 def test_set_malformatted_interpolation(self):
1036 cf = self.fromstring("[sect]\n"
1037 "option1{eq}foo\n".format(eq=self.delimiters[0]))
1038
1039 self.assertEqual(cf.get('sect', "option1"), "foo")
1040
1041 cf.set("sect", "option1", "%foo")
1042 self.assertEqual(cf.get('sect', "option1"), "%foo")
1043 cf.set("sect", "option1", "foo%")
1044 self.assertEqual(cf.get('sect', "option1"), "foo%")
1045 cf.set("sect", "option1", "f%oo")
1046 self.assertEqual(cf.get('sect', "option1"), "f%oo")
1047
1048 # bug #5741: double percents are *not* malformed
1049 cf.set("sect", "option2", "foo%%bar")
1050 self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
1051
1052
1053 class ESC[4;38;5;81mConfigParserTestCaseInvalidInterpolationType(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1054 def test_error_on_wrong_type_for_interpolation(self):
1055 for value in [configparser.ExtendedInterpolation, 42, "a string"]:
1056 with self.subTest(value=value):
1057 with self.assertRaises(TypeError):
1058 configparser.ConfigParser(interpolation=value)
1059
1060
1061 class ESC[4;38;5;81mConfigParserTestCaseNonStandardDelimiters(ESC[4;38;5;149mConfigParserTestCase):
1062 delimiters = (':=', '$')
1063 comment_prefixes = ('//', '"')
1064 inline_comment_prefixes = ('//', '"')
1065
1066
1067 class ESC[4;38;5;81mConfigParserTestCaseNonStandardDefaultSection(ESC[4;38;5;149mConfigParserTestCase):
1068 default_section = 'general'
1069
1070
1071 class ESC[4;38;5;81mMultilineValuesTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1072 config_class = configparser.ConfigParser
1073 wonderful_spam = ("I'm having spam spam spam spam "
1074 "spam spam spam beaked beans spam "
1075 "spam spam and spam!").replace(' ', '\t\n')
1076
1077 def setUp(self):
1078 cf = self.newconfig()
1079 for i in range(100):
1080 s = 'section{}'.format(i)
1081 cf.add_section(s)
1082 for j in range(10):
1083 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1084 with open(os_helper.TESTFN, 'w', encoding="utf-8") as f:
1085 cf.write(f)
1086
1087 def tearDown(self):
1088 os.unlink(os_helper.TESTFN)
1089
1090 def test_dominating_multiline_values(self):
1091 # We're reading from file because this is where the code changed
1092 # during performance updates in Python 3.2
1093 cf_from_file = self.newconfig()
1094 with open(os_helper.TESTFN, encoding="utf-8") as f:
1095 cf_from_file.read_file(f)
1096 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1097 self.wonderful_spam.replace('\t\n', '\n'))
1098
1099
1100 class ESC[4;38;5;81mRawConfigParserTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1101 config_class = configparser.RawConfigParser
1102
1103 def test_interpolation(self):
1104 cf = self.get_interpolation_config()
1105 eq = self.assertEqual
1106 eq(cf.get("Foo", "bar"),
1107 "something %(with1)s interpolation (1 step)")
1108 eq(cf.get("Foo", "bar9"),
1109 "something %(with9)s lots of interpolation (9 steps)")
1110 eq(cf.get("Foo", "bar10"),
1111 "something %(with10)s lots of interpolation (10 steps)")
1112 eq(cf.get("Foo", "bar11"),
1113 "something %(with11)s lots of interpolation (11 steps)")
1114
1115 def test_items(self):
1116 self.check_items_config([('default', '<default>'),
1117 ('getdefault', '|%(default)s|'),
1118 ('key', '|%(name)s|'),
1119 ('name', '%(value)s')])
1120
1121 def test_set_nonstring_types(self):
1122 cf = self.newconfig()
1123 cf.add_section('non-string')
1124 cf.set('non-string', 'int', 1)
1125 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1126 cf.set('non-string', 'dict', {'pi': 3.14159})
1127 self.assertEqual(cf.get('non-string', 'int'), 1)
1128 self.assertEqual(cf.get('non-string', 'list'),
1129 [0, 1, 1, 2, 3, 5, 8, 13])
1130 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
1131 cf.add_section(123)
1132 cf.set(123, 'this is sick', True)
1133 self.assertEqual(cf.get(123, 'this is sick'), True)
1134 if cf._dict is configparser._default_dict:
1135 # would not work for SortedDict; only checking for the most common
1136 # default dictionary (dict)
1137 cf.optionxform = lambda x: x
1138 cf.set('non-string', 1, 1)
1139 self.assertEqual(cf.get('non-string', 1), 1)
1140
1141 def test_defaults_keyword(self):
1142 """bpo-23835 legacy behavior for RawConfigParser"""
1143 with self.assertRaises(AttributeError) as ctx:
1144 self.newconfig(defaults={1: 2.4})
1145 err = ctx.exception
1146 self.assertEqual(str(err), "'int' object has no attribute 'lower'")
1147 cf = self.newconfig(defaults={"A": 5.2})
1148 self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
1149
1150
1151 class ESC[4;38;5;81mRawConfigParserTestCaseNonStandardDelimiters(ESC[4;38;5;149mRawConfigParserTestCase):
1152 delimiters = (':=', '$')
1153 comment_prefixes = ('//', '"')
1154 inline_comment_prefixes = ('//', '"')
1155
1156
1157 class ESC[4;38;5;81mRawConfigParserTestSambaConf(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1158 config_class = configparser.RawConfigParser
1159 comment_prefixes = ('#', ';', '----')
1160 inline_comment_prefixes = ('//',)
1161 empty_lines_in_values = False
1162
1163 def test_reading(self):
1164 smbconf = support.findfile("cfgparser.2", subdir="configdata")
1165 # check when we pass a mix of readable and non-readable files:
1166 cf = self.newconfig()
1167 parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
1168 self.assertEqual(parsed_files, [smbconf])
1169 sections = ['global', 'homes', 'printers',
1170 'print$', 'pdf-generator', 'tmp', 'Agustin']
1171 self.assertEqual(cf.sections(), sections)
1172 self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1173 self.assertEqual(cf.getint("global", "max log size"), 50)
1174 self.assertEqual(cf.get("global", "hosts allow"), "127.")
1175 self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
1176
1177 class ESC[4;38;5;81mConfigParserTestCaseExtendedInterpolation(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1178 config_class = configparser.ConfigParser
1179 interpolation = configparser.ExtendedInterpolation()
1180 default_section = 'common'
1181 strict = True
1182
1183 def fromstring(self, string, defaults=None, optionxform=None):
1184 cf = self.newconfig(defaults)
1185 if optionxform:
1186 cf.optionxform = optionxform
1187 cf.read_string(string)
1188 return cf
1189
1190 def test_extended_interpolation(self):
1191 cf = self.fromstring(textwrap.dedent("""
1192 [common]
1193 favourite Beatle = Paul
1194 favourite color = green
1195
1196 [tom]
1197 favourite band = ${favourite color} day
1198 favourite pope = John ${favourite Beatle} II
1199 sequel = ${favourite pope}I
1200
1201 [ambv]
1202 favourite Beatle = George
1203 son of Edward VII = ${favourite Beatle} V
1204 son of George V = ${son of Edward VII}I
1205
1206 [stanley]
1207 favourite Beatle = ${ambv:favourite Beatle}
1208 favourite pope = ${tom:favourite pope}
1209 favourite color = black
1210 favourite state of mind = paranoid
1211 favourite movie = soylent ${common:favourite color}
1212 favourite song = ${favourite color} sabbath - ${favourite state of mind}
1213 """).strip())
1214
1215 eq = self.assertEqual
1216 eq(cf['common']['favourite Beatle'], 'Paul')
1217 eq(cf['common']['favourite color'], 'green')
1218 eq(cf['tom']['favourite Beatle'], 'Paul')
1219 eq(cf['tom']['favourite color'], 'green')
1220 eq(cf['tom']['favourite band'], 'green day')
1221 eq(cf['tom']['favourite pope'], 'John Paul II')
1222 eq(cf['tom']['sequel'], 'John Paul III')
1223 eq(cf['ambv']['favourite Beatle'], 'George')
1224 eq(cf['ambv']['favourite color'], 'green')
1225 eq(cf['ambv']['son of Edward VII'], 'George V')
1226 eq(cf['ambv']['son of George V'], 'George VI')
1227 eq(cf['stanley']['favourite Beatle'], 'George')
1228 eq(cf['stanley']['favourite color'], 'black')
1229 eq(cf['stanley']['favourite state of mind'], 'paranoid')
1230 eq(cf['stanley']['favourite movie'], 'soylent green')
1231 eq(cf['stanley']['favourite pope'], 'John Paul II')
1232 eq(cf['stanley']['favourite song'],
1233 'black sabbath - paranoid')
1234
1235 def test_endless_loop(self):
1236 cf = self.fromstring(textwrap.dedent("""
1237 [one for you]
1238 ping = ${one for me:pong}
1239
1240 [one for me]
1241 pong = ${one for you:ping}
1242
1243 [selfish]
1244 me = ${me}
1245 """).strip())
1246
1247 with self.assertRaises(configparser.InterpolationDepthError):
1248 cf['one for you']['ping']
1249 with self.assertRaises(configparser.InterpolationDepthError):
1250 cf['selfish']['me']
1251
1252 def test_strange_options(self):
1253 cf = self.fromstring("""
1254 [dollars]
1255 $var = $$value
1256 $var2 = ${$var}
1257 ${sick} = cannot interpolate me
1258
1259 [interpolated]
1260 $other = ${dollars:$var}
1261 $trying = ${dollars:${sick}}
1262 """)
1263
1264 self.assertEqual(cf['dollars']['$var'], '$value')
1265 self.assertEqual(cf['interpolated']['$other'], '$value')
1266 self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1267 exception_class = configparser.InterpolationMissingOptionError
1268 with self.assertRaises(exception_class) as cm:
1269 cf['interpolated']['$trying']
1270 self.assertEqual(cm.exception.reference, 'dollars:${sick')
1271 self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
1272
1273 def test_case_sensitivity_basic(self):
1274 ini = textwrap.dedent("""
1275 [common]
1276 optionlower = value
1277 OptionUpper = Value
1278
1279 [Common]
1280 optionlower = a better ${common:optionlower}
1281 OptionUpper = A Better ${common:OptionUpper}
1282
1283 [random]
1284 foolower = ${common:optionlower} redefined
1285 FooUpper = ${Common:OptionUpper} Redefined
1286 """).strip()
1287
1288 cf = self.fromstring(ini)
1289 eq = self.assertEqual
1290 eq(cf['common']['optionlower'], 'value')
1291 eq(cf['common']['OptionUpper'], 'Value')
1292 eq(cf['Common']['optionlower'], 'a better value')
1293 eq(cf['Common']['OptionUpper'], 'A Better Value')
1294 eq(cf['random']['foolower'], 'value redefined')
1295 eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1296
1297 def test_case_sensitivity_conflicts(self):
1298 ini = textwrap.dedent("""
1299 [common]
1300 option = value
1301 Option = Value
1302
1303 [Common]
1304 option = a better ${common:option}
1305 Option = A Better ${common:Option}
1306
1307 [random]
1308 foo = ${common:option} redefined
1309 Foo = ${Common:Option} Redefined
1310 """).strip()
1311 with self.assertRaises(configparser.DuplicateOptionError):
1312 cf = self.fromstring(ini)
1313
1314 # raw options
1315 cf = self.fromstring(ini, optionxform=lambda opt: opt)
1316 eq = self.assertEqual
1317 eq(cf['common']['option'], 'value')
1318 eq(cf['common']['Option'], 'Value')
1319 eq(cf['Common']['option'], 'a better value')
1320 eq(cf['Common']['Option'], 'A Better Value')
1321 eq(cf['random']['foo'], 'value redefined')
1322 eq(cf['random']['Foo'], 'A Better Value Redefined')
1323
1324 def test_other_errors(self):
1325 cf = self.fromstring("""
1326 [interpolation fail]
1327 case1 = ${where's the brace
1328 case2 = ${does_not_exist}
1329 case3 = ${wrong_section:wrong_value}
1330 case4 = ${i:like:colon:characters}
1331 case5 = $100 for Fail No 5!
1332 """)
1333
1334 with self.assertRaises(configparser.InterpolationSyntaxError):
1335 cf['interpolation fail']['case1']
1336 with self.assertRaises(configparser.InterpolationMissingOptionError):
1337 cf['interpolation fail']['case2']
1338 with self.assertRaises(configparser.InterpolationMissingOptionError):
1339 cf['interpolation fail']['case3']
1340 with self.assertRaises(configparser.InterpolationSyntaxError):
1341 cf['interpolation fail']['case4']
1342 with self.assertRaises(configparser.InterpolationSyntaxError):
1343 cf['interpolation fail']['case5']
1344 with self.assertRaises(ValueError):
1345 cf['interpolation fail']['case6'] = "BLACK $ABBATH"
1346
1347
1348 class ESC[4;38;5;81mConfigParserTestCaseNoValue(ESC[4;38;5;149mConfigParserTestCase):
1349 allow_no_value = True
1350
1351
1352 class ESC[4;38;5;81mConfigParserTestCaseTrickyFile(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1353 config_class = configparser.ConfigParser
1354 delimiters = {'='}
1355 comment_prefixes = {'#'}
1356 allow_no_value = True
1357
1358 def test_cfgparser_dot_3(self):
1359 tricky = support.findfile("cfgparser.3", subdir="configdata")
1360 cf = self.newconfig()
1361 self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1362 self.assertEqual(cf.sections(), ['strange',
1363 'corruption',
1364 'yeah, sections can be '
1365 'indented as well',
1366 'another one!',
1367 'no values here',
1368 'tricky interpolation',
1369 'more interpolation'])
1370 self.assertEqual(cf.getint(self.default_section, 'go',
1371 vars={'interpolate': '-1'}), -1)
1372 with self.assertRaises(ValueError):
1373 # no interpolation will happen
1374 cf.getint(self.default_section, 'go', raw=True,
1375 vars={'interpolate': '-1'})
1376 self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1377 self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1378 longname = 'yeah, sections can be indented as well'
1379 self.assertFalse(cf.getboolean(longname, 'are they subsections'))
1380 self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
1381 self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1382 # `go` from DEFAULT
1383 with self.assertRaises(configparser.InterpolationMissingOptionError):
1384 cf.items('no values here')
1385 self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1386 self.assertEqual(cf.get('tricky interpolation', 'lets'),
1387 cf.get('tricky interpolation', 'go'))
1388 self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1389
1390 def test_unicode_failure(self):
1391 tricky = support.findfile("cfgparser.3", subdir="configdata")
1392 cf = self.newconfig()
1393 with self.assertRaises(UnicodeDecodeError):
1394 cf.read(tricky, encoding='ascii')
1395
1396
1397 class ESC[4;38;5;81mIssue7005TestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1398 """Test output when None is set() as a value and allow_no_value == False.
1399
1400 http://bugs.python.org/issue7005
1401
1402 """
1403
1404 expected_output = "[section]\noption = None\n\n"
1405
1406 def prepare(self, config_class):
1407 # This is the default, but that's the point.
1408 cp = config_class(allow_no_value=False)
1409 cp.add_section("section")
1410 cp.set("section", "option", None)
1411 sio = io.StringIO()
1412 cp.write(sio)
1413 return sio.getvalue()
1414
1415 def test_none_as_value_stringified(self):
1416 cp = configparser.ConfigParser(allow_no_value=False)
1417 cp.add_section("section")
1418 with self.assertRaises(TypeError):
1419 cp.set("section", "option", None)
1420
1421 def test_none_as_value_stringified_raw(self):
1422 output = self.prepare(configparser.RawConfigParser)
1423 self.assertEqual(output, self.expected_output)
1424
1425
1426 class ESC[4;38;5;81mSortedTestCase(ESC[4;38;5;149mRawConfigParserTestCase):
1427 dict_type = SortedDict
1428
1429 def test_sorted(self):
1430 cf = self.fromstring("[b]\n"
1431 "o4=1\n"
1432 "o3=2\n"
1433 "o2=3\n"
1434 "o1=4\n"
1435 "[a]\n"
1436 "k=v\n")
1437 output = io.StringIO()
1438 cf.write(output)
1439 self.assertEqual(output.getvalue(),
1440 "[a]\n"
1441 "k = v\n\n"
1442 "[b]\n"
1443 "o1 = 4\n"
1444 "o2 = 3\n"
1445 "o3 = 2\n"
1446 "o4 = 1\n\n")
1447
1448
1449 class ESC[4;38;5;81mCompatibleTestCase(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1450 config_class = configparser.RawConfigParser
1451 comment_prefixes = '#;'
1452 inline_comment_prefixes = ';'
1453
1454 def test_comment_handling(self):
1455 config_string = textwrap.dedent("""\
1456 [Commented Bar]
1457 baz=qwe ; a comment
1458 foo: bar # not a comment!
1459 # but this is a comment
1460 ; another comment
1461 quirk: this;is not a comment
1462 ; a space must precede an inline comment
1463 """)
1464 cf = self.fromstring(config_string)
1465 self.assertEqual(cf.get('Commented Bar', 'foo'),
1466 'bar # not a comment!')
1467 self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
1468 self.assertEqual(cf.get('Commented Bar', 'quirk'),
1469 'this;is not a comment')
1470
1471 class ESC[4;38;5;81mCopyTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1472 config_class = configparser.ConfigParser
1473
1474 def fromstring(self, string, defaults=None):
1475 cf = self.newconfig(defaults)
1476 cf.read_string(string)
1477 cf_copy = self.newconfig()
1478 cf_copy.read_dict(cf)
1479 # we have to clean up option duplicates that appeared because of
1480 # the magic DEFAULTSECT behaviour.
1481 for section in cf_copy.values():
1482 if section.name == self.default_section:
1483 continue
1484 for default, value in cf[self.default_section].items():
1485 if section[default] == value:
1486 del section[default]
1487 return cf_copy
1488
1489
1490 class ESC[4;38;5;81mFakeFile:
1491 def __init__(self):
1492 file_path = support.findfile("cfgparser.1", subdir="configdata")
1493 with open(file_path, encoding="utf-8") as f:
1494 self.lines = f.readlines()
1495 self.lines.reverse()
1496
1497 def readline(self):
1498 if len(self.lines):
1499 return self.lines.pop()
1500 return ''
1501
1502
1503 def readline_generator(f):
1504 """As advised in Doc/library/configparser.rst."""
1505 line = f.readline()
1506 while line:
1507 yield line
1508 line = f.readline()
1509
1510
1511 class ESC[4;38;5;81mReadFileTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1512 def test_file(self):
1513 file_paths = [support.findfile("cfgparser.1", subdir="configdata")]
1514 try:
1515 file_paths.append(file_paths[0].encode('utf8'))
1516 except UnicodeEncodeError:
1517 pass # unfortunately we can't test bytes on this path
1518 for file_path in file_paths:
1519 parser = configparser.ConfigParser()
1520 with open(file_path, encoding="utf-8") as f:
1521 parser.read_file(f)
1522 self.assertIn("Foo Bar", parser)
1523 self.assertIn("foo", parser["Foo Bar"])
1524 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1525
1526 def test_iterable(self):
1527 lines = textwrap.dedent("""
1528 [Foo Bar]
1529 foo=newbar""").strip().split('\n')
1530 parser = configparser.ConfigParser()
1531 parser.read_file(lines)
1532 self.assertIn("Foo Bar", parser)
1533 self.assertIn("foo", parser["Foo Bar"])
1534 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1535
1536 def test_readline_generator(self):
1537 """Issue #11670."""
1538 parser = configparser.ConfigParser()
1539 with self.assertRaises(TypeError):
1540 parser.read_file(FakeFile())
1541 parser.read_file(readline_generator(FakeFile()))
1542 self.assertIn("Foo Bar", parser)
1543 self.assertIn("foo", parser["Foo Bar"])
1544 self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1545
1546 def test_source_as_bytes(self):
1547 """Issue #18260."""
1548 lines = textwrap.dedent("""
1549 [badbad]
1550 [badbad]""").strip().split('\n')
1551 parser = configparser.ConfigParser()
1552 with self.assertRaises(configparser.DuplicateSectionError) as dse:
1553 parser.read_file(lines, source=b"badbad")
1554 self.assertEqual(
1555 str(dse.exception),
1556 "While reading from b'badbad' [line 2]: section 'badbad' "
1557 "already exists"
1558 )
1559 lines = textwrap.dedent("""
1560 [badbad]
1561 bad = bad
1562 bad = bad""").strip().split('\n')
1563 parser = configparser.ConfigParser()
1564 with self.assertRaises(configparser.DuplicateOptionError) as dse:
1565 parser.read_file(lines, source=b"badbad")
1566 self.assertEqual(
1567 str(dse.exception),
1568 "While reading from b'badbad' [line 3]: option 'bad' in section "
1569 "'badbad' already exists"
1570 )
1571 lines = textwrap.dedent("""
1572 [badbad]
1573 = bad""").strip().split('\n')
1574 parser = configparser.ConfigParser()
1575 with self.assertRaises(configparser.ParsingError) as dse:
1576 parser.read_file(lines, source=b"badbad")
1577 self.assertEqual(
1578 str(dse.exception),
1579 "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1580 )
1581 lines = textwrap.dedent("""
1582 [badbad
1583 bad = bad""").strip().split('\n')
1584 parser = configparser.ConfigParser()
1585 with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1586 parser.read_file(lines, source=b"badbad")
1587 self.assertEqual(
1588 str(dse.exception),
1589 "File contains no section headers.\nfile: b'badbad', line: 1\n"
1590 "'[badbad'"
1591 )
1592
1593
1594 class ESC[4;38;5;81mCoverageOneHundredTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1595 """Covers edge cases in the codebase."""
1596
1597 def test_duplicate_option_error(self):
1598 error = configparser.DuplicateOptionError('section', 'option')
1599 self.assertEqual(error.section, 'section')
1600 self.assertEqual(error.option, 'option')
1601 self.assertEqual(error.source, None)
1602 self.assertEqual(error.lineno, None)
1603 self.assertEqual(error.args, ('section', 'option', None, None))
1604 self.assertEqual(str(error), "Option 'option' in section 'section' "
1605 "already exists")
1606
1607 def test_interpolation_depth_error(self):
1608 error = configparser.InterpolationDepthError('option', 'section',
1609 'rawval')
1610 self.assertEqual(error.args, ('option', 'section', 'rawval'))
1611 self.assertEqual(error.option, 'option')
1612 self.assertEqual(error.section, 'section')
1613
1614 def test_parsing_error(self):
1615 with self.assertRaises(ValueError) as cm:
1616 configparser.ParsingError()
1617 self.assertEqual(str(cm.exception), "Required argument `source' not "
1618 "given.")
1619 with self.assertRaises(ValueError) as cm:
1620 configparser.ParsingError(source='source', filename='filename')
1621 self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1622 "and `source'. Use `source'.")
1623 error = configparser.ParsingError(filename='source')
1624 self.assertEqual(error.source, 'source')
1625 with warnings.catch_warnings(record=True) as w:
1626 warnings.simplefilter("always", DeprecationWarning)
1627 self.assertEqual(error.filename, 'source')
1628 error.filename = 'filename'
1629 self.assertEqual(error.source, 'filename')
1630 for warning in w:
1631 self.assertTrue(warning.category is DeprecationWarning)
1632
1633 def test_interpolation_validation(self):
1634 parser = configparser.ConfigParser()
1635 parser.read_string("""
1636 [section]
1637 invalid_percent = %
1638 invalid_reference = %(()
1639 invalid_variable = %(does_not_exist)s
1640 """)
1641 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1642 parser['section']['invalid_percent']
1643 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1644 "'(', found: '%'")
1645 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1646 parser['section']['invalid_reference']
1647 self.assertEqual(str(cm.exception), "bad interpolation variable "
1648 "reference '%(()'")
1649
1650 def test_readfp_deprecation(self):
1651 sio = io.StringIO("""
1652 [section]
1653 option = value
1654 """)
1655 parser = configparser.ConfigParser()
1656 with warnings.catch_warnings(record=True) as w:
1657 warnings.simplefilter("always", DeprecationWarning)
1658 parser.readfp(sio, filename='StringIO')
1659 for warning in w:
1660 self.assertTrue(warning.category is DeprecationWarning)
1661 self.assertEqual(len(parser), 2)
1662 self.assertEqual(parser['section']['option'], 'value')
1663
1664 def test_safeconfigparser_deprecation(self):
1665 with warnings.catch_warnings(record=True) as w:
1666 warnings.simplefilter("always", DeprecationWarning)
1667 parser = configparser.SafeConfigParser()
1668 for warning in w:
1669 self.assertTrue(warning.category is DeprecationWarning)
1670
1671 def test_legacyinterpolation_deprecation(self):
1672 with warnings.catch_warnings(record=True) as w:
1673 warnings.simplefilter("always", DeprecationWarning)
1674 configparser.LegacyInterpolation()
1675 self.assertGreaterEqual(len(w), 1)
1676 for warning in w:
1677 self.assertIs(warning.category, DeprecationWarning)
1678
1679 def test_sectionproxy_repr(self):
1680 parser = configparser.ConfigParser()
1681 parser.read_string("""
1682 [section]
1683 key = value
1684 """)
1685 self.assertEqual(repr(parser['section']), '<Section: section>')
1686
1687 def test_inconsistent_converters_state(self):
1688 parser = configparser.ConfigParser()
1689 import decimal
1690 parser.converters['decimal'] = decimal.Decimal
1691 parser.read_string("""
1692 [s1]
1693 one = 1
1694 [s2]
1695 two = 2
1696 """)
1697 self.assertIn('decimal', parser.converters)
1698 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1699 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1700 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1701 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1702 del parser.getdecimal
1703 with self.assertRaises(AttributeError):
1704 parser.getdecimal('s1', 'one')
1705 self.assertIn('decimal', parser.converters)
1706 del parser.converters['decimal']
1707 self.assertNotIn('decimal', parser.converters)
1708 with self.assertRaises(AttributeError):
1709 parser.getdecimal('s1', 'one')
1710 with self.assertRaises(AttributeError):
1711 parser['s1'].getdecimal('one')
1712 with self.assertRaises(AttributeError):
1713 parser['s2'].getdecimal('two')
1714
1715
1716 class ESC[4;38;5;81mExceptionPicklingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1717 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1718
1719 def test_error(self):
1720 import pickle
1721 e1 = configparser.Error('value')
1722 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1723 pickled = pickle.dumps(e1, proto)
1724 e2 = pickle.loads(pickled)
1725 self.assertEqual(e1.message, e2.message)
1726 self.assertEqual(repr(e1), repr(e2))
1727
1728 def test_nosectionerror(self):
1729 import pickle
1730 e1 = configparser.NoSectionError('section')
1731 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1732 pickled = pickle.dumps(e1, proto)
1733 e2 = pickle.loads(pickled)
1734 self.assertEqual(e1.message, e2.message)
1735 self.assertEqual(e1.args, e2.args)
1736 self.assertEqual(e1.section, e2.section)
1737 self.assertEqual(repr(e1), repr(e2))
1738
1739 def test_nooptionerror(self):
1740 import pickle
1741 e1 = configparser.NoOptionError('option', 'section')
1742 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1743 pickled = pickle.dumps(e1, proto)
1744 e2 = pickle.loads(pickled)
1745 self.assertEqual(e1.message, e2.message)
1746 self.assertEqual(e1.args, e2.args)
1747 self.assertEqual(e1.section, e2.section)
1748 self.assertEqual(e1.option, e2.option)
1749 self.assertEqual(repr(e1), repr(e2))
1750
1751 def test_duplicatesectionerror(self):
1752 import pickle
1753 e1 = configparser.DuplicateSectionError('section', 'source', 123)
1754 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1755 pickled = pickle.dumps(e1, proto)
1756 e2 = pickle.loads(pickled)
1757 self.assertEqual(e1.message, e2.message)
1758 self.assertEqual(e1.args, e2.args)
1759 self.assertEqual(e1.section, e2.section)
1760 self.assertEqual(e1.source, e2.source)
1761 self.assertEqual(e1.lineno, e2.lineno)
1762 self.assertEqual(repr(e1), repr(e2))
1763
1764 def test_duplicateoptionerror(self):
1765 import pickle
1766 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1767 123)
1768 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1769 pickled = pickle.dumps(e1, proto)
1770 e2 = pickle.loads(pickled)
1771 self.assertEqual(e1.message, e2.message)
1772 self.assertEqual(e1.args, e2.args)
1773 self.assertEqual(e1.section, e2.section)
1774 self.assertEqual(e1.option, e2.option)
1775 self.assertEqual(e1.source, e2.source)
1776 self.assertEqual(e1.lineno, e2.lineno)
1777 self.assertEqual(repr(e1), repr(e2))
1778
1779 def test_interpolationerror(self):
1780 import pickle
1781 e1 = configparser.InterpolationError('option', 'section', 'msg')
1782 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1783 pickled = pickle.dumps(e1, proto)
1784 e2 = pickle.loads(pickled)
1785 self.assertEqual(e1.message, e2.message)
1786 self.assertEqual(e1.args, e2.args)
1787 self.assertEqual(e1.section, e2.section)
1788 self.assertEqual(e1.option, e2.option)
1789 self.assertEqual(repr(e1), repr(e2))
1790
1791 def test_interpolationmissingoptionerror(self):
1792 import pickle
1793 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1794 'rawval', 'reference')
1795 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1796 pickled = pickle.dumps(e1, proto)
1797 e2 = pickle.loads(pickled)
1798 self.assertEqual(e1.message, e2.message)
1799 self.assertEqual(e1.args, e2.args)
1800 self.assertEqual(e1.section, e2.section)
1801 self.assertEqual(e1.option, e2.option)
1802 self.assertEqual(e1.reference, e2.reference)
1803 self.assertEqual(repr(e1), repr(e2))
1804
1805 def test_interpolationsyntaxerror(self):
1806 import pickle
1807 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
1808 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1809 pickled = pickle.dumps(e1, proto)
1810 e2 = pickle.loads(pickled)
1811 self.assertEqual(e1.message, e2.message)
1812 self.assertEqual(e1.args, e2.args)
1813 self.assertEqual(e1.section, e2.section)
1814 self.assertEqual(e1.option, e2.option)
1815 self.assertEqual(repr(e1), repr(e2))
1816
1817 def test_interpolationdeptherror(self):
1818 import pickle
1819 e1 = configparser.InterpolationDepthError('option', 'section',
1820 'rawval')
1821 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1822 pickled = pickle.dumps(e1, proto)
1823 e2 = pickle.loads(pickled)
1824 self.assertEqual(e1.message, e2.message)
1825 self.assertEqual(e1.args, e2.args)
1826 self.assertEqual(e1.section, e2.section)
1827 self.assertEqual(e1.option, e2.option)
1828 self.assertEqual(repr(e1), repr(e2))
1829
1830 def test_parsingerror(self):
1831 import pickle
1832 e1 = configparser.ParsingError('source')
1833 e1.append(1, 'line1')
1834 e1.append(2, 'line2')
1835 e1.append(3, 'line3')
1836 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1837 pickled = pickle.dumps(e1, proto)
1838 e2 = pickle.loads(pickled)
1839 self.assertEqual(e1.message, e2.message)
1840 self.assertEqual(e1.args, e2.args)
1841 self.assertEqual(e1.source, e2.source)
1842 self.assertEqual(e1.errors, e2.errors)
1843 self.assertEqual(repr(e1), repr(e2))
1844 e1 = configparser.ParsingError(filename='filename')
1845 e1.append(1, 'line1')
1846 e1.append(2, 'line2')
1847 e1.append(3, 'line3')
1848 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1849 pickled = pickle.dumps(e1, proto)
1850 e2 = pickle.loads(pickled)
1851 self.assertEqual(e1.message, e2.message)
1852 self.assertEqual(e1.args, e2.args)
1853 self.assertEqual(e1.source, e2.source)
1854 self.assertEqual(e1.errors, e2.errors)
1855 self.assertEqual(repr(e1), repr(e2))
1856
1857 def test_missingsectionheadererror(self):
1858 import pickle
1859 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
1860 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1861 pickled = pickle.dumps(e1, proto)
1862 e2 = pickle.loads(pickled)
1863 self.assertEqual(e1.message, e2.message)
1864 self.assertEqual(e1.args, e2.args)
1865 self.assertEqual(e1.line, e2.line)
1866 self.assertEqual(e1.source, e2.source)
1867 self.assertEqual(e1.lineno, e2.lineno)
1868 self.assertEqual(repr(e1), repr(e2))
1869
1870
1871 class ESC[4;38;5;81mInlineCommentStrippingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1872 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1873 delimiter occurs earlier without preceding space.."""
1874
1875 def test_stripping(self):
1876 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1877 '//'))
1878 cfg.read_string("""
1879 [section]
1880 k1 = v1;still v1
1881 k2 = v2 ;a comment
1882 k3 = v3 ; also a comment
1883 k4 = v4;still v4 ;a comment
1884 k5 = v5;still v5 ; also a comment
1885 k6 = v6;still v6; and still v6 ;a comment
1886 k7 = v7;still v7; and still v7 ; also a comment
1887
1888 [multiprefix]
1889 k1 = v1;still v1 #a comment ; yeah, pretty much
1890 k2 = v2 // this already is a comment ; continued
1891 k3 = v3;#//still v3# and still v3 ; a comment
1892 """)
1893 s = cfg['section']
1894 self.assertEqual(s['k1'], 'v1;still v1')
1895 self.assertEqual(s['k2'], 'v2')
1896 self.assertEqual(s['k3'], 'v3')
1897 self.assertEqual(s['k4'], 'v4;still v4')
1898 self.assertEqual(s['k5'], 'v5;still v5')
1899 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1900 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1901 s = cfg['multiprefix']
1902 self.assertEqual(s['k1'], 'v1;still v1')
1903 self.assertEqual(s['k2'], 'v2')
1904 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1905
1906
1907 class ESC[4;38;5;81mExceptionContextTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1908 """ Test that implementation details doesn't leak
1909 through raising exceptions. """
1910
1911 def test_get_basic_interpolation(self):
1912 parser = configparser.ConfigParser()
1913 parser.read_string("""
1914 [Paths]
1915 home_dir: /Users
1916 my_dir: %(home_dir1)s/lumberjack
1917 my_pictures: %(my_dir)s/Pictures
1918 """)
1919 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1920 with cm:
1921 parser.get('Paths', 'my_dir')
1922 self.assertIs(cm.exception.__suppress_context__, True)
1923
1924 def test_get_extended_interpolation(self):
1925 parser = configparser.ConfigParser(
1926 interpolation=configparser.ExtendedInterpolation())
1927 parser.read_string("""
1928 [Paths]
1929 home_dir: /Users
1930 my_dir: ${home_dir1}/lumberjack
1931 my_pictures: ${my_dir}/Pictures
1932 """)
1933 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1934 with cm:
1935 parser.get('Paths', 'my_dir')
1936 self.assertIs(cm.exception.__suppress_context__, True)
1937
1938 def test_missing_options(self):
1939 parser = configparser.ConfigParser()
1940 parser.read_string("""
1941 [Paths]
1942 home_dir: /Users
1943 """)
1944 with self.assertRaises(configparser.NoSectionError) as cm:
1945 parser.options('test')
1946 self.assertIs(cm.exception.__suppress_context__, True)
1947
1948 def test_missing_section(self):
1949 config = configparser.ConfigParser()
1950 with self.assertRaises(configparser.NoSectionError) as cm:
1951 config.set('Section1', 'an_int', '15')
1952 self.assertIs(cm.exception.__suppress_context__, True)
1953
1954 def test_remove_option(self):
1955 config = configparser.ConfigParser()
1956 with self.assertRaises(configparser.NoSectionError) as cm:
1957 config.remove_option('Section1', 'an_int')
1958 self.assertIs(cm.exception.__suppress_context__, True)
1959
1960
1961 class ESC[4;38;5;81mConvertersTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1962 """Introduced in 3.5, issue #18159."""
1963
1964 config_class = configparser.ConfigParser
1965
1966 def newconfig(self, defaults=None):
1967 instance = super().newconfig(defaults=defaults)
1968 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1969 if e.strip()]
1970 return instance
1971
1972 def test_converters(self):
1973 cfg = self.newconfig()
1974 self.assertIn('boolean', cfg.converters)
1975 self.assertIn('list', cfg.converters)
1976 self.assertIsNone(cfg.converters['int'])
1977 self.assertIsNone(cfg.converters['float'])
1978 self.assertIsNone(cfg.converters['boolean'])
1979 self.assertIsNotNone(cfg.converters['list'])
1980 self.assertEqual(len(cfg.converters), 4)
1981 with self.assertRaises(ValueError):
1982 cfg.converters[''] = lambda v: v
1983 with self.assertRaises(ValueError):
1984 cfg.converters[None] = lambda v: v
1985 cfg.read_string("""
1986 [s]
1987 str = string
1988 int = 1
1989 float = 0.5
1990 list = a b c d e f g
1991 bool = yes
1992 """)
1993 s = cfg['s']
1994 self.assertEqual(s['str'], 'string')
1995 self.assertEqual(s['int'], '1')
1996 self.assertEqual(s['float'], '0.5')
1997 self.assertEqual(s['list'], 'a b c d e f g')
1998 self.assertEqual(s['bool'], 'yes')
1999 self.assertEqual(cfg.get('s', 'str'), 'string')
2000 self.assertEqual(cfg.get('s', 'int'), '1')
2001 self.assertEqual(cfg.get('s', 'float'), '0.5')
2002 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
2003 self.assertEqual(cfg.get('s', 'bool'), 'yes')
2004 self.assertEqual(cfg.get('s', 'str'), 'string')
2005 self.assertEqual(cfg.getint('s', 'int'), 1)
2006 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
2007 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
2008 'e', 'f', 'g'])
2009 self.assertEqual(cfg.getboolean('s', 'bool'), True)
2010 self.assertEqual(s.get('str'), 'string')
2011 self.assertEqual(s.getint('int'), 1)
2012 self.assertEqual(s.getfloat('float'), 0.5)
2013 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
2014 'e', 'f', 'g'])
2015 self.assertEqual(s.getboolean('bool'), True)
2016 with self.assertRaises(AttributeError):
2017 cfg.getdecimal('s', 'float')
2018 with self.assertRaises(AttributeError):
2019 s.getdecimal('float')
2020 import decimal
2021 cfg.converters['decimal'] = decimal.Decimal
2022 self.assertIn('decimal', cfg.converters)
2023 self.assertIsNotNone(cfg.converters['decimal'])
2024 self.assertEqual(len(cfg.converters), 5)
2025 dec0_5 = decimal.Decimal('0.5')
2026 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
2027 self.assertEqual(s.getdecimal('float'), dec0_5)
2028 del cfg.converters['decimal']
2029 self.assertNotIn('decimal', cfg.converters)
2030 self.assertEqual(len(cfg.converters), 4)
2031 with self.assertRaises(AttributeError):
2032 cfg.getdecimal('s', 'float')
2033 with self.assertRaises(AttributeError):
2034 s.getdecimal('float')
2035 with self.assertRaises(KeyError):
2036 del cfg.converters['decimal']
2037 with self.assertRaises(KeyError):
2038 del cfg.converters['']
2039 with self.assertRaises(KeyError):
2040 del cfg.converters[None]
2041
2042
2043 class ESC[4;38;5;81mBlatantOverrideConvertersTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2044 """What if somebody overrode a getboolean()? We want to make sure that in
2045 this case the automatic converters do not kick in."""
2046
2047 config = """
2048 [one]
2049 one = false
2050 two = false
2051 three = long story short
2052
2053 [two]
2054 one = false
2055 two = false
2056 three = four
2057 """
2058
2059 def test_converters_at_init(self):
2060 cfg = configparser.ConfigParser(converters={'len': len})
2061 cfg.read_string(self.config)
2062 self._test_len(cfg)
2063 self.assertIsNotNone(cfg.converters['len'])
2064
2065 def test_inheritance(self):
2066 class ESC[4;38;5;81mStrangeConfigParser(ESC[4;38;5;149mconfigparserESC[4;38;5;149m.ESC[4;38;5;149mConfigParser):
2067 gettysburg = 'a historic borough in south central Pennsylvania'
2068
2069 def getboolean(self, section, option, *, raw=False, vars=None,
2070 fallback=configparser._UNSET):
2071 if section == option:
2072 return True
2073 return super().getboolean(section, option, raw=raw, vars=vars,
2074 fallback=fallback)
2075 def getlen(self, section, option, *, raw=False, vars=None,
2076 fallback=configparser._UNSET):
2077 return self._get_conv(section, option, len, raw=raw, vars=vars,
2078 fallback=fallback)
2079
2080 cfg = StrangeConfigParser()
2081 cfg.read_string(self.config)
2082 self._test_len(cfg)
2083 self.assertIsNone(cfg.converters['len'])
2084 self.assertTrue(cfg.getboolean('one', 'one'))
2085 self.assertTrue(cfg.getboolean('two', 'two'))
2086 self.assertFalse(cfg.getboolean('one', 'two'))
2087 self.assertFalse(cfg.getboolean('two', 'one'))
2088 cfg.converters['boolean'] = cfg._convert_to_boolean
2089 self.assertFalse(cfg.getboolean('one', 'one'))
2090 self.assertFalse(cfg.getboolean('two', 'two'))
2091 self.assertFalse(cfg.getboolean('one', 'two'))
2092 self.assertFalse(cfg.getboolean('two', 'one'))
2093
2094 def _test_len(self, cfg):
2095 self.assertEqual(len(cfg.converters), 4)
2096 self.assertIn('boolean', cfg.converters)
2097 self.assertIn('len', cfg.converters)
2098 self.assertNotIn('tysburg', cfg.converters)
2099 self.assertIsNone(cfg.converters['int'])
2100 self.assertIsNone(cfg.converters['float'])
2101 self.assertIsNone(cfg.converters['boolean'])
2102 self.assertEqual(cfg.getlen('one', 'one'), 5)
2103 self.assertEqual(cfg.getlen('one', 'two'), 5)
2104 self.assertEqual(cfg.getlen('one', 'three'), 16)
2105 self.assertEqual(cfg.getlen('two', 'one'), 5)
2106 self.assertEqual(cfg.getlen('two', 'two'), 5)
2107 self.assertEqual(cfg.getlen('two', 'three'), 4)
2108 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2109 with self.assertRaises(configparser.NoOptionError):
2110 cfg.getlen('two', 'four')
2111 self.assertEqual(cfg['one'].getlen('one'), 5)
2112 self.assertEqual(cfg['one'].getlen('two'), 5)
2113 self.assertEqual(cfg['one'].getlen('three'), 16)
2114 self.assertEqual(cfg['two'].getlen('one'), 5)
2115 self.assertEqual(cfg['two'].getlen('two'), 5)
2116 self.assertEqual(cfg['two'].getlen('three'), 4)
2117 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2118 self.assertEqual(cfg['two'].getlen('four'), None)
2119
2120 def test_instance_assignment(self):
2121 cfg = configparser.ConfigParser()
2122 cfg.getboolean = lambda section, option: True
2123 cfg.getlen = lambda section, option: len(cfg[section][option])
2124 cfg.read_string(self.config)
2125 self.assertEqual(len(cfg.converters), 3)
2126 self.assertIn('boolean', cfg.converters)
2127 self.assertNotIn('len', cfg.converters)
2128 self.assertIsNone(cfg.converters['int'])
2129 self.assertIsNone(cfg.converters['float'])
2130 self.assertIsNone(cfg.converters['boolean'])
2131 self.assertTrue(cfg.getboolean('one', 'one'))
2132 self.assertTrue(cfg.getboolean('two', 'two'))
2133 self.assertTrue(cfg.getboolean('one', 'two'))
2134 self.assertTrue(cfg.getboolean('two', 'one'))
2135 cfg.converters['boolean'] = cfg._convert_to_boolean
2136 self.assertFalse(cfg.getboolean('one', 'one'))
2137 self.assertFalse(cfg.getboolean('two', 'two'))
2138 self.assertFalse(cfg.getboolean('one', 'two'))
2139 self.assertFalse(cfg.getboolean('two', 'one'))
2140 self.assertEqual(cfg.getlen('one', 'one'), 5)
2141 self.assertEqual(cfg.getlen('one', 'two'), 5)
2142 self.assertEqual(cfg.getlen('one', 'three'), 16)
2143 self.assertEqual(cfg.getlen('two', 'one'), 5)
2144 self.assertEqual(cfg.getlen('two', 'two'), 5)
2145 self.assertEqual(cfg.getlen('two', 'three'), 4)
2146 # If a getter impl is assigned straight to the instance, it won't
2147 # be available on the section proxies.
2148 with self.assertRaises(AttributeError):
2149 self.assertEqual(cfg['one'].getlen('one'), 5)
2150 with self.assertRaises(AttributeError):
2151 self.assertEqual(cfg['two'].getlen('one'), 5)
2152
2153
2154 class ESC[4;38;5;81mMiscTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2155 def test__all__(self):
2156 support.check__all__(self, configparser, not_exported={"Error"})
2157
2158
2159 if __name__ == '__main__':
2160 unittest.main()