~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/test/test_configparser.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import collections
 
2
import configparser
 
3
import io
 
4
import os
 
5
import sys
 
6
import textwrap
 
7
import unittest
 
8
import warnings
 
9
 
 
10
from test import support
 
11
 
 
12
class SortedDict(collections.UserDict):
 
13
 
 
14
    def items(self):
 
15
        return sorted(self.data.items())
 
16
 
 
17
    def keys(self):
 
18
        return sorted(self.data.keys())
 
19
 
 
20
    def values(self):
 
21
        return [i[1] for i in self.items()]
 
22
 
 
23
    def iteritems(self):
 
24
        return iter(self.items())
 
25
 
 
26
    def iterkeys(self):
 
27
        return iter(self.keys())
 
28
 
 
29
    def itervalues(self):
 
30
        return iter(self.values())
 
31
 
 
32
    __iter__ = iterkeys
 
33
 
 
34
 
 
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
 
42
    strict = False
 
43
    default_section = configparser.DEFAULTSECT
 
44
    interpolation = configparser._UNSET
 
45
 
 
46
    def newconfig(self, defaults=None):
 
47
        arguments = dict(
 
48
            defaults=defaults,
 
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,
 
55
            strict=self.strict,
 
56
            default_section=self.default_section,
 
57
            interpolation=self.interpolation,
 
58
        )
 
59
        instance = self.config_class(**arguments)
 
60
        return instance
 
61
 
 
62
    def fromstring(self, string, defaults=None):
 
63
        cf = self.newconfig(defaults)
 
64
        cf.read_string(string)
 
65
        return cf
 
66
 
 
67
class BasicTestCase(CfgParserTestCaseClass):
 
68
 
 
69
    def basic_test(self, cf):
 
70
        E = ['Commented Bar',
 
71
             'Foo Bar',
 
72
             'Internationalized Stuff',
 
73
             'Long Line',
 
74
             'Section\\with$weird%characters[\t',
 
75
             'Spaces',
 
76
             'Spacey Bar',
 
77
             'Spacey Bar From The Beginning',
 
78
             'Types',
 
79
             ]
 
80
 
 
81
        if self.allow_no_value:
 
82
            E.append('NoValue')
 
83
        E.sort()
 
84
        F = [('baz', 'qwe'), ('foo', 'bar3')]
 
85
 
 
86
        # API access
 
87
        L = cf.sections()
 
88
        L.sort()
 
89
        eq = self.assertEqual
 
90
        eq(L, E)
 
91
        L = cf.items('Spacey Bar From The Beginning')
 
92
        L.sort()
 
93
        eq(L, F)
 
94
 
 
95
        # mapping access
 
96
        L = [section for section in cf]
 
97
        L.sort()
 
98
        E.append(self.default_section)
 
99
        E.sort()
 
100
        eq(L, E)
 
101
        L = cf['Spacey Bar From The Beginning'].items()
 
102
        L = sorted(list(L))
 
103
        eq(L, F)
 
104
        L = cf.items()
 
105
        L = sorted(list(L))
 
106
        self.assertEqual(len(L), len(E))
 
107
        for name, section in L:
 
108
            eq(name, section.name)
 
109
        eq(cf.defaults(), cf[self.default_section])
 
110
 
 
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
 
114
 
 
115
        # API access
 
116
        eq(cf.get('Foo Bar', 'foo'), 'bar1')
 
117
        eq(cf.get('Spacey Bar', 'foo'), 'bar2')
 
118
        eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
 
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)
 
132
 
 
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',
 
150
                                           fallback=0.0), 0.44)
 
151
        self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
 
152
                                           fallback=0.0), 0.0)
 
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"),
 
158
           "yes") # sic!
 
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)
 
167
 
 
168
        # mapping access
 
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',
 
214
                                                    fallback=0.0), 0.44)
 
215
        self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
 
216
        self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
 
217
                                                    fallback=0.0), 0.0)
 
218
        eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
 
219
        eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
 
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"),
 
225
           "yes") # sic!
 
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)
 
235
 
 
236
        # Make sure the right things happen for remove_section() and
 
237
        # remove_option(); added to include check for SourceForge bug #123324.
 
238
 
 
239
        cf[self.default_section]['this_value'] = '1'
 
240
        cf[self.default_section]['that_value'] = '2'
 
241
 
 
242
        # API access
 
243
        self.assertTrue(cf.remove_section('Spaces'))
 
244
        self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
 
245
        self.assertFalse(cf.remove_section('Spaces'))
 
246
        self.assertFalse(cf.remove_section(self.default_section))
 
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"
 
253
                    " that was removed")
 
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'))
 
259
 
 
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',))
 
263
 
 
264
        eq(cf.get('Long Line', 'foo'),
 
265
           'this line is much, much longer than my editor\nlikes it.')
 
266
 
 
267
        # mapping access
 
268
        del cf['Types']
 
269
        self.assertFalse('Types' in cf)
 
270
        with self.assertRaises(KeyError):
 
271
            del cf['Types']
 
272
        with self.assertRaises(ValueError):
 
273
            del cf[self.default_section]
 
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']
 
287
 
 
288
        # Don't add new asserts below in this method as most of the options
 
289
        # and sections are now removed.
 
290
 
 
291
    def test_basic(self):
 
