python (3.12.0)
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 # https://bugs.python.org/issue583248
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")
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")
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").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 https://bugs.python.org/issue511737
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")
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")
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")
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")
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")]
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(TypeError) as cm:
1616 configparser.ParsingError()
1617 error = configparser.ParsingError(source='source')
1618 self.assertEqual(error.source, 'source')
1619 error = configparser.ParsingError('source')
1620 self.assertEqual(error.source, 'source')
1621
1622 def test_interpolation_validation(self):
1623 parser = configparser.ConfigParser()
1624 parser.read_string("""
1625 [section]
1626 invalid_percent = %
1627 invalid_reference = %(()
1628 invalid_variable = %(does_not_exist)s
1629 """)
1630 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1631 parser['section']['invalid_percent']
1632 self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1633 "'(', found: '%'")
1634 with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1635 parser['section']['invalid_reference']
1636 self.assertEqual(str(cm.exception), "bad interpolation variable "
1637 "reference '%(()'")
1638
1639 def test_legacyinterpolation_deprecation(self):
1640 with warnings.catch_warnings(record=True) as w:
1641 warnings.simplefilter("always", DeprecationWarning)
1642 configparser.LegacyInterpolation()
1643 self.assertGreaterEqual(len(w), 1)
1644 for warning in w:
1645 self.assertIs(warning.category, DeprecationWarning)
1646
1647 def test_sectionproxy_repr(self):
1648 parser = configparser.ConfigParser()
1649 parser.read_string("""
1650 [section]
1651 key = value
1652 """)
1653 self.assertEqual(repr(parser['section']), '<Section: section>')
1654
1655 def test_inconsistent_converters_state(self):
1656 parser = configparser.ConfigParser()
1657 import decimal
1658 parser.converters['decimal'] = decimal.Decimal
1659 parser.read_string("""
1660 [s1]
1661 one = 1
1662 [s2]
1663 two = 2
1664 """)
1665 self.assertIn('decimal', parser.converters)
1666 self.assertEqual(parser.getdecimal('s1', 'one'), 1)
1667 self.assertEqual(parser.getdecimal('s2', 'two'), 2)
1668 self.assertEqual(parser['s1'].getdecimal('one'), 1)
1669 self.assertEqual(parser['s2'].getdecimal('two'), 2)
1670 del parser.getdecimal
1671 with self.assertRaises(AttributeError):
1672 parser.getdecimal('s1', 'one')
1673 self.assertIn('decimal', parser.converters)
1674 del parser.converters['decimal']
1675 self.assertNotIn('decimal', parser.converters)
1676 with self.assertRaises(AttributeError):
1677 parser.getdecimal('s1', 'one')
1678 with self.assertRaises(AttributeError):
1679 parser['s1'].getdecimal('one')
1680 with self.assertRaises(AttributeError):
1681 parser['s2'].getdecimal('two')
1682
1683
1684 class ESC[4;38;5;81mExceptionPicklingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1685 """Tests for issue #13760: ConfigParser exceptions are not picklable."""
1686
1687 def test_error(self):
1688 import pickle
1689 e1 = configparser.Error('value')
1690 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1691 pickled = pickle.dumps(e1, proto)
1692 e2 = pickle.loads(pickled)
1693 self.assertEqual(e1.message, e2.message)
1694 self.assertEqual(repr(e1), repr(e2))
1695
1696 def test_nosectionerror(self):
1697 import pickle
1698 e1 = configparser.NoSectionError('section')
1699 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1700 pickled = pickle.dumps(e1, proto)
1701 e2 = pickle.loads(pickled)
1702 self.assertEqual(e1.message, e2.message)
1703 self.assertEqual(e1.args, e2.args)
1704 self.assertEqual(e1.section, e2.section)
1705 self.assertEqual(repr(e1), repr(e2))
1706
1707 def test_nooptionerror(self):
1708 import pickle
1709 e1 = configparser.NoOptionError('option', 'section')
1710 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1711 pickled = pickle.dumps(e1, proto)
1712 e2 = pickle.loads(pickled)
1713 self.assertEqual(e1.message, e2.message)
1714 self.assertEqual(e1.args, e2.args)
1715 self.assertEqual(e1.section, e2.section)
1716 self.assertEqual(e1.option, e2.option)
1717 self.assertEqual(repr(e1), repr(e2))
1718
1719 def test_duplicatesectionerror(self):
1720 import pickle
1721 e1 = configparser.DuplicateSectionError('section', 'source', 123)
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(e1.args, e2.args)
1727 self.assertEqual(e1.section, e2.section)
1728 self.assertEqual(e1.source, e2.source)
1729 self.assertEqual(e1.lineno, e2.lineno)
1730 self.assertEqual(repr(e1), repr(e2))
1731
1732 def test_duplicateoptionerror(self):
1733 import pickle
1734 e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1735 123)
1736 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1737 pickled = pickle.dumps(e1, proto)
1738 e2 = pickle.loads(pickled)
1739 self.assertEqual(e1.message, e2.message)
1740 self.assertEqual(e1.args, e2.args)
1741 self.assertEqual(e1.section, e2.section)
1742 self.assertEqual(e1.option, e2.option)
1743 self.assertEqual(e1.source, e2.source)
1744 self.assertEqual(e1.lineno, e2.lineno)
1745 self.assertEqual(repr(e1), repr(e2))
1746
1747 def test_interpolationerror(self):
1748 import pickle
1749 e1 = configparser.InterpolationError('option', 'section', 'msg')
1750 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1751 pickled = pickle.dumps(e1, proto)
1752 e2 = pickle.loads(pickled)
1753 self.assertEqual(e1.message, e2.message)
1754 self.assertEqual(e1.args, e2.args)
1755 self.assertEqual(e1.section, e2.section)
1756 self.assertEqual(e1.option, e2.option)
1757 self.assertEqual(repr(e1), repr(e2))
1758
1759 def test_interpolationmissingoptionerror(self):
1760 import pickle
1761 e1 = configparser.InterpolationMissingOptionError('option', 'section',
1762 'rawval', 'reference')
1763 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1764 pickled = pickle.dumps(e1, proto)
1765 e2 = pickle.loads(pickled)
1766 self.assertEqual(e1.message, e2.message)
1767 self.assertEqual(e1.args, e2.args)
1768 self.assertEqual(e1.section, e2.section)
1769 self.assertEqual(e1.option, e2.option)
1770 self.assertEqual(e1.reference, e2.reference)
1771 self.assertEqual(repr(e1), repr(e2))
1772
1773 def test_interpolationsyntaxerror(self):
1774 import pickle
1775 e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
1776 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1777 pickled = pickle.dumps(e1, proto)
1778 e2 = pickle.loads(pickled)
1779 self.assertEqual(e1.message, e2.message)
1780 self.assertEqual(e1.args, e2.args)
1781 self.assertEqual(e1.section, e2.section)
1782 self.assertEqual(e1.option, e2.option)
1783 self.assertEqual(repr(e1), repr(e2))
1784
1785 def test_interpolationdeptherror(self):
1786 import pickle
1787 e1 = configparser.InterpolationDepthError('option', 'section',
1788 'rawval')
1789 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1790 pickled = pickle.dumps(e1, proto)
1791 e2 = pickle.loads(pickled)
1792 self.assertEqual(e1.message, e2.message)
1793 self.assertEqual(e1.args, e2.args)
1794 self.assertEqual(e1.section, e2.section)
1795 self.assertEqual(e1.option, e2.option)
1796 self.assertEqual(repr(e1), repr(e2))
1797
1798 def test_parsingerror(self):
1799 import pickle
1800 e1 = configparser.ParsingError('source')
1801 e1.append(1, 'line1')
1802 e1.append(2, 'line2')
1803 e1.append(3, 'line3')
1804 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1805 pickled = pickle.dumps(e1, proto)
1806 e2 = pickle.loads(pickled)
1807 self.assertEqual(e1.message, e2.message)
1808 self.assertEqual(e1.args, e2.args)
1809 self.assertEqual(e1.source, e2.source)
1810 self.assertEqual(e1.errors, e2.errors)
1811 self.assertEqual(repr(e1), repr(e2))
1812 e1 = configparser.ParsingError('filename')
1813 e1.append(1, 'line1')
1814 e1.append(2, 'line2')
1815 e1.append(3, 'line3')
1816 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1817 pickled = pickle.dumps(e1, proto)
1818 e2 = pickle.loads(pickled)
1819 self.assertEqual(e1.message, e2.message)
1820 self.assertEqual(e1.args, e2.args)
1821 self.assertEqual(e1.source, e2.source)
1822 self.assertEqual(e1.errors, e2.errors)
1823 self.assertEqual(repr(e1), repr(e2))
1824
1825 def test_missingsectionheadererror(self):
1826 import pickle
1827 e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
1828 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1829 pickled = pickle.dumps(e1, proto)
1830 e2 = pickle.loads(pickled)
1831 self.assertEqual(e1.message, e2.message)
1832 self.assertEqual(e1.args, e2.args)
1833 self.assertEqual(e1.line, e2.line)
1834 self.assertEqual(e1.source, e2.source)
1835 self.assertEqual(e1.lineno, e2.lineno)
1836 self.assertEqual(repr(e1), repr(e2))
1837
1838
1839 class ESC[4;38;5;81mInlineCommentStrippingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1840 """Tests for issue #14590: ConfigParser doesn't strip inline comment when
1841 delimiter occurs earlier without preceding space.."""
1842
1843 def test_stripping(self):
1844 cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1845 '//'))
1846 cfg.read_string("""
1847 [section]
1848 k1 = v1;still v1
1849 k2 = v2 ;a comment
1850 k3 = v3 ; also a comment
1851 k4 = v4;still v4 ;a comment
1852 k5 = v5;still v5 ; also a comment
1853 k6 = v6;still v6; and still v6 ;a comment
1854 k7 = v7;still v7; and still v7 ; also a comment
1855
1856 [multiprefix]
1857 k1 = v1;still v1 #a comment ; yeah, pretty much
1858 k2 = v2 // this already is a comment ; continued
1859 k3 = v3;#//still v3# and still v3 ; a comment
1860 """)
1861 s = cfg['section']
1862 self.assertEqual(s['k1'], 'v1;still v1')
1863 self.assertEqual(s['k2'], 'v2')
1864 self.assertEqual(s['k3'], 'v3')
1865 self.assertEqual(s['k4'], 'v4;still v4')
1866 self.assertEqual(s['k5'], 'v5;still v5')
1867 self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1868 self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1869 s = cfg['multiprefix']
1870 self.assertEqual(s['k1'], 'v1;still v1')
1871 self.assertEqual(s['k2'], 'v2')
1872 self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1873
1874
1875 class ESC[4;38;5;81mExceptionContextTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1876 """ Test that implementation details doesn't leak
1877 through raising exceptions. """
1878
1879 def test_get_basic_interpolation(self):
1880 parser = configparser.ConfigParser()
1881 parser.read_string("""
1882 [Paths]
1883 home_dir: /Users
1884 my_dir: %(home_dir1)s/lumberjack
1885 my_pictures: %(my_dir)s/Pictures
1886 """)
1887 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1888 with cm:
1889 parser.get('Paths', 'my_dir')
1890 self.assertIs(cm.exception.__suppress_context__, True)
1891
1892 def test_get_extended_interpolation(self):
1893 parser = configparser.ConfigParser(
1894 interpolation=configparser.ExtendedInterpolation())
1895 parser.read_string("""
1896 [Paths]
1897 home_dir: /Users
1898 my_dir: ${home_dir1}/lumberjack
1899 my_pictures: ${my_dir}/Pictures
1900 """)
1901 cm = self.assertRaises(configparser.InterpolationMissingOptionError)
1902 with cm:
1903 parser.get('Paths', 'my_dir')
1904 self.assertIs(cm.exception.__suppress_context__, True)
1905
1906 def test_missing_options(self):
1907 parser = configparser.ConfigParser()
1908 parser.read_string("""
1909 [Paths]
1910 home_dir: /Users
1911 """)
1912 with self.assertRaises(configparser.NoSectionError) as cm:
1913 parser.options('test')
1914 self.assertIs(cm.exception.__suppress_context__, True)
1915
1916 def test_missing_section(self):
1917 config = configparser.ConfigParser()
1918 with self.assertRaises(configparser.NoSectionError) as cm:
1919 config.set('Section1', 'an_int', '15')
1920 self.assertIs(cm.exception.__suppress_context__, True)
1921
1922 def test_remove_option(self):
1923 config = configparser.ConfigParser()
1924 with self.assertRaises(configparser.NoSectionError) as cm:
1925 config.remove_option('Section1', 'an_int')
1926 self.assertIs(cm.exception.__suppress_context__, True)
1927
1928
1929 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):
1930 """Introduced in 3.5, issue #18159."""
1931
1932 config_class = configparser.ConfigParser
1933
1934 def newconfig(self, defaults=None):
1935 instance = super().newconfig(defaults=defaults)
1936 instance.converters['list'] = lambda v: [e.strip() for e in v.split()
1937 if e.strip()]
1938 return instance
1939
1940 def test_converters(self):
1941 cfg = self.newconfig()
1942 self.assertIn('boolean', cfg.converters)
1943 self.assertIn('list', cfg.converters)
1944 self.assertIsNone(cfg.converters['int'])
1945 self.assertIsNone(cfg.converters['float'])
1946 self.assertIsNone(cfg.converters['boolean'])
1947 self.assertIsNotNone(cfg.converters['list'])
1948 self.assertEqual(len(cfg.converters), 4)
1949 with self.assertRaises(ValueError):
1950 cfg.converters[''] = lambda v: v
1951 with self.assertRaises(ValueError):
1952 cfg.converters[None] = lambda v: v
1953 cfg.read_string("""
1954 [s]
1955 str = string
1956 int = 1
1957 float = 0.5
1958 list = a b c d e f g
1959 bool = yes
1960 """)
1961 s = cfg['s']
1962 self.assertEqual(s['str'], 'string')
1963 self.assertEqual(s['int'], '1')
1964 self.assertEqual(s['float'], '0.5')
1965 self.assertEqual(s['list'], 'a b c d e f g')
1966 self.assertEqual(s['bool'], 'yes')
1967 self.assertEqual(cfg.get('s', 'str'), 'string')
1968 self.assertEqual(cfg.get('s', 'int'), '1')
1969 self.assertEqual(cfg.get('s', 'float'), '0.5')
1970 self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
1971 self.assertEqual(cfg.get('s', 'bool'), 'yes')
1972 self.assertEqual(cfg.get('s', 'str'), 'string')
1973 self.assertEqual(cfg.getint('s', 'int'), 1)
1974 self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
1975 self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
1976 'e', 'f', 'g'])
1977 self.assertEqual(cfg.getboolean('s', 'bool'), True)
1978 self.assertEqual(s.get('str'), 'string')
1979 self.assertEqual(s.getint('int'), 1)
1980 self.assertEqual(s.getfloat('float'), 0.5)
1981 self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
1982 'e', 'f', 'g'])
1983 self.assertEqual(s.getboolean('bool'), True)
1984 with self.assertRaises(AttributeError):
1985 cfg.getdecimal('s', 'float')
1986 with self.assertRaises(AttributeError):
1987 s.getdecimal('float')
1988 import decimal
1989 cfg.converters['decimal'] = decimal.Decimal
1990 self.assertIn('decimal', cfg.converters)
1991 self.assertIsNotNone(cfg.converters['decimal'])
1992 self.assertEqual(len(cfg.converters), 5)
1993 dec0_5 = decimal.Decimal('0.5')
1994 self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
1995 self.assertEqual(s.getdecimal('float'), dec0_5)
1996 del cfg.converters['decimal']
1997 self.assertNotIn('decimal', cfg.converters)
1998 self.assertEqual(len(cfg.converters), 4)
1999 with self.assertRaises(AttributeError):
2000 cfg.getdecimal('s', 'float')
2001 with self.assertRaises(AttributeError):
2002 s.getdecimal('float')
2003 with self.assertRaises(KeyError):
2004 del cfg.converters['decimal']
2005 with self.assertRaises(KeyError):
2006 del cfg.converters['']
2007 with self.assertRaises(KeyError):
2008 del cfg.converters[None]
2009
2010
2011 class ESC[4;38;5;81mBlatantOverrideConvertersTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2012 """What if somebody overrode a getboolean()? We want to make sure that in
2013 this case the automatic converters do not kick in."""
2014
2015 config = """
2016 [one]
2017 one = false
2018 two = false
2019 three = long story short
2020
2021 [two]
2022 one = false
2023 two = false
2024 three = four
2025 """
2026
2027 def test_converters_at_init(self):
2028 cfg = configparser.ConfigParser(converters={'len': len})
2029 cfg.read_string(self.config)
2030 self._test_len(cfg)
2031 self.assertIsNotNone(cfg.converters['len'])
2032
2033 def test_inheritance(self):
2034 class ESC[4;38;5;81mStrangeConfigParser(ESC[4;38;5;149mconfigparserESC[4;38;5;149m.ESC[4;38;5;149mConfigParser):
2035 gettysburg = 'a historic borough in south central Pennsylvania'
2036
2037 def getboolean(self, section, option, *, raw=False, vars=None,
2038 fallback=configparser._UNSET):
2039 if section == option:
2040 return True
2041 return super().getboolean(section, option, raw=raw, vars=vars,
2042 fallback=fallback)
2043 def getlen(self, section, option, *, raw=False, vars=None,
2044 fallback=configparser._UNSET):
2045 return self._get_conv(section, option, len, raw=raw, vars=vars,
2046 fallback=fallback)
2047
2048 cfg = StrangeConfigParser()
2049 cfg.read_string(self.config)
2050 self._test_len(cfg)
2051 self.assertIsNone(cfg.converters['len'])
2052 self.assertTrue(cfg.getboolean('one', 'one'))
2053 self.assertTrue(cfg.getboolean('two', 'two'))
2054 self.assertFalse(cfg.getboolean('one', 'two'))
2055 self.assertFalse(cfg.getboolean('two', 'one'))
2056 cfg.converters['boolean'] = cfg._convert_to_boolean
2057 self.assertFalse(cfg.getboolean('one', 'one'))
2058 self.assertFalse(cfg.getboolean('two', 'two'))
2059 self.assertFalse(cfg.getboolean('one', 'two'))
2060 self.assertFalse(cfg.getboolean('two', 'one'))
2061
2062 def _test_len(self, cfg):
2063 self.assertEqual(len(cfg.converters), 4)
2064 self.assertIn('boolean', cfg.converters)
2065 self.assertIn('len', cfg.converters)
2066 self.assertNotIn('tysburg', cfg.converters)
2067 self.assertIsNone(cfg.converters['int'])
2068 self.assertIsNone(cfg.converters['float'])
2069 self.assertIsNone(cfg.converters['boolean'])
2070 self.assertEqual(cfg.getlen('one', 'one'), 5)
2071 self.assertEqual(cfg.getlen('one', 'two'), 5)
2072 self.assertEqual(cfg.getlen('one', 'three'), 16)
2073 self.assertEqual(cfg.getlen('two', 'one'), 5)
2074 self.assertEqual(cfg.getlen('two', 'two'), 5)
2075 self.assertEqual(cfg.getlen('two', 'three'), 4)
2076 self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
2077 with self.assertRaises(configparser.NoOptionError):
2078 cfg.getlen('two', 'four')
2079 self.assertEqual(cfg['one'].getlen('one'), 5)
2080 self.assertEqual(cfg['one'].getlen('two'), 5)
2081 self.assertEqual(cfg['one'].getlen('three'), 16)
2082 self.assertEqual(cfg['two'].getlen('one'), 5)
2083 self.assertEqual(cfg['two'].getlen('two'), 5)
2084 self.assertEqual(cfg['two'].getlen('three'), 4)
2085 self.assertEqual(cfg['two'].getlen('four', 0), 0)
2086 self.assertEqual(cfg['two'].getlen('four'), None)
2087
2088 def test_instance_assignment(self):
2089 cfg = configparser.ConfigParser()
2090 cfg.getboolean = lambda section, option: True
2091 cfg.getlen = lambda section, option: len(cfg[section][option])
2092 cfg.read_string(self.config)
2093 self.assertEqual(len(cfg.converters), 3)
2094 self.assertIn('boolean', cfg.converters)
2095 self.assertNotIn('len', cfg.converters)
2096 self.assertIsNone(cfg.converters['int'])
2097 self.assertIsNone(cfg.converters['float'])
2098 self.assertIsNone(cfg.converters['boolean'])
2099 self.assertTrue(cfg.getboolean('one', 'one'))
2100 self.assertTrue(cfg.getboolean('two', 'two'))
2101 self.assertTrue(cfg.getboolean('one', 'two'))
2102 self.assertTrue(cfg.getboolean('two', 'one'))
2103 cfg.converters['boolean'] = cfg._convert_to_boolean
2104 self.assertFalse(cfg.getboolean('one', 'one'))
2105 self.assertFalse(cfg.getboolean('two', 'two'))
2106 self.assertFalse(cfg.getboolean('one', 'two'))
2107 self.assertFalse(cfg.getboolean('two', 'one'))
2108 self.assertEqual(cfg.getlen('one', 'one'), 5)
2109 self.assertEqual(cfg.getlen('one', 'two'), 5)
2110 self.assertEqual(cfg.getlen('one', 'three'), 16)
2111 self.assertEqual(cfg.getlen('two', 'one'), 5)
2112 self.assertEqual(cfg.getlen('two', 'two'), 5)
2113 self.assertEqual(cfg.getlen('two', 'three'), 4)
2114 # If a getter impl is assigned straight to the instance, it won't
2115 # be available on the section proxies.
2116 with self.assertRaises(AttributeError):
2117 self.assertEqual(cfg['one'].getlen('one'), 5)
2118 with self.assertRaises(AttributeError):
2119 self.assertEqual(cfg['two'].getlen('one'), 5)
2120
2121
2122 class ESC[4;38;5;81mMiscTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2123 def test__all__(self):
2124 support.check__all__(self, configparser, not_exported={"Error"})
2125
2126
2127 if __name__ == '__main__':
2128 unittest.main()