10
from test import support
12
class SortedDict(collections.UserDict):
15
return sorted(self.data.items())
18
return sorted(self.data.keys())
21
return [i[1] for i in self.items()]
24
return iter(self.items())
27
return iter(self.keys())
30
return iter(self.values())
35
class CfgParserTestCaseClass:
36
allow_no_value = False
37
delimiters = ('=', ':')
38
comment_prefixes = (';', '#')
39
inline_comment_prefixes = (';', '#')
40
empty_lines_in_values = True
41
dict_type = configparser._default_dict
43
default_section = configparser.DEFAULTSECT
44
interpolation = configparser._UNSET
46
def newconfig(self, defaults=None):
49
allow_no_value=self.allow_no_value,
50
delimiters=self.delimiters,
51
comment_prefixes=self.comment_prefixes,
52
inline_comment_prefixes=self.inline_comment_prefixes,
53
empty_lines_in_values=self.empty_lines_in_values,
54
dict_type=self.dict_type,
56
default_section=self.default_section,
57
interpolation=self.interpolation,
59
instance = self.config_class(**arguments)
62
def fromstring(self, string, defaults=None):
63
cf = self.newconfig(defaults)
64
cf.read_string(string)
67
class BasicTestCase(CfgParserTestCaseClass):
69
def basic_test(self, cf):
72
'Internationalized Stuff',
74
'Section\\with$weird%characters[\t',
77
'Spacey Bar From The Beginning',
81
if self.allow_no_value:
84
F = [('baz', 'qwe'), ('foo', 'bar3')]
91
L = cf.items('Spacey Bar From The Beginning')
96
L = [section for section in cf]
98
E.append(self.default_section)
101
L = cf['Spacey Bar From The Beginning'].items()
106
self.assertEqual(len(L), len(E))
107
for name, section in L:
108
eq(name, section.name)
109
eq(cf.defaults(), cf[self.default_section])
111
# The use of spaces in the section names serves as a
112
# regression test for SourceForge bug #583248:
113
# http://www.python.org/sf/583248
116
eq(cf.get('Foo Bar', 'foo'), 'bar1')
117
eq(cf.get('Spacey Bar', 'foo'), 'bar2')
118
eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
119
eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
120
eq(cf.get('Commented Bar', 'foo'), 'bar4')
121
eq(cf.get('Commented Bar', 'baz'), 'qwe')
122
eq(cf.get('Spaces', 'key with spaces'), 'value')
123
eq(cf.get('Spaces', 'another with spaces'), 'splat!')
124
eq(cf.getint('Types', 'int'), 42)
125
eq(cf.get('Types', 'int'), "42")
126
self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
127
eq(cf.get('Types', 'float'), "0.44")
128
eq(cf.getboolean('Types', 'boolean'), False)
129
eq(cf.get('Types', '123'), 'strange but acceptable')
130
if self.allow_no_value:
131
eq(cf.get('NoValue', 'option-without-value'), None)
133
# test vars= and fallback=
134
eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
135
eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
136
with self.assertRaises(configparser.NoSectionError):
137
cf.get('No Such Foo Bar', 'foo')
138
with self.assertRaises(configparser.NoOptionError):
139
cf.get('Foo Bar', 'no-such-foo')
140
eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
141
eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
142
eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
143
eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
144
eq(cf.getint('Types', 'int', fallback=18), 42)
145
eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
146
eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
147
with self.assertRaises(configparser.NoOptionError):
148
cf.getint('Types', 'no-such-int')
149
self.assertAlmostEqual(cf.getfloat('Types', 'float',
151
self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
153
eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
154
with self.assertRaises(configparser.NoOptionError):
155
cf.getfloat('Types', 'no-such-float')
156
eq(cf.getboolean('Types', 'boolean', fallback=True), False)
157
eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
159
eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
160
with self.assertRaises(configparser.NoOptionError):
161
cf.getboolean('Types', 'no-such-boolean')
162
eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
163
if self.allow_no_value:
164
eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
165
eq(cf.get('NoValue', 'no-such-option-without-value',
166
fallback=False), False)
169
eq(cf['Foo Bar']['foo'], 'bar1')
170
eq(cf['Spacey Bar']['foo'], 'bar2')
171
section = cf['Spacey Bar From The Beginning']
172
eq(section.name, 'Spacey Bar From The Beginning')
173
self.assertIs(section.parser, cf)
174
with self.assertRaises(AttributeError):
175
section.name = 'Name is read-only'
176
with self.assertRaises(AttributeError):
177
section.parser = 'Parser is read-only'
178
eq(section['foo'], 'bar3')
179
eq(section['baz'], 'qwe')
180
eq(cf['Commented Bar']['foo'], 'bar4')
181
eq(cf['Commented Bar']['baz'], 'qwe')
182
eq(cf['Spaces']['key with spaces'], 'value')
183
eq(cf['Spaces']['another with spaces'], 'splat!')
184
eq(cf['Long Line']['foo'],
185
'this line is much, much longer than my editor\nlikes it.')
186
if self.allow_no_value:
187
eq(cf['NoValue']['option-without-value'], None)
188
# test vars= and fallback=
189
eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
190
eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
191
eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
192
with self.assertRaises(KeyError):
193
cf['No Such Foo Bar']['foo']
194
with self.assertRaises(KeyError):
195
cf['Foo Bar']['no-such-foo']
196
with self.assertRaises(KeyError):
197
cf['No Such Foo Bar'].get('foo', fallback='baz')
198
eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
199
eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
200
eq(cf['Foo Bar'].get('no-such-foo'), None)
201
eq(cf['Spacey Bar'].get('foo', None), 'bar2')
202
eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
203
with self.assertRaises(KeyError):
204
cf['No Such Spacey Bar'].get('foo', None)
205
eq(cf['Types'].getint('int', 18), 42)
206
eq(cf['Types'].getint('int', fallback=18), 42)
207
eq(cf['Types'].getint('no-such-int', 18), 18)
208
eq(cf['Types'].getint('no-such-int', fallback=18), 18)
209
eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
210
eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
211
eq(cf['Types'].getint('no-such-int'), None)
212
self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
213
self.assertAlmostEqual(cf['Types'].getfloat('float',
215
self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
216
self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
218
eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
219
eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
220
eq(cf['Types'].getfloat('no-such-float'), None)
221
eq(cf['Types'].getboolean('boolean', True), False)
222
eq(cf['Types'].getboolean('boolean', fallback=True), False)
223
eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
224
eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
226
eq(cf['Types'].getboolean('no-such-boolean', True), True)
227
eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
228
eq(cf['Types'].getboolean('no-such-boolean'), None)
229
if self.allow_no_value:
230
eq(cf['NoValue'].get('option-without-value', False), None)
231
eq(cf['NoValue'].get('option-without-value', fallback=False), None)
232
eq(cf['NoValue'].get('no-such-option-without-value', False), False)
233
eq(cf['NoValue'].get('no-such-option-without-value',
234
fallback=False), False)
236
# Make sure the right things happen for remove_section() and
237
# remove_option(); added to include check for SourceForge bug #123324.
239
cf[self.default_section]['this_value'] = '1'
240
cf[self.default_section]['that_value'] = '2'
243
self.assertTrue(cf.remove_section('Spaces'))
244
self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
245
self.assertFalse(cf.remove_section('Spaces'))
246
self.assertFalse(cf.remove_section(self.default_section))
247
self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
248
"remove_option() failed to report existence of option")
249
self.assertFalse(cf.has_option('Foo Bar', 'foo'),
250
"remove_option() failed to remove option")
251
self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
252
"remove_option() failed to report non-existence of option"
254
self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
255
self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
256
self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
257
self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
258
self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
260
with self.assertRaises(configparser.NoSectionError) as cm:
261
cf.remove_option('No Such Section', 'foo')
262
self.assertEqual(cm.exception.args, ('No Such Section',))
264
eq(cf.get('Long Line', 'foo'),
265
'this line is much, much longer than my editor\nlikes it.')
269
self.assertFalse('Types' in cf)
270
with self.assertRaises(KeyError):
272
with self.assertRaises(ValueError):
273
del cf[self.default_section]
274
del cf['Spacey Bar']['foo']
275
self.assertFalse('foo' in cf['Spacey Bar'])
276
with self.assertRaises(KeyError):
277
del cf['Spacey Bar']['foo']
278
self.assertTrue('that_value' in cf['Spacey Bar'])
279
with self.assertRaises(KeyError):
280
del cf['Spacey Bar']['that_value']
281
del cf[self.default_section]['that_value']
282
self.assertFalse('that_value' in cf['Spacey Bar'])
283
with self.assertRaises(KeyError):
284
del cf[self.default_section]['that_value']
285
with self.assertRaises(KeyError):
286
del cf['No Such Section']['foo']
288
# Don't add new asserts below in this method as most of the options
289
# and sections are now removed.
291
def test_basic(self):
297
[Spacey Bar From The Beginning]
301
foo{0[1]} bar4 {1[1]} comment
302
baz{0[0]}qwe {1[0]}another one
304
foo{0[1]} this line is much, much longer than my editor
306
[Section\\with$weird%characters[\t]
307
[Internationalized Stuff]
308
foo[bg]{0[1]} Bulgarian
313
key with spaces {0[1]} value
314
another with spaces {0[0]} splat!
319
123 {0[1]} strange but acceptable
320
""".format(self.delimiters, self.comment_prefixes)
321
if self.allow_no_value:
324
"option-without-value\n"
326
cf = self.fromstring(config_string)
329
with self.assertRaises(configparser.DuplicateOptionError):
330
cf.read_string(textwrap.dedent("""\
331
[Duplicate Options Here]
332
option {0[0]} with a value
333
option {0[1]} with another value
334
""".format(self.delimiters)))
335
with self.assertRaises(configparser.DuplicateSectionError):
336
cf.read_string(textwrap.dedent("""\
337
[And Now For Something]
338
completely different {0[0]} True
339
[And Now For Something]
341
""".format(self.delimiters)))
343
cf.read_string(textwrap.dedent("""\
344
[Duplicate Options Here]
345
option {0[0]} with a value
346
option {0[1]} with another value
347
""".format(self.delimiters)))
349
cf.read_string(textwrap.dedent("""\
350
[And Now For Something]
351
completely different {0[0]} True
352
[And Now For Something]
354
""".format(self.delimiters)))
356
def test_basic_from_dict(self):
364
"Spacey Bar From The Beginning": {
373
"foo": "this line is much, much longer than my editor\nlikes "
376
"Section\\with$weird%characters[\t": {
378
"Internationalized Stuff": {
379
"foo[bg]": "Bulgarian",
381
"foo[en]": "English",
382
"foo[de]": "Deutsch",
385
"key with spaces": "value",
386
"another with spaces": "splat!",
392
123: "strange but acceptable",
395
if self.allow_no_value:
398
"option-without-value": None,
401
cf = self.newconfig()
405
with self.assertRaises(configparser.DuplicateSectionError):
407
'1': {'key': 'value'},
408
1: {'key2': 'value2'},
410
with self.assertRaises(configparser.DuplicateOptionError):
412
"Duplicate Options Here": {
413
'option': 'with a value',
414
'OPTION': 'with another value',
419
'section': {'key': 'value'},
420
'SECTION': {'key2': 'value2'},
423
"Duplicate Options Here": {
424
'option': 'with a value',
425
'OPTION': 'with another value',
429
def test_case_sensitivity(self):
430
cf = self.newconfig()
436
eq = self.assertEqual
437
eq(L, ["A", "B", "a"])
438
cf.set("a", "B", "value")
439
eq(cf.options("a"), ["b"])
440
eq(cf.get("a", "b"), "value",
441
"could not locate option, expecting case-insensitive option names")
442
with self.assertRaises(configparser.NoSectionError):
443
# section names are case-sensitive
444
cf.set("b", "A", "value")
445
self.assertTrue(cf.has_option("a", "b"))
446
self.assertFalse(cf.has_option("b", "b"))
447
cf.set("A", "A-B", "A-B value")
448
for opt in ("a-b", "A-b", "a-B", "A-B"):
450
cf.has_option("A", opt),
451
"has_option() returned false for option which should exist")
452
eq(cf.options("A"), ["a-b"])
453
eq(cf.options("a"), ["b"])
454
cf.remove_option("a", "B")
455
eq(cf.options("a"), [])
458
cf = self.fromstring(
459
"[MySection]\nOption{} first line \n\tsecond line \n".format(
461
eq(cf.options("MySection"), ["option"])
462
eq(cf.get("MySection", "Option"), "first line\nsecond line")
465
cf = self.fromstring("[section]\n"
466
"nekey{}nevalue\n".format(self.delimiters[0]),
467
defaults={"key":"value"})
468
self.assertTrue(cf.has_option("section", "Key"))
471
def test_case_sensitivity_mapping_access(self):
472
cf = self.newconfig()
474
cf["a"] = {"B": "value"}
476
L = [section for section in cf]
478
eq = self.assertEqual
479
elem_eq = self.assertCountEqual
480
eq(L, sorted(["A", "B", self.default_section, "a"]))
481
eq(cf["a"].keys(), {"b"})
482
eq(cf["a"]["b"], "value",
483
"could not locate option, expecting case-insensitive option names")
484
with self.assertRaises(KeyError):
485
# section names are case-sensitive
486
cf["b"]["A"] = "value"
487
self.assertTrue("b" in cf["a"])
488
cf["A"]["A-B"] = "A-B value"
489
for opt in ("a-b", "A-b", "a-B", "A-B"):
492
"has_option() returned false for option which should exist")
493
eq(cf["A"].keys(), {"a-b"})
494
eq(cf["a"].keys(), {"b"})
496
elem_eq(cf["a"].keys(), {})
499
cf = self.fromstring(
500
"[MySection]\nOption{} first line \n\tsecond line \n".format(
502
eq(cf["MySection"].keys(), {"option"})
503
eq(cf["MySection"]["Option"], "first line\nsecond line")
506
cf = self.fromstring("[section]\n"
507
"nekey{}nevalue\n".format(self.delimiters[0]),
508
defaults={"key":"value"})
509
self.assertTrue("Key" in cf["section"])
511
def test_default_case_sensitivity(self):
512
cf = self.newconfig({"foo": "Bar"})
514
cf.get(self.default_section, "Foo"), "Bar",
515
"could not locate option, expecting case-insensitive option names")
516
cf = self.newconfig({"Foo": "Bar"})
518
cf.get(self.default_section, "Foo"), "Bar",
519
"could not locate option, expecting case-insensitive defaults")
521
def test_parse_errors(self):
522
cf = self.newconfig()
523
self.parse_error(cf, configparser.ParsingError,
525
"{}val-without-opt-name\n".format(self.delimiters[0]))
526
self.parse_error(cf, configparser.ParsingError,
528
"{}val-without-opt-name\n".format(self.delimiters[1]))
529
e = self.parse_error(cf, configparser.MissingSectionHeaderError,
531
self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
532
if not self.allow_no_value:
533
e = self.parse_error(cf, configparser.ParsingError,
534
"[Foo]\n wrong-indent\n")
535
self.assertEqual(e.args, ('<???>',))
536
# read_file on a real file
537
tricky = support.findfile("cfgparser.3")
538
if self.delimiters[0] == '=':
539
error = configparser.ParsingError
542
error = configparser.MissingSectionHeaderError
543
expected = (tricky, 1,
544
' # INI with as many tricky parts as possible\n')
545
with open(tricky, encoding='utf-8') as f:
546
e = self.parse_error(cf, error, f)
547
self.assertEqual(e.args, expected)
549
def parse_error(self, cf, exc, src):
550
if hasattr(src, 'readline'):
553
sio = io.StringIO(src)
554
with self.assertRaises(exc) as cm:
558
def test_query_errors(self):
559
cf = self.newconfig()
560
self.assertEqual(cf.sections(), [],
561
"new ConfigParser should have no defined sections")
562
self.assertFalse(cf.has_section("Foo"),
563
"new ConfigParser should have no acknowledged "
565
with self.assertRaises(configparser.NoSectionError):
567
with self.assertRaises(configparser.NoSectionError):
568
cf.set("foo", "bar", "value")
569
e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
570
self.assertEqual(e.args, ("foo",))
571
cf.add_section("foo")
572
e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
573
self.assertEqual(e.args, ("bar", "foo"))
575
def get_error(self, cf, exc, section, option):
577
cf.get(section, option)
581
self.fail("expected exception type %s.%s"
582
% (exc.__module__, exc.__name__))
584
def test_boolean(self):
585
cf = self.fromstring(
601
"E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
603
for x in range(1, 5):
604
self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
605
self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
606
self.assertRaises(ValueError,
607
cf.getboolean, 'BOOLTEST', 'e%d' % x)
609
def test_weird_errors(self):
610
cf = self.newconfig()
611
cf.add_section("Foo")
612
with self.assertRaises(configparser.DuplicateSectionError) as cm:
613
cf.add_section("Foo")
615
self.assertEqual(str(e), "Section 'Foo' already exists")
616
self.assertEqual(e.args, ("Foo", None, None))
619
with self.assertRaises(configparser.DuplicateSectionError) as cm:
620
cf.read_string(textwrap.dedent("""\
622
will this be added{equals}True
624
what about this{equals}True
626
oops{equals}this won't
627
""".format(equals=self.delimiters[0])), source='<foo-bar>')
629
self.assertEqual(str(e), "While reading from '<foo-bar>' "
630
"[line 5]: section 'Foo' already exists")
631
self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
633
with self.assertRaises(configparser.DuplicateOptionError) as cm:
634
cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
636
self.assertEqual(str(e), "While reading from '<dict>': option "
637
"'opt' in section 'Bar' already exists")
638
self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
640
def test_write(self):
643
"foo{0[0]} this line is much, much longer than my editor\n"
645
"[{default_section}]\n"
646
"foo{0[1]} another very\n"
648
"[Long Line - With Comments!]\n"
649
"test {0[1]} we {comment} can\n"
650
" also {comment} place\n"
651
" comments {comment} in\n"
652
" multiline {comment} values"
653
"\n".format(self.delimiters, comment=self.comment_prefixes[0],
654
default_section=self.default_section)
656
if self.allow_no_value:
659
"option-without-value\n"
662
cf = self.fromstring(config_string)
663
for space_around_delimiters in (True, False):
664
output = io.StringIO()
665
cf.write(output, space_around_delimiters=space_around_delimiters)
666
delimiter = self.delimiters[0]
667
if space_around_delimiters:
668
delimiter = " {} ".format(delimiter)
670
"[{default_section}]\n"
671
"foo{equals}another very\n"
675
"foo{equals}this line is much, much longer than my editor\n"
678
"[Long Line - With Comments!]\n"
683
"\n".format(equals=delimiter,
684
default_section=self.default_section)
686
if self.allow_no_value:
689
"option-without-value\n"
692
self.assertEqual(output.getvalue(), expect_string)
694
def test_set_string_types(self):
695
cf = self.fromstring("[sect]\n"
696
"option1{eq}foo\n".format(eq=self.delimiters[0]))
697
# Check that we don't get an exception when setting values in
698
# an existing section using strings:
701
cf.set("sect", "option1", "splat")
702
cf.set("sect", "option1", mystr("splat"))
703
cf.set("sect", "option2", "splat")
704
cf.set("sect", "option2", mystr("splat"))
705
cf.set("sect", "option1", "splat")
706
cf.set("sect", "option2", "splat")
708
def test_read_returns_file_list(self):
709
if self.delimiters[0] != '=':
710
# skip reading the file if we're using an incompatible format
712
file1 = support.findfile("cfgparser.1")
713
# check when we pass a mix of readable and non-readable files:
714
cf = self.newconfig()
715
parsed_files = cf.read([file1, "nonexistent-file"])
716
self.assertEqual(parsed_files, [file1])
717
self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
718
# check when we pass only a filename:
719
cf = self.newconfig()
720
parsed_files = cf.read(file1)
721
self.assertEqual(parsed_files, [file1])
722
self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
723
# check when we pass only missing files:
724
cf = self.newconfig()
725
parsed_files = cf.read(["nonexistent-file"])
726
self.assertEqual(parsed_files, [])
727
# check when we pass no files:
728
cf = self.newconfig()
729
parsed_files = cf.read([])
730
self.assertEqual(parsed_files, [])
732
# shared by subclasses
733
def get_interpolation_config(self):
734
return self.fromstring(
736
"bar{equals}something %(with1)s interpolation (1 step)\n"
737
"bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
738
"bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
739
"bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
740
"with11{equals}%(with10)s\n"
741
"with10{equals}%(with9)s\n"
742
"with9{equals}%(with8)s\n"
743
"with8{equals}%(With7)s\n"
744
"with7{equals}%(WITH6)s\n"
745
"with6{equals}%(with5)s\n"
746
"With5{equals}%(with4)s\n"
747
"WITH4{equals}%(with3)s\n"
748
"with3{equals}%(with2)s\n"
749
"with2{equals}%(with1)s\n"
750
"with1{equals}with\n"
752
"[Mutual Recursion]\n"
753
"foo{equals}%(bar)s\n"
754
"bar{equals}%(foo)s\n"
756
"[Interpolation Error]\n"
757
# no definition for 'reference'
758
"name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
760
def check_items_config(self, expected):
761
cf = self.fromstring("""
763
name {0[0]} %(value)s
765
getdefault{0[1]} |%(default)s|
766
""".format(self.delimiters), defaults={"default": "<default>"})
767
L = list(cf.items("section", vars={'value': 'value'}))
769
self.assertEqual(L, expected)
770
with self.assertRaises(configparser.NoSectionError):
771
cf.items("no such section")
773
def test_popitem(self):
774
cf = self.fromstring("""
781
""".format(self.delimiters), defaults={"default": "<default>"})
782
self.assertEqual(cf.popitem()[0], 'section1')
783
self.assertEqual(cf.popitem()[0], 'section2')
784
self.assertEqual(cf.popitem()[0], 'section3')
785
with self.assertRaises(KeyError):
788
def test_clear(self):
789
cf = self.newconfig({"foo": "Bar"})
791
cf.get(self.default_section, "Foo"), "Bar",
792
"could not locate option, expecting case-insensitive option names")
793
cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
794
self.assertEqual(cf.sections(), ['zing'])
795
self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
797
self.assertEqual(set(cf.sections()), set())
798
self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
800
def test_setitem(self):
801
cf = self.fromstring("""
808
""".format(self.delimiters), defaults={"nameD": "valueD"})
809
self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
810
self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
811
self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
812
self.assertEqual(cf['section1']['name1'], 'value1')
813
self.assertEqual(cf['section2']['name2'], 'value2')
814
self.assertEqual(cf['section3']['name3'], 'value3')
815
self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
816
cf['section2'] = {'name22': 'value22'}
817
self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
818
self.assertEqual(cf['section2']['name22'], 'value22')
819
self.assertNotIn('name2', cf['section2'])
820
self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
822
self.assertEqual(set(cf['section3'].keys()), {'named'})
823
self.assertNotIn('name3', cf['section3'])
824
self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
825
cf[self.default_section] = {}
826
self.assertEqual(set(cf[self.default_section].keys()), set())
827
self.assertEqual(set(cf['section1'].keys()), {'name1'})
828
self.assertEqual(set(cf['section2'].keys()), {'name22'})
829
self.assertEqual(set(cf['section3'].keys()), set())
830
self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
833
class StrictTestCase(BasicTestCase, unittest.TestCase):
834
config_class = configparser.RawConfigParser
838
class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
839
config_class = configparser.ConfigParser
841
def test_interpolation(self):
842
cf = self.get_interpolation_config()
843
eq = self.assertEqual
844
eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
845
eq(cf.get("Foo", "bar9"),
846
"something with lots of interpolation (9 steps)")
847
eq(cf.get("Foo", "bar10"),
848
"something with lots of interpolation (10 steps)")
849
e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
850
if self.interpolation == configparser._UNSET:
851
self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
852
elif isinstance(self.interpolation, configparser.LegacyInterpolation):
853
self.assertEqual(e.args, ("bar11", "Foo",
854
"something %(with11)s lots of interpolation (11 steps)"))
856
def test_interpolation_missing_value(self):
857
cf = self.get_interpolation_config()
858
e = self.get_error(cf, configparser.InterpolationMissingOptionError,
859
"Interpolation Error", "name")
860
self.assertEqual(e.reference, "reference")
861
self.assertEqual(e.section, "Interpolation Error")
862
self.assertEqual(e.option, "name")
863
if self.interpolation == configparser._UNSET:
864
self.assertEqual(e.args, ('name', 'Interpolation Error',
866
elif isinstance(self.interpolation, configparser.LegacyInterpolation):
867
self.assertEqual(e.args, ('name', 'Interpolation Error',
868
'%(reference)s', 'reference'))
870
def test_items(self):
871
self.check_items_config([('default', '<default>'),
872
('getdefault', '|<default>|'),
877
def test_safe_interpolation(self):
878
# See http://www.python.org/sf/511737
879
cf = self.fromstring("[section]\n"
881
"option2{eq}%(option1)s/xxx\n"
882
"ok{eq}%(option1)s/%%s\n"
883
"not_ok{eq}%(option2)s/%%s".format(
884
eq=self.delimiters[0]))
885
self.assertEqual(cf.get("section", "ok"), "xxx/%s")
886
if self.interpolation == configparser._UNSET:
887
self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
888
elif isinstance(self.interpolation, configparser.LegacyInterpolation):
889
with self.assertRaises(TypeError):
890
cf.get("section", "not_ok")
892
def test_set_malformatted_interpolation(self):
893
cf = self.fromstring("[sect]\n"
894
"option1{eq}foo\n".format(eq=self.delimiters[0]))
896
self.assertEqual(cf.get('sect', "option1"), "foo")
898
self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
899
self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
900
self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
902
self.assertEqual(cf.get('sect', "option1"), "foo")
904
# bug #5741: double percents are *not* malformed
905
cf.set("sect", "option2", "foo%%bar")
906
self.assertEqual(cf.get("sect", "option2"), "foo%bar")
908
def test_set_nonstring_types(self):
909
cf = self.fromstring("[sect]\n"
910
"option1{eq}foo\n".format(eq=self.delimiters[0]))
911
# Check that we get a TypeError when setting non-string values
912
# in an existing section:
913
self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
914
self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
915
self.assertRaises(TypeError, cf.set, "sect", "option1", object())
916
self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
917
self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
918
self.assertRaises(TypeError, cf.set, "sect", "option2", object())
919
self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
920
self.assertRaises(TypeError, cf.add_section, 123)
922
def test_add_section_default(self):
923
cf = self.newconfig()
924
self.assertRaises(ValueError, cf.add_section, self.default_section)
927
class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
928
config_class = configparser.ConfigParser
930
ini = textwrap.dedent("""
934
three = ${common:one} * 3
937
sixteen = ${numbers:two} * 8
940
def assertMatchesIni(self, cf):
941
self.assertEqual(cf['numbers']['one'], '1')
942
self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
943
self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
944
self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
946
def test_no_interpolation(self):
947
cf = self.fromstring(self.ini)
948
self.assertMatchesIni(cf)
950
def test_empty_case(self):
951
cf = self.newconfig()
952
self.assertIsNone(cf.read_string(""))
954
def test_none_as_default_interpolation(self):
955
class CustomConfigParser(configparser.ConfigParser):
956
_DEFAULT_INTERPOLATION = None
958
cf = CustomConfigParser()
959
cf.read_string(self.ini)
960
self.assertMatchesIni(cf)
963
class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
964
config_class = configparser.ConfigParser
965
interpolation = configparser.LegacyInterpolation()
967
def test_set_malformatted_interpolation(self):
968
cf = self.fromstring("[sect]\n"
969
"option1{eq}foo\n".format(eq=self.delimiters[0]))
971
self.assertEqual(cf.get('sect', "option1"), "foo")
973
cf.set("sect", "option1", "%foo")
974
self.assertEqual(cf.get('sect', "option1"), "%foo")
975
cf.set("sect", "option1", "foo%")
976
self.assertEqual(cf.get('sect', "option1"), "foo%")
977
cf.set("sect", "option1", "f%oo")
978
self.assertEqual(cf.get('sect', "option1"), "f%oo")
980
# bug #5741: double percents are *not* malformed
981
cf.set("sect", "option2", "foo%%bar")
982
self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
984
class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
985
delimiters = (':=', '$')
986
comment_prefixes = ('//', '"')
987
inline_comment_prefixes = ('//', '"')
989
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
990
default_section = 'general'
992
class MultilineValuesTestCase(BasicTestCase, unittest.TestCase):
993
config_class = configparser.ConfigParser
994
wonderful_spam = ("I'm having spam spam spam spam "
995
"spam spam spam beaked beans spam "
996
"spam spam and spam!").replace(' ', '\t\n')
999
cf = self.newconfig()
1000
for i in range(100):
1001
s = 'section{}'.format(i)
1004
cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
1005
with open(support.TESTFN, 'w') as f:
1009
os.unlink(support.TESTFN)
1011
def test_dominating_multiline_values(self):
1012
# We're reading from file because this is where the code changed
1013
# during performance updates in Python 3.2
1014
cf_from_file = self.newconfig()
1015
with open(support.TESTFN) as f:
1016
cf_from_file.read_file(f)
1017
self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
1018
self.wonderful_spam.replace('\t\n', '\n'))
1020
class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
1021
config_class = configparser.RawConfigParser
1023
def test_interpolation(self):
1024
cf = self.get_interpolation_config()
1025
eq = self.assertEqual
1026
eq(cf.get("Foo", "bar"),
1027
"something %(with1)s interpolation (1 step)")
1028
eq(cf.get("Foo", "bar9"),
1029
"something %(with9)s lots of interpolation (9 steps)")
1030
eq(cf.get("Foo", "bar10"),
1031
"something %(with10)s lots of interpolation (10 steps)")
1032
eq(cf.get("Foo", "bar11"),
1033
"something %(with11)s lots of interpolation (11 steps)")
1035
def test_items(self):
1036
self.check_items_config([('default', '<default>'),
1037
('getdefault', '|%(default)s|'),
1038
('key', '|%(name)s|'),
1039
('name', '%(value)s'),
1040
('value', 'value')])
1042
def test_set_nonstring_types(self):
1043
cf = self.newconfig()
1044
cf.add_section('non-string')
1045
cf.set('non-string', 'int', 1)
1046
cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
1047
cf.set('non-string', 'dict', {'pi': 3.14159})
1048
self.assertEqual(cf.get('non-string', 'int'), 1)
1049
self.assertEqual(cf.get('non-string', 'list'),
1050
[0, 1, 1, 2, 3, 5, 8, 13])
1051
self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
1053
cf.set(123, 'this is sick', True)
1054
self.assertEqual(cf.get(123, 'this is sick'), True)
1055
if cf._dict is configparser._default_dict:
1056
# would not work for SortedDict; only checking for the most common
1057
# default dictionary (OrderedDict)
1058
cf.optionxform = lambda x: x
1059
cf.set('non-string', 1, 1)
1060
self.assertEqual(cf.get('non-string', 1), 1)
1062
class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
1063
delimiters = (':=', '$')
1064
comment_prefixes = ('//', '"')
1065
inline_comment_prefixes = ('//', '"')
1067
class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
1068
config_class = configparser.RawConfigParser
1069
comment_prefixes = ('#', ';', '----')
1070
inline_comment_prefixes = ('//',)
1071
empty_lines_in_values = False
1073
def test_reading(self):
1074
smbconf = support.findfile("cfgparser.2")
1075
# check when we pass a mix of readable and non-readable files:
1076
cf = self.newconfig()
1077
parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
1078
self.assertEqual(parsed_files, [smbconf])
1079
sections = ['global', 'homes', 'printers',
1080
'print$', 'pdf-generator', 'tmp', 'Agustin']
1081
self.assertEqual(cf.sections(), sections)
1082
self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
1083
self.assertEqual(cf.getint("global", "max log size"), 50)
1084
self.assertEqual(cf.get("global", "hosts allow"), "127.")
1085
self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
1087
class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
1088
config_class = configparser.ConfigParser
1089
interpolation = configparser.ExtendedInterpolation()
1090
default_section = 'common'
1093
def fromstring(self, string, defaults=None, optionxform=None):
1094
cf = self.newconfig(defaults)
1096
cf.optionxform = optionxform
1097
cf.read_string(string)
1100
def test_extended_interpolation(self):
1101
cf = self.fromstring(textwrap.dedent("""
1103
favourite Beatle = Paul
1104
favourite color = green
1107
favourite band = ${favourite color} day
1108
favourite pope = John ${favourite Beatle} II
1109
sequel = ${favourite pope}I
1112
favourite Beatle = George
1113
son of Edward VII = ${favourite Beatle} V
1114
son of George V = ${son of Edward VII}I
1117
favourite Beatle = ${ambv:favourite Beatle}
1118
favourite pope = ${tom:favourite pope}
1119
favourite color = black
1120
favourite state of mind = paranoid
1121
favourite movie = soylent ${common:favourite color}
1122
favourite song = ${favourite color} sabbath - ${favourite state of mind}
1125
eq = self.assertEqual
1126
eq(cf['common']['favourite Beatle'], 'Paul')
1127
eq(cf['common']['favourite color'], 'green')
1128
eq(cf['tom']['favourite Beatle'], 'Paul')
1129
eq(cf['tom']['favourite color'], 'green')
1130
eq(cf['tom']['favourite band'], 'green day')
1131
eq(cf['tom']['favourite pope'], 'John Paul II')
1132
eq(cf['tom']['sequel'], 'John Paul III')
1133
eq(cf['ambv']['favourite Beatle'], 'George')
1134
eq(cf['ambv']['favourite color'], 'green')
1135
eq(cf['ambv']['son of Edward VII'], 'George V')
1136
eq(cf['ambv']['son of George V'], 'George VI')
1137
eq(cf['stanley']['favourite Beatle'], 'George')
1138
eq(cf['stanley']['favourite color'], 'black')
1139
eq(cf['stanley']['favourite state of mind'], 'paranoid')
1140
eq(cf['stanley']['favourite movie'], 'soylent green')
1141
eq(cf['stanley']['favourite pope'], 'John Paul II')
1142
eq(cf['stanley']['favourite song'],
1143
'black sabbath - paranoid')
1145
def test_endless_loop(self):
1146
cf = self.fromstring(textwrap.dedent("""
1148
ping = ${one for me:pong}
1151
pong = ${one for you:ping}
1157
with self.assertRaises(configparser.InterpolationDepthError):
1158
cf['one for you']['ping']
1159
with self.assertRaises(configparser.InterpolationDepthError):
1162
def test_strange_options(self):
1163
cf = self.fromstring("""
1167
${sick} = cannot interpolate me
1170
$other = ${dollars:$var}
1171
$trying = ${dollars:${sick}}
1174
self.assertEqual(cf['dollars']['$var'], '$value')
1175
self.assertEqual(cf['interpolated']['$other'], '$value')
1176
self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
1177
exception_class = configparser.InterpolationMissingOptionError
1178
with self.assertRaises(exception_class) as cm:
1179
cf['interpolated']['$trying']
1180
self.assertEqual(cm.exception.reference, 'dollars:${sick')
1181
self.assertEqual(cm.exception.args[2], '}') #rawval
1183
def test_case_sensitivity_basic(self):
1184
ini = textwrap.dedent("""
1190
optionlower = a better ${common:optionlower}
1191
OptionUpper = A Better ${common:OptionUpper}
1194
foolower = ${common:optionlower} redefined
1195
FooUpper = ${Common:OptionUpper} Redefined
1198
cf = self.fromstring(ini)
1199
eq = self.assertEqual
1200
eq(cf['common']['optionlower'], 'value')
1201
eq(cf['common']['OptionUpper'], 'Value')
1202
eq(cf['Common']['optionlower'], 'a better value')
1203
eq(cf['Common']['OptionUpper'], 'A Better Value')
1204
eq(cf['random']['foolower'], 'value redefined')
1205
eq(cf['random']['FooUpper'], 'A Better Value Redefined')
1207
def test_case_sensitivity_conflicts(self):
1208
ini = textwrap.dedent("""
1214
option = a better ${common:option}
1215
Option = A Better ${common:Option}
1218
foo = ${common:option} redefined
1219
Foo = ${Common:Option} Redefined
1221
with self.assertRaises(configparser.DuplicateOptionError):
1222
cf = self.fromstring(ini)
1225
cf = self.fromstring(ini, optionxform=lambda opt: opt)
1226
eq = self.assertEqual
1227
eq(cf['common']['option'], 'value')
1228
eq(cf['common']['Option'], 'Value')
1229
eq(cf['Common']['option'], 'a better value')
1230
eq(cf['Common']['Option'], 'A Better Value')
1231
eq(cf['random']['foo'], 'value redefined')
1232
eq(cf['random']['Foo'], 'A Better Value Redefined')
1234
def test_other_errors(self):
1235
cf = self.fromstring("""
1236
[interpolation fail]
1237
case1 = ${where's the brace
1238
case2 = ${does_not_exist}
1239
case3 = ${wrong_section:wrong_value}
1240
case4 = ${i:like:colon:characters}
1241
case5 = $100 for Fail No 5!
1244
with self.assertRaises(configparser.InterpolationSyntaxError):
1245
cf['interpolation fail']['case1']
1246
with self.assertRaises(configparser.InterpolationMissingOptionError):
1247
cf['interpolation fail']['case2']
1248
with self.assertRaises(configparser.InterpolationMissingOptionError):
1249
cf['interpolation fail']['case3']
1250
with self.assertRaises(configparser.InterpolationSyntaxError):
1251
cf['interpolation fail']['case4']
1252
with self.assertRaises(configparser.InterpolationSyntaxError):
1253
cf['interpolation fail']['case5']
1254
with self.assertRaises(ValueError):
1255
cf['interpolation fail']['case6'] = "BLACK $ABBATH"
1258
class ConfigParserTestCaseNoValue(ConfigParserTestCase):
1259
allow_no_value = True
1261
class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
1262
config_class = configparser.ConfigParser
1264
comment_prefixes = {'#'}
1265
allow_no_value = True
1267
def test_cfgparser_dot_3(self):
1268
tricky = support.findfile("cfgparser.3")
1269
cf = self.newconfig()
1270
self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
1271
self.assertEqual(cf.sections(), ['strange',
1273
'yeah, sections can be '
1277
'tricky interpolation',
1278
'more interpolation'])
1279
self.assertEqual(cf.getint(self.default_section, 'go',
1280
vars={'interpolate': '-1'}), -1)
1281
with self.assertRaises(ValueError):
1282
# no interpolation will happen
1283
cf.getint(self.default_section, 'go', raw=True,
1284
vars={'interpolate': '-1'})
1285
self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
1286
self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
1287
longname = 'yeah, sections can be indented as well'
1288
self.assertFalse(cf.getboolean(longname, 'are they subsections'))
1289
self.assertEqual(cf.get(longname, 'lets use some Unicode'), 'ēä»®å')
1290
self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
1292
with self.assertRaises(configparser.InterpolationMissingOptionError):
1293
cf.items('no values here')
1294
self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
1295
self.assertEqual(cf.get('tricky interpolation', 'lets'),
1296
cf.get('tricky interpolation', 'go'))
1297
self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
1299
def test_unicode_failure(self):
1300
tricky = support.findfile("cfgparser.3")
1301
cf = self.newconfig()
1302
with self.assertRaises(UnicodeDecodeError):
1303
cf.read(tricky, encoding='ascii')
1306
class Issue7005TestCase(unittest.TestCase):
1307
"""Test output when None is set() as a value and allow_no_value == False.
1309
http://bugs.python.org/issue7005
1313
expected_output = "[section]\noption = None\n\n"
1315
def prepare(self, config_class):
1316
# This is the default, but that's the point.
1317
cp = config_class(allow_no_value=False)
1318
cp.add_section("section")
1319
cp.set("section", "option", None)
1322
return sio.getvalue()
1324
def test_none_as_value_stringified(self):
1325
cp = configparser.ConfigParser(allow_no_value=False)
1326
cp.add_section("section")
1327
with self.assertRaises(TypeError):
1328
cp.set("section", "option", None)
1330
def test_none_as_value_stringified_raw(self):
1331
output = self.prepare(configparser.RawConfigParser)
1332
self.assertEqual(output, self.expected_output)
1335
class SortedTestCase(RawConfigParserTestCase):
1336
dict_type = SortedDict
1338
def test_sorted(self):
1339
cf = self.fromstring("[b]\n"
1346
output = io.StringIO()
1348
self.assertEqual(output.getvalue(),
1358
class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
1359
config_class = configparser.RawConfigParser
1360
comment_prefixes = '#;'
1361
inline_comment_prefixes = ';'
1363
def test_comment_handling(self):
1364
config_string = textwrap.dedent("""\
1367
foo: bar # not a comment!
1368
# but this is a comment
1370
quirk: this;is not a comment
1371
; a space must precede an inline comment
1373
cf = self.fromstring(config_string)
1374
self.assertEqual(cf.get('Commented Bar', 'foo'),
1375
'bar # not a comment!')
1376
self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
1377
self.assertEqual(cf.get('Commented Bar', 'quirk'),
1378
'this;is not a comment')
1380
class CopyTestCase(BasicTestCase, unittest.TestCase):
1381
config_class = configparser.ConfigParser
1383
def fromstring(self, string, defaults=None):
1384
cf = self.newconfig(defaults)
1385
cf.read_string(string)
1386
cf_copy = self.newconfig()
1387
cf_copy.read_dict(cf)
1388
# we have to clean up option duplicates that appeared because of
1389
# the magic DEFAULTSECT behaviour.
1390
for section in cf_copy.values():
1391
if section.name == self.default_section:
1393
for default, value in cf[self.default_section].items():
1394
if section[default] == value:
1395
del section[default]
1401
file_path = support.findfile("cfgparser.1")
1402
with open(file_path) as f:
1403
self.lines = f.readlines()
1404
self.lines.reverse()
1408
return self.lines.pop()
1412
def readline_generator(f):
1413
"""As advised in Doc/library/configparser.rst."""
1420
class ReadFileTestCase(unittest.TestCase):
1421
def test_file(self):
1422
file_paths = [support.findfile("cfgparser.1")]
1424
file_paths.append(file_paths[0].encode('utf8'))
1425
except UnicodeEncodeError:
1426
pass # unfortunately we can't test bytes on this path
1427
for file_path in file_paths:
1428
parser = configparser.ConfigParser()
1429
with open(file_path) as f:
1431
self.assertIn("Foo Bar", parser)
1432
self.assertIn("foo", parser["Foo Bar"])
1433
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1435
def test_iterable(self):
1436
lines = textwrap.dedent("""
1438
foo=newbar""").strip().split('\n')
1439
parser = configparser.ConfigParser()
1440
parser.read_file(lines)
1441
self.assertIn("Foo Bar", parser)
1442
self.assertIn("foo", parser["Foo Bar"])
1443
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1445
def test_readline_generator(self):
1447
parser = configparser.ConfigParser()
1448
with self.assertRaises(TypeError):
1449
parser.read_file(FakeFile())
1450
parser.read_file(readline_generator(FakeFile()))
1451
self.assertIn("Foo Bar", parser)
1452
self.assertIn("foo", parser["Foo Bar"])
1453
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1455
def test_source_as_bytes(self):
1457
lines = textwrap.dedent("""
1459
[badbad]""").strip().split('\n')
1460
parser = configparser.ConfigParser()
1461
with self.assertRaises(configparser.DuplicateSectionError) as dse:
1462
parser.read_file(lines, source=b"badbad")
1465
"While reading from b'badbad' [line 2]: section 'badbad' "
1468
lines = textwrap.dedent("""
1471
bad = bad""").strip().split('\n')
1472
parser = configparser.ConfigParser()
1473
with self.assertRaises(configparser.DuplicateOptionError) as dse:
1474
parser.read_file(lines, source=b"badbad")
1477
"While reading from b'badbad' [line 3]: option 'bad' in section "
1478
"'badbad' already exists"
1480
lines = textwrap.dedent("""
1482
= bad""").strip().split('\n')
1483
parser = configparser.ConfigParser()
1484
with self.assertRaises(configparser.ParsingError) as dse:
1485
parser.read_file(lines, source=b"badbad")
1488
"Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'"
1490
lines = textwrap.dedent("""
1492
bad = bad""").strip().split('\n')
1493
parser = configparser.ConfigParser()
1494
with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
1495
parser.read_file(lines, source=b"badbad")
1498
"File contains no section headers.\nfile: b'badbad', line: 1\n"
1503
class CoverageOneHundredTestCase(unittest.TestCase):
1504
"""Covers edge cases in the codebase."""
1506
def test_duplicate_option_error(self):
1507
error = configparser.DuplicateOptionError('section', 'option')
1508
self.assertEqual(error.section, 'section')
1509
self.assertEqual(error.option, 'option')
1510
self.assertEqual(error.source, None)
1511
self.assertEqual(error.lineno, None)
1512
self.assertEqual(error.args, ('section', 'option', None, None))
1513
self.assertEqual(str(error), "Option 'option' in section 'section' "
1516
def test_interpolation_depth_error(self):
1517
error = configparser.InterpolationDepthError('option', 'section',
1519
self.assertEqual(error.args, ('option', 'section', 'rawval'))
1520
self.assertEqual(error.option, 'option')
1521
self.assertEqual(error.section, 'section')
1523
def test_parsing_error(self):
1524
with self.assertRaises(ValueError) as cm:
1525
configparser.ParsingError()
1526
self.assertEqual(str(cm.exception), "Required argument `source' not "
1528
with self.assertRaises(ValueError) as cm:
1529
configparser.ParsingError(source='source', filename='filename')
1530
self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
1531
"and `source'. Use `source'.")
1532
error = configparser.ParsingError(filename='source')
1533
self.assertEqual(error.source, 'source')
1534
with warnings.catch_warnings(record=True) as w:
1535
warnings.simplefilter("always", DeprecationWarning)
1536
self.assertEqual(error.filename, 'source')
1537
error.filename = 'filename'
1538
self.assertEqual(error.source, 'filename')
1540
self.assertTrue(warning.category is DeprecationWarning)
1542
def test_interpolation_validation(self):
1543
parser = configparser.ConfigParser()
1544
parser.read_string("""
1547
invalid_reference = %(()
1548
invalid_variable = %(does_not_exist)s
1550
with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1551
parser['section']['invalid_percent']
1552
self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
1554
with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
1555
parser['section']['invalid_reference']
1556
self.assertEqual(str(cm.exception), "bad interpolation variable "
1559
def test_readfp_deprecation(self):
1560
sio = io.StringIO("""
1564
parser = configparser.ConfigParser()
1565
with warnings.catch_warnings(record=True) as w:
1566
warnings.simplefilter("always", DeprecationWarning)
1567
parser.readfp(sio, filename='StringIO')
1569
self.assertTrue(warning.category is DeprecationWarning)
1570
self.assertEqual(len(parser), 2)
1571
self.assertEqual(parser['section']['option'], 'value')
1573
def test_safeconfigparser_deprecation(self):
1574
with warnings.catch_warnings(record=True) as w:
1575
warnings.simplefilter("always", DeprecationWarning)
1576
parser = configparser.SafeConfigParser()
1578
self.assertTrue(warning.category is DeprecationWarning)
1580
def test_sectionproxy_repr(self):
1581
parser = configparser.ConfigParser()
1582
parser.read_string("""
1586
self.assertEqual(repr(parser['section']), '<Section: section>')
1589
class ExceptionPicklingTestCase(unittest.TestCase):
1590
"""Tests for issue #13760: ConfigParser exceptions are not picklable."""
1592
def test_error(self):
1594
e1 = configparser.Error('value')
1595
pickled = pickle.dumps(e1)
1596
e2 = pickle.loads(pickled)
1597
self.assertEqual(e1.message, e2.message)
1598
self.assertEqual(repr(e1), repr(e2))
1600
def test_nosectionerror(self):
1602
e1 = configparser.NoSectionError('section')
1603
pickled = pickle.dumps(e1)
1604
e2 = pickle.loads(pickled)
1605
self.assertEqual(e1.message, e2.message)
1606
self.assertEqual(e1.args, e2.args)
1607
self.assertEqual(e1.section, e2.section)
1608
self.assertEqual(repr(e1), repr(e2))
1610
def test_nooptionerror(self):
1612
e1 = configparser.NoOptionError('option', 'section')
1613
pickled = pickle.dumps(e1)
1614
e2 = pickle.loads(pickled)
1615
self.assertEqual(e1.message, e2.message)
1616
self.assertEqual(e1.args, e2.args)
1617
self.assertEqual(e1.section, e2.section)
1618
self.assertEqual(e1.option, e2.option)
1619
self.assertEqual(repr(e1), repr(e2))
1621
def test_duplicatesectionerror(self):
1623
e1 = configparser.DuplicateSectionError('section', 'source', 123)
1624
pickled = pickle.dumps(e1)
1625
e2 = pickle.loads(pickled)
1626
self.assertEqual(e1.message, e2.message)
1627
self.assertEqual(e1.args, e2.args)
1628
self.assertEqual(e1.section, e2.section)
1629
self.assertEqual(e1.source, e2.source)
1630
self.assertEqual(e1.lineno, e2.lineno)
1631
self.assertEqual(repr(e1), repr(e2))
1633
def test_duplicateoptionerror(self):
1635
e1 = configparser.DuplicateOptionError('section', 'option', 'source',
1637
pickled = pickle.dumps(e1)
1638
e2 = pickle.loads(pickled)
1639
self.assertEqual(e1.message, e2.message)
1640
self.assertEqual(e1.args, e2.args)
1641
self.assertEqual(e1.section, e2.section)
1642
self.assertEqual(e1.option, e2.option)
1643
self.assertEqual(e1.source, e2.source)
1644
self.assertEqual(e1.lineno, e2.lineno)
1645
self.assertEqual(repr(e1), repr(e2))
1647
def test_interpolationerror(self):
1649
e1 = configparser.InterpolationError('option', 'section', 'msg')
1650
pickled = pickle.dumps(e1)
1651
e2 = pickle.loads(pickled)
1652
self.assertEqual(e1.message, e2.message)
1653
self.assertEqual(e1.args, e2.args)
1654
self.assertEqual(e1.section, e2.section)
1655
self.assertEqual(e1.option, e2.option)
1656
self.assertEqual(repr(e1), repr(e2))
1658
def test_interpolationmissingoptionerror(self):
1660
e1 = configparser.InterpolationMissingOptionError('option', 'section',
1661
'rawval', 'reference')
1662
pickled = pickle.dumps(e1)
1663
e2 = pickle.loads(pickled)
1664
self.assertEqual(e1.message, e2.message)
1665
self.assertEqual(e1.args, e2.args)
1666
self.assertEqual(e1.section, e2.section)
1667
self.assertEqual(e1.option, e2.option)
1668
self.assertEqual(e1.reference, e2.reference)
1669
self.assertEqual(repr(e1), repr(e2))
1671
def test_interpolationsyntaxerror(self):
1673
e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
1674
pickled = pickle.dumps(e1)
1675
e2 = pickle.loads(pickled)
1676
self.assertEqual(e1.message, e2.message)
1677
self.assertEqual(e1.args, e2.args)
1678
self.assertEqual(e1.section, e2.section)
1679
self.assertEqual(e1.option, e2.option)
1680
self.assertEqual(repr(e1), repr(e2))
1682
def test_interpolationdeptherror(self):
1684
e1 = configparser.InterpolationDepthError('option', 'section',
1686
pickled = pickle.dumps(e1)
1687
e2 = pickle.loads(pickled)
1688
self.assertEqual(e1.message, e2.message)
1689
self.assertEqual(e1.args, e2.args)
1690
self.assertEqual(e1.section, e2.section)
1691
self.assertEqual(e1.option, e2.option)
1692
self.assertEqual(repr(e1), repr(e2))
1694
def test_parsingerror(self):
1696
e1 = configparser.ParsingError('source')
1697
e1.append(1, 'line1')
1698
e1.append(2, 'line2')
1699
e1.append(3, 'line3')
1700
pickled = pickle.dumps(e1)
1701
e2 = pickle.loads(pickled)
1702
self.assertEqual(e1.message, e2.message)
1703
self.assertEqual(e1.args, e2.args)
1704
self.assertEqual(e1.source, e2.source)
1705
self.assertEqual(e1.errors, e2.errors)
1706
self.assertEqual(repr(e1), repr(e2))
1707
e1 = configparser.ParsingError(filename='filename')
1708
e1.append(1, 'line1')
1709
e1.append(2, 'line2')
1710
e1.append(3, 'line3')
1711
pickled = pickle.dumps(e1)
1712
e2 = pickle.loads(pickled)
1713
self.assertEqual(e1.message, e2.message)
1714
self.assertEqual(e1.args, e2.args)
1715
self.assertEqual(e1.source, e2.source)
1716
self.assertEqual(e1.errors, e2.errors)
1717
self.assertEqual(repr(e1), repr(e2))
1719
def test_missingsectionheadererror(self):
1721
e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
1722
pickled = pickle.dumps(e1)
1723
e2 = pickle.loads(pickled)
1724
self.assertEqual(e1.message, e2.message)
1725
self.assertEqual(e1.args, e2.args)
1726
self.assertEqual(e1.line, e2.line)
1727
self.assertEqual(e1.source, e2.source)
1728
self.assertEqual(e1.lineno, e2.lineno)
1729
self.assertEqual(repr(e1), repr(e2))
1732
class InlineCommentStrippingTestCase(unittest.TestCase):
1733
"""Tests for issue #14590: ConfigParser doesn't strip inline comment when
1734
delimiter occurs earlier without preceding space.."""
1736
def test_stripping(self):
1737
cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
1743
k3 = v3 ; also a comment
1744
k4 = v4;still v4 ;a comment
1745
k5 = v5;still v5 ; also a comment
1746
k6 = v6;still v6; and still v6 ;a comment
1747
k7 = v7;still v7; and still v7 ; also a comment
1750
k1 = v1;still v1 #a comment ; yeah, pretty much
1751
k2 = v2 // this already is a comment ; continued
1752
k3 = v3;#//still v3# and still v3 ; a comment
1755
self.assertEqual(s['k1'], 'v1;still v1')
1756
self.assertEqual(s['k2'], 'v2')
1757
self.assertEqual(s['k3'], 'v3')
1758
self.assertEqual(s['k4'], 'v4;still v4')
1759
self.assertEqual(s['k5'], 'v5;still v5')
1760
self.assertEqual(s['k6'], 'v6;still v6; and still v6')
1761
self.assertEqual(s['k7'], 'v7;still v7; and still v7')
1762
s = cfg['multiprefix']
1763
self.assertEqual(s['k1'], 'v1;still v1')
1764
self.assertEqual(s['k2'], 'v2')
1765
self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
1768
if __name__ == '__main__':