292
        config_string = """\
 
293
[Foo Bar]
 
294
foo{0[0]}bar1
 
295
[Spacey Bar]
 
296
foo {0[0]} bar2
 
297
[Spacey Bar From The Beginning]
 
298
  foo {0[0]} bar3
 
299
  baz {0[0]} qwe
 
300
[Commented Bar]
 
301
foo{0[1]} bar4 {1[1]} comment
 
302
baz{0[0]}qwe {1[0]}another one
 
303
[Long Line]
 
304
foo{0[1]} this line is much, much longer than my editor
 
305
   likes it.
 
306
[Section\\with$weird%characters[\t]
 
307
[Internationalized Stuff]
 
308
foo[bg]{0[1]} Bulgarian
 
309
foo{0[0]}Default
 
310
foo[en]{0[0]}English
 
311
foo[de]{0[0]}Deutsch
 
312
[Spaces]
 
313
key with spaces {0[1]} value
 
314
another with spaces {0[0]} splat!
 
315
[Types]
 
316
int {0[1]} 42
 
317
float {0[0]} 0.44
 
318
boolean {0[0]} NO
 
319
123 {0[1]} strange but acceptable
 
320
""".format(self.delimiters, self.comment_prefixes)
 
321
        if self.allow_no_value:
 
322
            config_string += (
 
323
                "[NoValue]\n"
 
324
                "option-without-value\n"
 
325
                )
 
326
        cf = self.fromstring(config_string)
 
327
        self.basic_test(cf)
 
328
        if self.strict:
 
329
            with self.assertRaises(configparser.DuplicateOptionError):
 
330
                cf.read_string(textwrap.dedent("""\
 
331
                    [Duplicate Options Here]
 
332
                    option {0[0]} with a value
 
333
                    option {0[1]} with another value
 
334
                """.format(self.delimiters)))
 
335
            with self.assertRaises(configparser.DuplicateSectionError):
 
336
                cf.read_string(textwrap.dedent("""\
 
337
                    [And Now For Something]
 
338
                    completely different {0[0]} True
 
339
                    [And Now For Something]
 
340
                    the larch {0[1]} 1
 
341
                """.format(self.delimiters)))
 
342
        else:
 
343
            cf.read_string(textwrap.dedent("""\
 
344
                [Duplicate Options Here]
 
345
                option {0[0]} with a value
 
346
                option {0[1]} with another value
 
347
            """.format(self.delimiters)))
 
348
 
 
349
            cf.read_string(textwrap.dedent("""\
 
350
                [And Now For Something]
 
351
                completely different {0[0]} True
 
352
                [And Now For Something]
 
353
                the larch {0[1]} 1
 
354
            """.format(self.delimiters)))
 
355
 
 
356
    def test_basic_from_dict(self):
 
357
        config = {
 
358
            "Foo Bar": {
 
359
                "foo": "bar1",
 
360
            },
 
361
            "Spacey Bar": {
 
362
                "foo": "bar2",
 
363
            },
 
364
            "Spacey Bar From The Beginning": {
 
365
                "foo": "bar3",
 
366
                "baz": "qwe",
 
367
            },
 
368
            "Commented Bar": {
 
369
                "foo": "bar4",
 
370
                "baz": "qwe",
 
371
            },
 
372
            "Long Line": {
 
373
                "foo": "this line is much, much longer than my editor\nlikes "
 
374
                       "it.",
 
375
            },
 
376
            "Section\\with$weird%characters[\t": {
 
377
            },
 
378
            "Internationalized Stuff": {
 
379
                "foo[bg]": "Bulgarian",
 
380
                "foo": "Default",
 
381
                "foo[en]": "English",
 
382
                "foo[de]": "Deutsch",
 
383
            },
 
384
            "Spaces": {
 
385
                "key with spaces": "value",
 
386
                "another with spaces": "splat!",
 
387
            },
 
388
            "Types": {
 
389
                "int": 42,
 
390
                "float": 0.44,
 
391
                "boolean": False,
 
392
                123: "strange but acceptable",
 
393
            },
 
394
        }
 
395
        if self.allow_no_value:
 
396
            config.update({
 
397
                "NoValue": {
 
398
                    "option-without-value": None,
 
399
                }
 
400
            })
 
401
        cf = self.newconfig()
 
402
        cf.read_dict(config)
 
403
        self.basic_test(cf)
 
404
        if self.strict:
 
405
            with self.assertRaises(configparser.DuplicateSectionError):
 
406
                cf.read_dict({
 
407
                    '1': {'key': 'value'},
 
408
                    1: {'key2': 'value2'},
 
409
                })
 
410
            with self.assertRaises(configparser.DuplicateOptionError):
 
411
                cf.read_dict({
 
412
                    "Duplicate Options Here": {
 
413
                        'option': 'with a value',
 
414
                        'OPTION': 'with another value',
 
415
                    },
 
416
                })
 
417
        else:
 
418
            cf.read_dict({
 
419
                'section': {'key': 'value'},
 
420
                'SECTION': {'key2': 'value2'},
 
421
            })
 
422
            cf.read_dict({
 
423
                "Duplicate Options Here": {
 
424
                    'option': 'with a value',
 
425
                    'OPTION': 'with another value',
 
426
                },
 
427
            })
 
428
 
 
429
    def test_case_sensitivity(self):
 
430
        cf = self.newconfig()
 
431
        cf.add_section("A")
 
432
        cf.add_section("a")
 
433
        cf.add_section("B")
 
434
        L = cf.sections()
 
435
        L.sort()
 
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"):
 
449
            self.assertTrue(
 
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"), [])
 
456
 
 
457
        # SF bug #432369:
 
458
        cf = self.fromstring(
 
459
            "[MySection]\nOption{} first line   \n\tsecond line   \n".format(
 
460
                self.delimiters[0]))
 
461
        eq(cf.options("MySection"), ["option"])
 
462
        eq(cf.get("MySection", "Option"), "first line\nsecond line")
 
463
 
 
464
        # SF bug #561822:
 
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"))
 
469
 
 
470
 
 
471
    def test_case_sensitivity_mapping_access(self):
 
472
        cf = self.newconfig()
 
473
        cf["A"] = {}
 
474
        cf["a"] = {"B": "value"}
 
475
        cf["B"] = {}
 
476
        L = [section for section in cf]
 
477
        L.sort()
 
478
        eq = self.assertEqual
 
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"):
 
490
            self.assertTrue(
 
491
                opt in cf["A"],
 
492
                "has_option() returned false for option which should exist")
 
493
        eq(cf["A"].keys(), {"a-b"})
 
494
        eq(cf["a"].keys(), {"b"})
 
495
        del cf["a"]["B"]
 
496
        elem_eq(cf["a"].keys(), {})
 
497
 
 
498
        # SF bug #432369:
 
499
        cf = self.fromstring(
 
500
            "[MySection]\nOption{} first line   \n\tsecond line   \n".format(
 
501
                self.delimiters[0]))
 
502
        eq(cf["MySection"].keys(), {"option"})
 
503
        eq(cf["MySection"]["Option"], "first line\nsecond line")
 
504
 
 
505
        # SF bug #561822:
 
506
        cf = self.fromstring("[section]\n"
 
507
                             "nekey{}nevalue\n".format(self.delimiters[0]),
 
508
                             defaults={"key":"value"})
 
509
        self.assertTrue("Key" in cf["section"])
 
510
 
 
511
    def test_default_case_sensitivity(self):
 
512
        cf = self.newconfig({"foo": "Bar"})
 
513
        self.assertEqual(
 
514
            cf.get(self.default_section, "Foo"), "Bar",
 
515
            "could not locate option, expecting case-insensitive option names")
 
516
        cf = self.newconfig({"Foo": "Bar"})
 
517
        self.assertEqual(
 
518
            cf.get(self.default_section, "Foo"), "Bar",
 
519
            "could not locate option, expecting case-insensitive defaults")
 
520
 
 
521
    def test_parse_errors(self):
 
522
        cf = self.newconfig()
 
523
        self.parse_error(cf, configparser.ParsingError,
 
524
                         "[Foo]\n"
 
525
                         "{}val-without-opt-name\n".format(self.delimiters[0]))
 
526
        self.parse_error(cf, configparser.ParsingError,
 
527
                         "[Foo]\n"
 
528
                         "{}val-without-opt-name\n".format(self.delimiters[1]))
 
529
        e = self.parse_error(cf, configparser.MissingSectionHeaderError,
 
530
                             "No Section!\n")
 
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
 
540
                expected = (tricky,)
 
541
            else:
 
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)
 
548
 
 
549
    def parse_error(self, cf, exc, src):
 
550
        if hasattr(src, 'readline'):
 
551
            sio = src
 
552
        else:
 
553
            sio = io.StringIO(src)
 
554
        with self.assertRaises(exc) as cm:
 
555
            cf.read_file(sio)
 
556
        return cm.exception
 
557
 
 
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 "
 
564
                         "sections")
 
565
        with self.assertRaises(configparser.NoSectionError):
 
566
            cf.options("Foo")
 
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"))
 
574
 
 
575
    def get_error(self, cf, exc, section, option):
 
576
        try:
 
577
            cf.get(section, option)
 
578
        except exc as e:
 
579
            return e
 
580
        else:
 
581
            self.fail("expected exception type %s.%s"
 
582
                      % (exc.__module__, exc.__name__))
 
583
 
 
584
    def test_boolean(self):
 
585
        cf = self.fromstring(
 
586
            "[BOOLTEST]\n"
 
587
            "T1{equals}1\n"
 
588
            "T2{equals}TRUE\n"
 
589
            "T3{equals}True\n"
 
590
            "T4{equals}oN\n"
 
591
            "T5{equals}yes\n"
 
592
            "F1{equals}0\n"
 
593
            "F2{equals}FALSE\n"
 
594
            "F3{equals}False\n"
 
595
            "F4{equals}oFF\n"
 
596
            "F5{equals}nO\n"
 
597
            "E1{equals}2\n"
 
598
            "E2{equals}foo\n"
 
599
            "E3{equals}-1\n"
 
600
            "E4{equals}0.1\n"
 
601
            "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
 
602
            )
 
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)
 
608
 
 
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")
 
614
        e = cm.exception
 
615
        self.assertEqual(str(e), "Section 'Foo' already exists")
 
616
        self.assertEqual(e.args, ("Foo", None, None))
 
617
 
 
618
        if self.strict:
 
619
            with self.assertRaises(configparser.DuplicateSectionError) as cm:
 
620
                cf.read_string(textwrap.dedent("""\
 
621
                    [Foo]
 
622
                    will this be added{equals}True
 
623
                    [Bar]
 
624
                    what about this{equals}True
 
625
                    [Foo]
 
626
                    oops{equals}this won't
 
627
                """.format(equals=self.delimiters[0])), source='<foo-bar>')
 
628
            e = cm.exception
 
629
            self.assertEqual(str(e), "While reading from '<foo-bar>' "
 
630
                                     "[line  5]: section 'Foo' already exists")
 
631
            self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
 
632
 
 
633
            with self.assertRaises(configparser.DuplicateOptionError) as cm:
 
634
                cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
 
635
            e = cm.exception
 
636
            self.assertEqual(str(e), "While reading from '<dict>': option "
 
637
                                     "'opt' in section 'Bar' already exists")
 
638
            self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
 
639
 
 
640
    def test_write(self):
 
641
        config_string = (
 
642
            "[Long Line]\n"
 
643
            "foo{0[0]} this line is much, much longer than my editor\n"
 
644
            "   likes it.\n"
 
645
            "[{default_section}]\n"
 
646
            "foo{0[1]} another very\n"
 
647
            " long line\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)
 
655
            )
 
656
        if self.allow_no_value:
 
657
            config_string += (
 
658
            "[Valueless]\n"
 
659
            "option-without-value\n"
 
660
            )
 
661
 
 
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)
 
669
            expect_string = (
 
670
                "[{default_section}]\n"
 
671
                "foo{equals}another very\n"
 
672
                "\tlong line\n"
 
673
                "\n"
 
674
                "[Long Line]\n"
 
675
                "foo{equals}this line is much, much longer than my editor\n"
 
676
                "\tlikes it.\n"
 
677
                "\n"
 
678
                "[Long Line - With Comments!]\n"
 
679
                "test{equals}we\n"
 
680
                "\talso\n"
 
681
                "\tcomments\n"
 
682
                "\tmultiline\n"
 
683
                "\n".format(equals=delimiter,
 
684
                            default_section=self.default_section)
 
685
                )
 
686
            if self.allow_no_value:
 
687
                expect_string += (
 
688
                    "[Valueless]\n"
 
689
                    "option-without-value\n"
 
690
                    "\n"
 
691
                    )
 
692
            self.assertEqual(output.getvalue(), expect_string)
 
693
 
 
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:
 
699
        class mystr(str):
 
700
            pass
 
701
        cf.set("sect", "option1", "splat")
 
702
        cf.set("sect", "option1", mystr("splat"))
 
703
        cf.set("sect", "option2", "splat")
 
704
        cf.set("sect", "option2", mystr("splat"))
 
705
        cf.set("sect", "option1", "splat")
 
706
        cf.set("sect", "option2", "splat")
 
707
 
 
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
 
711
            return
 
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, [])
 
731
 
 
732
    # shared by subclasses
 
733
    def get_interpolation_config(self):
 
734
        return self.fromstring(
 
735
            "[Foo]\n"
 
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"
 
751
            "\n"
 
752
            "[Mutual Recursion]\n"
 
753
            "foo{equals}%(bar)s\n"
 
754
            "bar{equals}%(foo)s\n"
 
755
            "\n"
 
756
            "[Interpolation Error]\n"
 
757
            # no definition for 'reference'
 
758
            "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
 
759
 
 
760
    def check_items_config(self, expected):
 
761
        cf = self.fromstring("""
 
762
            [section]
 
763
            name {0[0]} %(value)s
 
764
            key{0[1]} |%(name)s|
 
765
            getdefault{0[1]} |%(default)s|
 
766
        """.format(self.delimiters), defaults={"default": "<default>"})
 
767
        L = list(cf.items("section", vars={'value': 'value'}))
 
768
        L.sort()
 
769
        self.assertEqual(L, expected)
 
770
        with self.assertRaises(configparser.NoSectionError):
 
771
            cf.items("no such section")
 
772
 
 
773
    def test_popitem(self):
 
774
        cf = self.fromstring("""
 
775
            [section1]
 
776
            name1 {0[0]} value1
 
777
            [section2]
 
778
            name2 {0[0]} value2
 
779
            [section3]
 
780
            name3 {0[0]} value3
 
781
        """.format(self.delimiters), defaults={"default": "<default>"})
 
782
        self.assertEqual(cf.popitem()[0], 'section1')
 
783
        self.assertEqual(cf.popitem()[0], 'section2')
 
784
        self.assertEqual(cf.popitem()[0], 'section3')
 
785
        with self.assertRaises(KeyError):
 
786
            cf.popitem()
 
787
 
 
788
    def test_clear(self):
 
789
        cf = self.newconfig({"foo": "Bar"})
 
790
        self.assertEqual(
 
791
            cf.get(self.default_section, "Foo"), "Bar",
 
792
            "could not locate option, expecting case-insensitive option names")
 
793
        cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
 
794
        self.assertEqual(cf.sections(), ['zing'])
 
795
        self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
 
796
        cf.clear()
 
797
        self.assertEqual(set(cf.sections()), set())
 
798
        self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
 
799
 
 
800
    def test_setitem(self):
 
801
        cf = self.fromstring("""
 
802
            [section1]
 
803
            name1 {0[0]} value1
 
804
            [section2]
 
805
            name2 {0[0]} value2
 
806
            [section3]
 
807
            name3 {0[0]} value3
 
808
        """.format(self.delimiters), defaults={"nameD": "valueD"})
 
809
        self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
 
810
        self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
 
811
        self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
 
812
        self.assertEqual(cf['section1']['name1'], 'value1')
 
813
        self.assertEqual(cf['section2']['name2'], 'value2')
 
814
        self.assertEqual(cf['section3']['name3'], 'value3')
 
815
        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'])
 
821
        cf['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'])
 
831
 
 
832
 
 
833
class StrictTestCase(BasicTestCase, unittest.TestCase):
 
834
    config_class = configparser.RawConfigParser
 
835
    strict = True
 
836
 
 
837
 
 
838
class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
 
839
    config_class = configparser.ConfigParser
 
840
 
 
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)"))
 
855
 
 
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',
 
865
                                    '', 'reference'))
 
866
        elif isinstance(self.interpolation, configparser.LegacyInterpolation):
 
867
            self.assertEqual(e.args, ('name', 'Interpolation Error',
 
868
                                    '%(reference)s', 'reference'))
 
869
 
 
870
    def test_items(self):
 
871
        self.check_items_config([('default', '<default>'),
 
872
                                 ('getdefault', '|<default>|'),
 
873
                                 ('key', '|value|'),
 
874
                                 ('name', 'value'),
 
875
                                 ('value', 'value')])
 
876
 
 
877
    def test_safe_interpolation(self):
 
878
        # See http://www.python.org/sf/511737
 
879
        cf = self.fromstring("[section]\n"
 
880
                             "option1{eq}xxx\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")
 
891
 
 
892
    def test_set_malformatted_interpolation(self):
 
893
        cf = self.fromstring("[sect]\n"
 
894
                             "option1{eq}foo\n".format(eq=self.delimiters[0]))
 
895
 
 
896
        self.assertEqual(cf.get('sect', "option1"), "foo")
 
897
 
 
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")
 
901
 
 
902
        self.assertEqual(cf.get('sect', "option1"), "foo")
 
903
 
 
904
        # bug #5741: double percents are *not* malformed
 
905
        cf.set("sect", "option2", "foo%%bar")
 
906
        self.assertEqual(cf.get("sect", "option2"), "foo%bar")
 
907
 
 
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)
 
921
 
 
922
    def test_add_section_default(self):
 
923
        cf = self.newconfig()
 
924
        self.assertRaises(ValueError, cf.add_section, self.default_section)
 
925
 
 
926
 
 
927
class ConfigParserTestCaseNoInterpolation(BasicTestCase, unittest.TestCase):
 
928
    config_class = configparser.ConfigParser
 
929
    interpolation = None
 
930
    ini = textwrap.dedent("""
 
931
        [numbers]
 
932
        one = 1
 
933
        two = %(one)s * 2
 
934
        three = ${common:one} * 3
 
935
 
 
936
        [hexen]
 
937
        sixteen = ${numbers:two} * 8
 
938
    """).strip()
 
939
 
 
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')
 
945
 
 
946
    def test_no_interpolation(self):
 
947
        cf = self.fromstring(self.ini)
 
948
        self.assertMatchesIni(cf)
 
949
 
 
950
    def test_empty_case(self):
 
951
        cf = self.newconfig()
 
952
        self.assertIsNone(cf.read_string(""))
 
953
 
 
954
    def test_none_as_default_interpolation(self):
 
955
        class CustomConfigParser(configparser.ConfigParser):
 
956
            _DEFAULT_INTERPOLATION = None
 
957
 
 
958
        cf = CustomConfigParser()
 
959
        cf.read_string(self.ini)
 
960
        self.assertMatchesIni(cf)
 
961
 
 
962
 
 
963
class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
 
964
    config_class = configparser.ConfigParser
 
965
    interpolation = configparser.LegacyInterpolation()
 
966
 
 
967
    def test_set_malformatted_interpolation(self):
 
968
        cf = self.fromstring("[sect]\n"
 
969
                             "option1{eq}foo\n".format(eq=self.delimiters[0]))
 
970
 
 
971
        self.assertEqual(cf.get('sect', "option1"), "foo")
 
972
 
 
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")
 
979
 
 
980
        # bug #5741: double percents are *not* malformed
 
981
        cf.set("sect", "option2", "foo%%bar")
 
982
        self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
 
983
 
 
984
class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
 
985
    delimiters = (':=', '$')
 
986
    comment_prefixes = ('//', '"')
 
987
    inline_comment_prefixes = ('//', '"')
 
988
 
 
989
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
 
990
    default_section = 'general'
 
991
 
 
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')
 
997
 
 
998
    def setUp(self):
 
999
        cf = self.newconfig()
 
1000
        for i in range(100):
 
1001
            s = 'section{}'.format(i)
 
1002
            cf.add_section(s)
 
1003
            for j in range(10):
 
1004
                cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
 
1005
        with open(support.TESTFN, 'w') as f:
 
1006
            cf.write(f)
 
1007
 
 
1008
    def tearDown(self):
 
1009
        os.unlink(support.TESTFN)
 
1010
 
 
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'))
 
1019
 
 
1020
class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
 
1021
    config_class = configparser.RawConfigParser
 
1022
 
 
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)")
 
1034
 
 
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')])
 
1041
 
 
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})
 
1052
        cf.add_section(123)
 
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)
 
1061
 
 
1062
class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
 
1063
    delimiters = (':=', '$')
 
1064
    comment_prefixes = ('//', '"')
 
1065
    inline_comment_prefixes = ('//', '"')
 
1066
 
 
1067
class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase):
 
1068
    config_class = configparser.RawConfigParser
 
1069
    comment_prefixes = ('#', ';', '----')
 
1070
    inline_comment_prefixes = ('//',)
 
1071
    empty_lines_in_values = False
 
1072
 
 
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")
 
1086
 
 
1087
class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase):
 
1088
    config_class = configparser.ConfigParser
 
1089
    interpolation = configparser.ExtendedInterpolation()
 
1090
    default_section = 'common'
 
1091
    strict = True
 
1092
 
 
1093
    def fromstring(self, string, defaults=None, optionxform=None):
 
1094
        cf = self.newconfig(defaults)
 
1095
        if optionxform:
 
1096
            cf.optionxform = optionxform
 
1097
        cf.read_string(string)
 
1098
        return cf
 
1099
 
 
1100
    def test_extended_interpolation(self):
 
1101
        cf = self.fromstring(textwrap.dedent("""
 
1102
            [common]
 
1103
            favourite Beatle = Paul
 
1104
            favourite color = green
 
1105
 
 
1106
            [tom]
 
1107
            favourite band = ${favourite color} day
 
1108
            favourite pope = John ${favourite Beatle} II
 
1109
            sequel = ${favourite pope}I
 
1110
 
 
1111
            [ambv]
 
1112
            favourite Beatle = George
 
1113
            son of Edward VII = ${favourite Beatle} V
 
1114
            son of George V = ${son of Edward VII}I
 
1115
 
 
1116
            [stanley]
 
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}
 
1123
        """).strip())
 
1124
 
 
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')
 
1144
 
 
1145
    def test_endless_loop(self):
 
1146
        cf = self.fromstring(textwrap.dedent("""
 
1147
            [one for you]
 
1148
            ping = ${one for me:pong}
 
1149
 
 
1150
            [one for me]
 
1151
            pong = ${one for you:ping}
 
1152
 
 
1153
            [selfish]
 
1154
            me = ${me}
 
1155
        """).strip())
 
1156
 
 
1157
        with self.assertRaises(configparser.InterpolationDepthError):
 
1158
            cf['one for you']['ping']
 
1159
        with self.assertRaises(configparser.InterpolationDepthError):
 
1160
            cf['selfish']['me']
 
1161
 
 
1162
    def test_strange_options(self):
 
1163
        cf = self.fromstring("""
 
1164
            [dollars]
 
1165
            $var = $$value
 
1166
            $var2 = ${$var}
 
1167
            ${sick} = cannot interpolate me
 
1168
 
 
1169
            [interpolated]
 
1170
            $other = ${dollars:$var}
 
1171
            $trying = ${dollars:${sick}}
 
1172
        """)
 
1173
 
 
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
 
1182
 
 
1183
    def test_case_sensitivity_basic(self):
 
1184
        ini = textwrap.dedent("""
 
1185
            [common]
 
1186
            optionlower = value
 
1187
            OptionUpper = Value
 
1188
 
 
1189
            [Common]
 
1190
            optionlower = a better ${common:optionlower}
 
1191
            OptionUpper = A Better ${common:OptionUpper}
 
1192
 
 
1193
            [random]
 
1194
            foolower = ${common:optionlower} redefined
 
1195
            FooUpper = ${Common:OptionUpper} Redefined
 
1196
        """).strip()
 
1197
 
 
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')
 
1206
 
 
1207
    def test_case_sensitivity_conflicts(self):
 
1208
        ini = textwrap.dedent("""
 
1209
            [common]
 
1210
            option = value
 
1211
            Option = Value
 
1212
 
 
1213
            [Common]
 
1214
            option = a better ${common:option}
 
1215
            Option = A Better ${common:Option}
 
1216
 
 
1217
            [random]
 
1218
            foo = ${common:option} redefined
 
1219
            Foo = ${Common:Option} Redefined
 
1220
        """).strip()
 
1221
        with self.assertRaises(configparser.DuplicateOptionError):
 
1222
            cf = self.fromstring(ini)
 
1223
 
 
1224
        # raw options
 
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')
 
1233
 
 
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!
 
1242
        """)
 
1243
 
 
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"
 
1256
 
 
1257
 
 
1258
class ConfigParserTestCaseNoValue(ConfigParserTestCase):
 
1259
    allow_no_value = True
 
1260
 
 
1261
class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase):
 
1262
    config_class = configparser.ConfigParser
 
1263
    delimiters = {'='}
 
1264
    comment_prefixes = {'#'}
 
1265
    allow_no_value = True
 
1266
 
 
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',
 
1272
                                         'corruption',
 
1273
                                         'yeah, sections can be '
 
1274
                                         'indented as well',
 
1275
                                         'another one!',
 
1276
                                         'no values here',
 
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
 
1291
                                                           # `go` from DEFAULT
 
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')
 
1298
 
 
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')
 
1304
 
 
1305
 
 
1306
class Issue7005TestCase(unittest.TestCase):
 
1307
    """Test output when None is set() as a value and allow_no_value == False.
 
1308
 
 
1309
    http://bugs.python.org/issue7005
 
1310
 
 
1311
    """
 
1312
 
 
1313
    expected_output = "[section]\noption = None\n\n"
 
1314
 
 
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)
 
1320
        sio = io.StringIO()
 
1321
        cp.write(sio)
 
1322
        return sio.getvalue()
 
1323
 
 
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)
 
1329
 
 
1330
    def test_none_as_value_stringified_raw(self):
 
1331
        output = self.prepare(configparser.RawConfigParser)
 
1332
        self.assertEqual(output, self.expected_output)
 
1333
 
 
1334
 
 
1335
class SortedTestCase(RawConfigParserTestCase):
 
1336
    dict_type = SortedDict
 
1337
 
 
1338
    def test_sorted(self):
 
1339
        cf = self.fromstring("[b]\n"
 
1340
                             "o4=1\n"
 
1341
                             "o3=2\n"
 
1342
                             "o2=3\n"
 
1343
                             "o1=4\n"
 
1344
                             "[a]\n"
 
1345
                             "k=v\n")
 
1346
        output = io.StringIO()
 
1347
        cf.write(output)
 
1348
        self.assertEqual(output.getvalue(),
 
1349
                         "[a]\n"
 
1350
                         "k = v\n\n"
 
1351
                         "[b]\n"
 
1352
                         "o1 = 4\n"
 
1353
                         "o2 = 3\n"
 
1354
                         "o3 = 2\n"
 
1355
                         "o4 = 1\n\n")
 
1356
 
 
1357
 
 
1358
class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase):
 
1359
    config_class = configparser.RawConfigParser
 
1360
    comment_prefixes = '#;'
 
1361
    inline_comment_prefixes = ';'
 
1362
 
 
1363
    def test_comment_handling(self):
 
1364
        config_string = textwrap.dedent("""\
 
1365
        [Commented Bar]
 
1366
        baz=qwe ; a comment
 
1367
        foo: bar # not a comment!
 
1368
        # but this is a comment
 
1369
        ; another comment
 
1370
        quirk: this;is not a comment
 
1371
        ; a space must precede an inline comment
 
1372
        """)
 
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')
 
1379
 
 
1380
class CopyTestCase(BasicTestCase, unittest.TestCase):
 
1381
    config_class = configparser.ConfigParser
 
1382
 
 
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:
 
1392
                continue
 
1393
            for default, value in cf[self.default_section].items():
 
1394
                if section[default] == value:
 
1395
                    del section[default]
 
1396
        return cf_copy
 
1397
 
 
1398
 
 
1399
class FakeFile:
 
1400
    def __init__(self):
 
1401
        file_path = support.findfile("cfgparser.1")
 
1402
        with open(file_path) as f:
 
1403
            self.lines = f.readlines()
 
1404
            self.lines.reverse()
 
1405
 
 
1406
    def readline(self):
 
1407
        if len(self.lines):
 
1408
            return self.lines.pop()
 
1409
        return ''
 
1410
 
 
1411
 
 
1412
def readline_generator(f):
 
1413
    """As advised in Doc/library/configparser.rst."""
 
1414
    line = f.readline()
 
1415
    while line:
 
1416
        yield line
 
1417
        line = f.readline()
 
1418
 
 
1419
 
 
1420
class ReadFileTestCase(unittest.TestCase):
 
1421
    def test_file(self):
 
1422
        file_paths = [support.findfile("cfgparser.1")]
 
1423
        try:
 
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:
 
1430
                parser.read_file(f)
 
1431
            self.assertIn("Foo Bar", parser)
 
1432
            self.assertIn("foo", parser["Foo Bar"])
 
1433
            self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
 
1434
 
 
1435
    def test_iterable(self):
 
1436
        lines = textwrap.dedent("""
 
1437
        [Foo Bar]
 
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")
 
1444
 
 
1445
    def test_readline_generator(self):
 
1446
        """Issue #11670."""
 
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")
 
1454
 
 
1455
    def test_source_as_bytes(self):
 
1456
        """Issue #18260."""
 
1457
        lines = textwrap.dedent("""
 
1458
        [badbad]
 
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")
 
1463
        self.assertEqual(
 
1464
            str(dse.exception),
 
1465
            "While reading from b'badbad' [line  2]: section 'badbad' "
 
1466
            "already exists"
 
1467
        )
 
1468
        lines = textwrap.dedent("""
 
1469
        [badbad]
 
1470
        bad = bad
 
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")
 
1475
        self.assertEqual(
 
1476
            str(dse.exception),
 
1477
            "While reading from b'badbad' [line  3]: option 'bad' in section "
 
1478
            "'badbad' already exists"
 
1479
        )
 
1480
        lines = textwrap.dedent("""
 
1481
        [badbad]
 
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")
 
1486
        self.assertEqual(
 
1487
            str(dse.exception),
 
1488
            "Source contains parsing errors: b'badbad'\n\t[line  2]: '= bad'"
 
1489
        )
 
1490
        lines = textwrap.dedent("""
 
1491
        [badbad
 
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")
 
1496
        self.assertEqual(
 
1497
            str(dse.exception),
 
1498
            "File contains no section headers.\nfile: b'badbad', line: 1\n"
 
1499
            "'[badbad'"
 
1500
        )
 
1501
 
 
1502
 
 
1503
class CoverageOneHundredTestCase(unittest.TestCase):
 
1504
    """Covers edge cases in the codebase."""
 
1505
 
 
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' "
 
1514
                                     "already exists")
 
1515
 
 
1516
    def test_interpolation_depth_error(self):
 
1517
        error = configparser.InterpolationDepthError('option', 'section',
 
1518
                                                     'rawval')
 
1519
        self.assertEqual(error.args, ('option', 'section', 'rawval'))
 
1520
        self.assertEqual(error.option, 'option')
 
1521
        self.assertEqual(error.section, 'section')
 
1522
 
 
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 "
 
1527
                                            "given.")
 
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')
 
1539
        for warning in w:
 
1540
            self.assertTrue(warning.category is DeprecationWarning)
 
1541
 
 
1542
    def test_interpolation_validation(self):
 
1543
        parser = configparser.ConfigParser()
 
1544
        parser.read_string("""
 
1545
            [section]
 
1546
            invalid_percent = %
 
1547
            invalid_reference = %(()
 
1548
            invalid_variable = %(does_not_exist)s
 
1549
        """)
 
1550
        with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
 
1551
            parser['section']['invalid_percent']
 
1552
        self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
 
1553
                                            "'(', found: '%'")
 
1554
        with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
 
1555
            parser['section']['invalid_reference']
 
1556
        self.assertEqual(str(cm.exception), "bad interpolation variable "
 
1557
                                            "reference '%(()'")
 
1558
 
 
1559
    def test_readfp_deprecation(self):
 
1560
        sio = io.StringIO("""
 
1561
        [section]
 
1562
        option = value
 
1563
        """)
 
1564
        parser = configparser.ConfigParser()
 
1565
        with warnings.catch_warnings(record=True) as w:
 
1566
            warnings.simplefilter("always", DeprecationWarning)
 
1567
            parser.readfp(sio, filename='StringIO')
 
1568
        for warning in w:
 
1569
            self.assertTrue(warning.category is DeprecationWarning)
 
1570
        self.assertEqual(len(parser), 2)
 
1571
        self.assertEqual(parser['section']['option'], 'value')
 
1572
 
 
1573
    def test_safeconfigparser_deprecation(self):
 
1574
        with warnings.catch_warnings(record=True) as w:
 
1575
            warnings.simplefilter("always", DeprecationWarning)
 
1576
            parser = configparser.SafeConfigParser()
 
1577
        for warning in w:
 
1578
            self.assertTrue(warning.category is DeprecationWarning)
 
1579
 
 
1580
    def test_sectionproxy_repr(self):
 
1581
        parser = configparser.ConfigParser()
 
1582
        parser.read_string("""
 
1583
            [section]
 
1584
            key = value
 
1585
        """)
 
1586
        self.assertEqual(repr(parser['section']), '<Section: section>')
 
1587
 
 
1588
 
 
1589
class ExceptionPicklingTestCase(unittest.TestCase):
 
1590
    """Tests for issue #13760: ConfigParser exceptions are not picklable."""
 
1591
 
 
1592
    def test_error(self):
 
1593
        import pickle
 
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))
 
1599
 
 
1600
    def test_nosectionerror(self):
 
1601
        import pickle
 
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))
 
1609
 
 
1610
    def test_nooptionerror(self):
 
1611
        import pickle
 
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))
 
1620
 
 
1621
    def test_duplicatesectionerror(self):
 
1622
        import pickle
 
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))
 
1632
 
 
1633
    def test_duplicateoptionerror(self):
 
1634
        import pickle
 
1635
        e1 = configparser.DuplicateOptionError('section', 'option', 'source',
 
1636
            123)
 
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))
 
1646
 
 
1647
    def test_interpolationerror(self):
 
1648
        import pickle
 
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))
 
1657
 
 
1658
    def test_interpolationmissingoptionerror(self):
 
1659
        import pickle
 
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))
 
1670
 
 
1671
    def test_interpolationsyntaxerror(self):
 
1672
        import pickle
 
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))
 
1681
 
 
1682
    def test_interpolationdeptherror(self):
 
1683
        import pickle
 
1684
        e1 = configparser.InterpolationDepthError('option', 'section',
 
1685
            'rawval')
 
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))
 
1693
 
 
1694
    def test_parsingerror(self):
 
1695
        import pickle
 
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))
 
1718
 
 
1719
    def test_missingsectionheadererror(self):
 
1720
        import pickle
 
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))
 
1730
 
 
1731
 
 
1732
class InlineCommentStrippingTestCase(unittest.TestCase):
 
1733
    """Tests for issue #14590: ConfigParser doesn't strip inline comment when
 
1734
    delimiter occurs earlier without preceding space.."""
 
1735
 
 
1736
    def test_stripping(self):
 
1737
        cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
 
1738
                '//'))
 
1739
        cfg.read_string("""
 
1740
        [section]
 
1741
        k1 = v1;still v1
 
1742
        k2 = v2 ;a comment
 
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
 
1748
 
 
1749
        [multiprefix]
 
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
 
1753
        """)
 
1754
        s = cfg['section']
 
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')
 
1766
 
 
1767
 
 
1768
if __name__ == '__main__':
 
1769
    unittest.main()