configparser.py 53.1 KB
Newer Older
1 2
"""Configuration file parser.

3
A configuration file consists of sections, lead by a "[section]" header,
4
and followed by "name: value" entries, with continuations and such in
Barry Warsaw's avatar
Barry Warsaw committed
5 6
the style of RFC 822.

7
Intrinsic defaults can be specified by passing them into the
8
ConfigParser constructor as a dictionary.
9 10 11

class:

12
ConfigParser -- responsible for parsing a list of
13
                    configuration files, and managing the parsed database.
14 15 16

    methods:

17
    __init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
18 19
             delimiters=('=', ':'), comment_prefixes=('#', ';'),
             inline_comment_prefixes=None, strict=True,
20 21
             empty_lines_in_values=True, default_section='DEFAULT',
             interpolation=<unset>, converters=<unset>):
22 23
        Create the parser. When `defaults' is given, it is initialized into the
        dictionary or intrinsic defaults. The keys must be strings, the values
24
        must be appropriate for %()s string interpolation.
25 26 27 28 29 30 31 32 33

        When `dict_type' is given, it will be used to create the dictionary
        objects for the list of sections, for the options within a section, and
        for the default values.

        When `delimiters' is given, it will be used as the set of substrings
        that divide keys from values.

        When `comment_prefixes' is given, it will be used as the set of
34 35 36 37 38
        substrings that prefix comments in empty lines. Comments can be
        indented.

        When `inline_comment_prefixes' is given, it will be used as the set of
        substrings that prefix comments in non-empty lines.
39

Fred Drake's avatar
Fred Drake committed
40 41
        When `strict` is True, the parser won't allow for any section or option
        duplicates while reading from a single source (file, string or
42
        dictionary). Default is True.
Fred Drake's avatar
Fred Drake committed
43

44 45 46 47 48 49
        When `empty_lines_in_values' is False (default: True), each empty line
        marks the end of an option. Otherwise, internal empty lines of
        a multiline option are kept as part of the value.

        When `allow_no_value' is True (default: False), options without
        values are accepted; the value presented for these is None.
50

51 52 53 54 55 56 57 58
        When `default_section' is given, the name of the special section is
        named accordingly. By default it is called ``"DEFAULT"`` but this can
        be customized to point to any other valid section name. Its current
        value can be retrieved using the ``parser_instance.default_section``
        attribute and may be modified at runtime.

        When `interpolation` is given, it should be an Interpolation subclass
        instance. It will be used as the handler for option value
59
        pre-processing when using getters. RawConfigParser objects don't do
60 61 62 63 64 65 66 67 68 69
        any sort of interpolation, whereas ConfigParser uses an instance of
        BasicInterpolation. The library also provides a ``zc.buildbot``
        inspired ExtendedInterpolation implementation.

        When `converters` is given, it should be a dictionary where each key
        represents the name of a type converter and each value is a callable
        implementing the conversion from string to the desired datatype. Every
        converter gets its corresponding get*() method on the parser object and
        section proxies.

70
    sections()
71
        Return all the configuration section names, sans DEFAULT.
72

73
    has_section(section)
74
        Return whether the given section exists.
75

76
    has_option(section, option)
77
        Return whether the given option exists in the given section.
78

79
    options(section)
80
        Return list of configuration options for the named section.
81

82
    read(filenames, encoding=None)
83
        Read and parse the iterable of named configuration files, given by
Guido van Rossum's avatar
Guido van Rossum committed
84
        name.  A single filename is also allowed.  Non-existing files
Fred Drake's avatar
Fred Drake committed
85
        are ignored.  Return list of successfully read files.
Guido van Rossum's avatar
Guido van Rossum committed
86

Fred Drake's avatar
Fred Drake committed
87
    read_file(f, filename=None)
88
        Read and parse one configuration file, given as a file object.
Fred Drake's avatar
Fred Drake committed
89 90 91 92 93 94 95 96 97 98
        The filename defaults to f.name; it is only used in error
        messages (if f has no `name' attribute, the string `<???>' is used).

    read_string(string)
        Read configuration from a given string.

    read_dict(dictionary)
        Read configuration from a dictionary. Keys are section names,
        values are dictionaries with keys and values that should be present
        in the section. If the used dictionary type preserves order, sections
99 100
        and their keys will be added in order. Values are automatically
        converted to strings.
101

102
    get(section, option, raw=False, vars=None, fallback=_UNSET)
103
        Return a string value for the named option.  All % interpolations are
104 105 106
        expanded in the return values, based on the defaults passed into the
        constructor and the DEFAULT section.  Additional substitutions may be
        provided using the `vars' argument, which must be a dictionary whose
107 108
        contents override any pre-existing defaults. If `option' is a key in
        `vars', the value from `vars' is used.
109

110
    getint(section, options, raw=False, vars=None, fallback=_UNSET)
111
        Like get(), but convert value to an integer.
112

113
    getfloat(section, options, raw=False, vars=None, fallback=_UNSET)
114
        Like get(), but convert value to a float.
115

116
    getboolean(section, options, raw=False, vars=None, fallback=_UNSET)
117
        Like get(), but convert value to a boolean (currently case
118 119
        insensitively defined as 0, false, no, off for False, and 1, true,
        yes, on for True).  Returns False or True.
120

121
    items(section=_UNSET, raw=False, vars=None)
122 123 124
        If section is given, return a list of tuples with (name, value) for
        each option in the section. Otherwise, return a list of tuples with
        (section_name, section_proxy) for each section, including DEFAULTSECT.
125

126
    remove_section(section)
127
        Remove the given file section and all its options.
128 129

    remove_option(section, option)
130
        Remove the given option from the given section.
131 132

    set(section, option, value)
133
        Set the given option.
134

135 136 137 138
    write(fp, space_around_delimiters=True)
        Write the configuration state in .ini format. If
        `space_around_delimiters' is True (the default), delimiters
        between keys and values are surrounded by spaces.
139 140
"""

141
from collections.abc import MutableMapping
142
from collections import ChainMap as _ChainMap
143
import functools
Fred Drake's avatar
Fred Drake committed
144
import io
145
import itertools
146
import os
Barry Warsaw's avatar
Barry Warsaw committed
147
import re
148
import sys
Fred Drake's avatar
Fred Drake committed
149
import warnings
150

Fred Drake's avatar
Fred Drake committed
151 152
__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError",
           "NoOptionError", "InterpolationError", "InterpolationDepthError",
153 154
           "InterpolationMissingOptionError", "InterpolationSyntaxError",
           "ParsingError", "MissingSectionHeaderError",
155
           "ConfigParser", "SafeConfigParser", "RawConfigParser",
156 157
           "Interpolation", "BasicInterpolation",  "ExtendedInterpolation",
           "LegacyInterpolation", "SectionProxy", "ConverterMapping",
158
           "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
159

160
_default_dict = dict
161 162
DEFAULTSECT = "DEFAULT"

163 164
MAX_INTERPOLATION_DEPTH = 10

165

Tim Peters's avatar
Tim Peters committed
166

167
# exception classes
168
class Error(Exception):
169 170
    """Base class for ConfigParser exceptions."""

171
    def __init__(self, msg=''):
172
        self.message = msg
173
        Exception.__init__(self, msg)
174

175
    def __repr__(self):
176
        return self.message
177

178
    __str__ = __repr__
179

180

181
class NoSectionError(Error):
182 183
    """Raised when no section matches a requested option."""

184
    def __init__(self, section):
185
        Error.__init__(self, 'No section: %r' % (section,))
186
        self.section = section
187
        self.args = (section, )
188

189

190
class DuplicateSectionError(Error):
Fred Drake's avatar
Fred Drake committed
191 192 193 194 195 196 197 198 199 200
    """Raised when a section is repeated in an input source.

    Possible repetitions that raise this exception are: multiple creation
    using the API or in strict parsers when a section is found more than once
    in a single input file, string or dictionary.
    """

    def __init__(self, section, source=None, lineno=None):
        msg = [repr(section), " already exists"]
        if source is not None:
201
            message = ["While reading from ", repr(source)]
Fred Drake's avatar
Fred Drake committed
202 203 204 205 206 207 208 209
            if lineno is not None:
                message.append(" [line {0:2d}]".format(lineno))
            message.append(": section ")
            message.extend(msg)
            msg = message
        else:
            msg.insert(0, "Section ")
        Error.__init__(self, "".join(msg))
210
        self.section = section
Fred Drake's avatar
Fred Drake committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
        self.source = source
        self.lineno = lineno
        self.args = (section, source, lineno)


class DuplicateOptionError(Error):
    """Raised by strict parsers when an option is repeated in an input source.

    Current implementation raises this exception only when an option is found
    more than once in a single file, string or dictionary.
    """

    def __init__(self, section, option, source=None, lineno=None):
        msg = [repr(option), " in section ", repr(section),
               " already exists"]
        if source is not None:
227
            message = ["While reading from ", repr(source)]
Fred Drake's avatar
Fred Drake committed
228 229 230 231 232 233 234 235 236 237 238 239 240
            if lineno is not None:
                message.append(" [line {0:2d}]".format(lineno))
            message.append(": option ")
            message.extend(msg)
            msg = message
        else:
            msg.insert(0, "Option ")
        Error.__init__(self, "".join(msg))
        self.section = section
        self.option = option
        self.source = source
        self.lineno = lineno
        self.args = (section, option, source, lineno)
241

242

243
class NoOptionError(Error):
244 245
    """A requested option was not found."""

246
    def __init__(self, option, section):
247
        Error.__init__(self, "No option %r in section: %r" %
248 249 250
                       (option, section))
        self.option = option
        self.section = section
251
        self.args = (option, section)
252

253

254
class InterpolationError(Error):
255
    """Base class for interpolation-related exceptions."""
256

257 258
    def __init__(self, option, section, msg):
        Error.__init__(self, msg)
259 260
        self.option = option
        self.section = section
261
        self.args = (option, section, msg)
262

263

264 265 266 267
class InterpolationMissingOptionError(InterpolationError):
    """A string substitution required a setting which was not available."""

    def __init__(self, option, section, rawval, reference):
268 269 270
        msg = ("Bad value substitution: option {!r} in section {!r} contains "
               "an interpolation key {!r} which is not a valid option name. "
               "Raw value: {!r}".format(option, section, reference, rawval))
271 272
        InterpolationError.__init__(self, option, section, msg)
        self.reference = reference
273
        self.args = (option, section, rawval, reference)
274

275

276
class InterpolationSyntaxError(InterpolationError):
Fred Drake's avatar
Fred Drake committed
277 278
    """Raised when the source text contains invalid syntax.

279 280
    Current implementation raises this exception when the source text into
    which substitutions are made does not conform to the required syntax.
Fred Drake's avatar
Fred Drake committed
281
    """
282

283

284
class InterpolationDepthError(InterpolationError):
285 286
    """Raised when substitutions are nested too deeply."""

287
    def __init__(self, option, section, rawval):
288 289 290 291 292
        msg = ("Recursion limit exceeded in value substitution: option {!r} "
               "in section {!r} contains an interpolation key which "
               "cannot be substituted in {} steps. Raw value: {!r}"
               "".format(option, section, MAX_INTERPOLATION_DEPTH,
                         rawval))
293
        InterpolationError.__init__(self, option, section, msg)
294
        self.args = (option, section, rawval)
Barry Warsaw's avatar
Barry Warsaw committed
295

296

Barry Warsaw's avatar
Barry Warsaw committed
297
class ParsingError(Error):
298 299
    """Raised when a configuration file does not follow legal syntax."""

Fred Drake's avatar
Fred Drake committed
300 301 302 303 304 305 306 307 308 309
    def __init__(self, source=None, filename=None):
        # Exactly one of `source'/`filename' arguments has to be given.
        # `filename' kept for compatibility.
        if filename and source:
            raise ValueError("Cannot specify both `filename' and `source'. "
                             "Use `source'.")
        elif not filename and not source:
            raise ValueError("Required argument `source' not given.")
        elif filename:
            source = filename
310
        Error.__init__(self, 'Source contains parsing errors: %r' % source)
Fred Drake's avatar
Fred Drake committed
311
        self.source = source
Barry Warsaw's avatar
Barry Warsaw committed
312
        self.errors = []
Fred Drake's avatar
Fred Drake committed
313 314 315 316 317 318
        self.args = (source, )

    @property
    def filename(self):
        """Deprecated, use `source'."""
        warnings.warn(
319
            "The 'filename' attribute will be removed in future versions.  "
Fred Drake's avatar
Fred Drake committed
320
            "Use 'source' instead.",
321
            DeprecationWarning, stacklevel=2
Fred Drake's avatar
Fred Drake committed
322 323 324 325 326 327 328 329 330
        )
        return self.source

    @filename.setter
    def filename(self, value):
        """Deprecated, user `source'."""
        warnings.warn(
            "The 'filename' attribute will be removed in future versions.  "
            "Use 'source' instead.",
331
            DeprecationWarning, stacklevel=2
Fred Drake's avatar
Fred Drake committed
332 333
        )
        self.source = value
Barry Warsaw's avatar
Barry Warsaw committed
334 335 336

    def append(self, lineno, line):
        self.errors.append((lineno, line))
337
        self.message += '\n\t[line %2d]: %s' % (lineno, line)
Barry Warsaw's avatar
Barry Warsaw committed
338

339

340
class MissingSectionHeaderError(ParsingError):
341 342
    """Raised when a key-value pair is found before any section header."""

343 344 345
    def __init__(self, filename, lineno, line):
        Error.__init__(
            self,
346
            'File contains no section headers.\nfile: %r, line: %d\n%r' %
347
            (filename, lineno, line))
Fred Drake's avatar
Fred Drake committed
348
        self.source = filename
349 350
        self.lineno = lineno
        self.line = line
351
        self.args = (filename, lineno, line)
352

353

354 355 356 357 358 359
# Used in parser getters to indicate the default behaviour when a specific
# option is not found it to raise an exception. Created to enable `None' as
# a valid fallback value.
_UNSET = object()


360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
class Interpolation:
    """Dummy interpolation that passes the value through with no changes."""

    def before_get(self, parser, section, option, value, defaults):
        return value

    def before_set(self, parser, section, option, value):
        return value

    def before_read(self, parser, section, option, value):
        return value

    def before_write(self, parser, section, option, value):
        return value


class BasicInterpolation(Interpolation):
377
    """Interpolation as implemented in the classic ConfigParser.
378 379 380 381 382 383 384 385 386 387

    The option values can contain format strings which refer to other values in
    the same section, or values in the special default section.

    For example:

        something: %(dir)s/whatever

    would resolve the "%(dir)s" to the value of dir.  All reference
    expansions are done late, on demand. If a user needs to use a bare % in
Ezio Melotti's avatar
Ezio Melotti committed
388
    a configuration file, she can escape it by writing %%. Other % usage
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
    is considered a user error and raises `InterpolationSyntaxError'."""

    _KEYCRE = re.compile(r"%\(([^)]+)\)s")

    def before_get(self, parser, section, option, value, defaults):
        L = []
        self._interpolate_some(parser, option, L, value, section, defaults, 1)
        return ''.join(L)

    def before_set(self, parser, section, option, value):
        tmp_value = value.replace('%%', '') # escaped percent signs
        tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
        if '%' in tmp_value:
            raise ValueError("invalid interpolation syntax in %r at "
                             "position %d" % (value, tmp_value.find('%')))
        return value

    def _interpolate_some(self, parser, option, accum, rest, section, map,
                          depth):
408
        rawval = parser.get(section, option, raw=True, fallback=rest)
409
        if depth > MAX_INTERPOLATION_DEPTH:
410
            raise InterpolationDepthError(option, section, rawval)
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
        while rest:
            p = rest.find("%")
            if p < 0:
                accum.append(rest)
                return
            if p > 0:
                accum.append(rest[:p])
                rest = rest[p:]
            # p is no longer used
            c = rest[1:2]
            if c == "%":
                accum.append("%")
                rest = rest[2:]
            elif c == "(":
                m = self._KEYCRE.match(rest)
                if m is None:
                    raise InterpolationSyntaxError(option, section,
                        "bad interpolation variable reference %r" % rest)
                var = parser.optionxform(m.group(1))
                rest = rest[m.end():]
                try:
                    v = map[var]
                except KeyError:
                    raise InterpolationMissingOptionError(
435
                        option, section, rawval, var) from None
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
                if "%" in v:
                    self._interpolate_some(parser, option, accum, v,
                                           section, map, depth + 1)
                else:
                    accum.append(v)
            else:
                raise InterpolationSyntaxError(
                    option, section,
                    "'%%' must be followed by '%%' or '(', "
                    "found: %r" % (rest,))


class ExtendedInterpolation(Interpolation):
    """Advanced variant of interpolation, supports the syntax used by
    `zc.buildout'. Enables interpolation between sections."""

    _KEYCRE = re.compile(r"\$\{([^}]+)\}")

    def before_get(self, parser, section, option, value, defaults):
        L = []
        self._interpolate_some(parser, option, L, value, section, defaults, 1)
        return ''.join(L)

    def before_set(self, parser, section, option, value):
        tmp_value = value.replace('$$', '') # escaped dollar signs
        tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
        if '$' in tmp_value:
            raise ValueError("invalid interpolation syntax in %r at "
464
                             "position %d" % (value, tmp_value.find('$')))
465 466 467 468
        return value

    def _interpolate_some(self, parser, option, accum, rest, section, map,
                          depth):
469
        rawval = parser.get(section, option, raw=True, fallback=rest)
470
        if depth > MAX_INTERPOLATION_DEPTH:
471
            raise InterpolationDepthError(option, section, rawval)
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
        while rest:
            p = rest.find("$")
            if p < 0:
                accum.append(rest)
                return
            if p > 0:
                accum.append(rest[:p])
                rest = rest[p:]
            # p is no longer used
            c = rest[1:2]
            if c == "$":
                accum.append("$")
                rest = rest[2:]
            elif c == "{":
                m = self._KEYCRE.match(rest)
                if m is None:
                    raise InterpolationSyntaxError(option, section,
                        "bad interpolation variable reference %r" % rest)
490
                path = m.group(1).split(':')
491 492 493 494 495
                rest = rest[m.end():]
                sect = section
                opt = option
                try:
                    if len(path) == 1:
496
                        opt = parser.optionxform(path[0])
497 498 499
                        v = map[opt]
                    elif len(path) == 2:
                        sect = path[0]
500
                        opt = parser.optionxform(path[1])
501 502 503 504 505
                        v = parser.get(sect, opt, raw=True)
                    else:
                        raise InterpolationSyntaxError(
                            option, section,
                            "More than one ':' found: %r" % (rest,))
506
                except (KeyError, NoSectionError, NoOptionError):
507
                    raise InterpolationMissingOptionError(
508
                        option, section, rawval, ":".join(path)) from None
509 510 511 512 513 514 515 516 517 518 519 520 521
                if "$" in v:
                    self._interpolate_some(parser, opt, accum, v, sect,
                                           dict(parser.items(sect, raw=True)),
                                           depth + 1)
                else:
                    accum.append(v)
            else:
                raise InterpolationSyntaxError(
                    option, section,
                    "'$' must be followed by '$' or '{', "
                    "found: %r" % (rest,))


522 523
class LegacyInterpolation(Interpolation):
    """Deprecated interpolation used in old versions of ConfigParser.
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    Use BasicInterpolation or ExtendedInterpolation instead."""

    _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")

    def before_get(self, parser, section, option, value, vars):
        rawval = value
        depth = MAX_INTERPOLATION_DEPTH
        while depth:                    # Loop through this until it's done
            depth -= 1
            if value and "%(" in value:
                replace = functools.partial(self._interpolation_replace,
                                            parser=parser)
                value = self._KEYCRE.sub(replace, value)
                try:
                    value = value % vars
                except KeyError as e:
                    raise InterpolationMissingOptionError(
541
                        option, section, rawval, e.args[0]) from None
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
            else:
                break
        if value and "%(" in value:
            raise InterpolationDepthError(option, section, rawval)
        return value

    def before_set(self, parser, section, option, value):
        return value

    @staticmethod
    def _interpolation_replace(match, parser):
        s = match.group(1)
        if s is None:
            return match.group()
        else:
            return "%%(%s)s" % parser.optionxform(s)


560
class RawConfigParser(MutableMapping):
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
    """ConfigParser that does not do interpolation."""

    # Regular expressions for parsing section headers and options
    _SECT_TMPL = r"""
        \[                                 # [
        (?P<header>[^]]+)                  # very permissive!
        \]                                 # ]
        """
    _OPT_TMPL = r"""
        (?P<option>.*?)                    # very permissive!
        \s*(?P<vi>{delim})\s*              # any number of space/tab,
                                           # followed by any of the
                                           # allowed delimiters,
                                           # followed by any space/tab
        (?P<value>.*)$                     # everything up to eol
        """
    _OPT_NV_TMPL = r"""
        (?P<option>.*?)                    # very permissive!
        \s*(?:                             # any number of space/tab,
        (?P<vi>{delim})\s*                 # optionally followed by
                                           # any of the allowed
                                           # delimiters, followed by any
                                           # space/tab
        (?P<value>.*))?$                   # everything up to eol
        """
586 587
    # Interpolation algorithm to be used if the user does not specify another
    _DEFAULT_INTERPOLATION = Interpolation()
588 589 590 591 592 593 594 595 596
    # Compiled regular expression for matching sections
    SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)
    # Compiled regular expression for matching options with typical separators
    OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE)
    # Compiled regular expression for matching options with optional values
    # delimited using typical separators
    OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE)
    # Compiled regular expression for matching leading whitespace in a line
    NONSPACECRE = re.compile(r"\S")
597 598 599
    # Possible boolean values in the configuration.
    BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True,
                      '0': False, 'no': False, 'false': False, 'off': False}
600

601
    def __init__(self, defaults=None, dict_type=_default_dict,
Fred Drake's avatar
Fred Drake committed
602
                 allow_no_value=False, *, delimiters=('=', ':'),
603 604
                 comment_prefixes=('#', ';'), inline_comment_prefixes=None,
                 strict=True, empty_lines_in_values=True,
605
                 default_section=DEFAULTSECT,
606
                 interpolation=_UNSET, converters=_UNSET):
607

608 609 610
        self._dict = dict_type
        self._sections = self._dict()
        self._defaults = self._dict()
611
        self._converters = ConverterMapping(self)
612
        self._proxies = self._dict()
613
        self._proxies[default_section] = SectionProxy(self, default_section)
614 615 616 617
        self._delimiters = tuple(delimiters)
        if delimiters == ('=', ':'):
            self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE
        else:
Fred Drake's avatar
Fred Drake committed
618
            d = "|".join(re.escape(d) for d in delimiters)
619
            if allow_no_value:
Fred Drake's avatar
Fred Drake committed
620
                self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d),
621 622
                                          re.VERBOSE)
            else:
Fred Drake's avatar
Fred Drake committed
623
                self._optcre = re.compile(self._OPT_TMPL.format(delim=d),
624
                                          re.VERBOSE)
625 626
        self._comment_prefixes = tuple(comment_prefixes or ())
        self._inline_comment_prefixes = tuple(inline_comment_prefixes or ())
Fred Drake's avatar
Fred Drake committed
627
        self._strict = strict
628
        self._allow_no_value = allow_no_value
629
        self._empty_lines_in_values = empty_lines_in_values
630
        self.default_section=default_section
631 632 633 634 635
        self._interpolation = interpolation
        if self._interpolation is _UNSET:
            self._interpolation = self._DEFAULT_INTERPOLATION
        if self._interpolation is None:
            self._interpolation = Interpolation()
636 637
        if converters is not _UNSET:
            self._converters.update(converters)
638
        if defaults:
639
            self._read_defaults(defaults)
640 641

    def defaults(self):
642
        return self._defaults
643 644

    def sections(self):
645
        """Return a list of section names, excluding [DEFAULT]"""
646
        # self._sections will never have [DEFAULT] in it
647
        return list(self._sections.keys())
648 649

    def add_section(self, section):
650
        """Create a new section in the configuration.
651

652
        Raise DuplicateSectionError if a section by the specified name
653
        already exists. Raise ValueError if name is DEFAULT.
654
        """
655
        if section == self.default_section:
656
            raise ValueError('Invalid section name: %r' % section)
Christian Heimes's avatar
Christian Heimes committed
657

658
        if section in self._sections:
659
            raise DuplicateSectionError(section)
660
        self._sections[section] = self._dict()
661
        self._proxies[section] = SectionProxy(self, section)
662 663

    def has_section(self, section):
664
        """Indicate whether the named section is present in the configuration.
665

666 667
        The DEFAULT section is not acknowledged.
        """
668
        return section in self._sections
669 670

    def options(self, section):
671
        """Return a list of option names for the given section name."""
672
        try:
673
            opts = self._sections[section].copy()
674
        except KeyError:
675
            raise NoSectionError(section) from None
676
        opts.update(self._defaults)
677
        return list(opts.keys())
678

679
    def read(self, filenames, encoding=None):
680
        """Read and parse a filename or an iterable of filenames.
Tim Peters's avatar
Tim Peters committed
681

Guido van Rossum's avatar
Guido van Rossum committed
682
        Files that cannot be opened are silently ignored; this is
683
        designed so that you can specify an iterable of potential
Guido van Rossum's avatar
Guido van Rossum committed
684 685
        configuration file locations (e.g. current directory, user's
        home directory, systemwide directory), and all existing
686
        configuration files in the iterable will be read.  A single
Guido van Rossum's avatar
Guido van Rossum committed
687
        filename may also be given.
Fred Drake's avatar
Fred Drake committed
688 689

        Return list of successfully read files.
Guido van Rossum's avatar
Guido van Rossum committed
690
        """
691
        if isinstance(filenames, (str, bytes, os.PathLike)):
692
            filenames = [filenames]
Fred Drake's avatar
Fred Drake committed
693
        read_ok = []
Guido van Rossum's avatar
Guido van Rossum committed
694 695
        for filename in filenames:
            try:
696 697
                with open(filename, encoding=encoding) as fp:
                    self._read(fp, filename)
698
            except OSError:
Guido van Rossum's avatar
Guido van Rossum committed
699
                continue
700 701
            if isinstance(filename, os.PathLike):
                filename = os.fspath(filename)
Fred Drake's avatar
Fred Drake committed
702 703
            read_ok.append(filename)
        return read_ok
704

Fred Drake's avatar
Fred Drake committed
705
    def read_file(self, f, source=None):
Guido van Rossum's avatar
Guido van Rossum committed
706 707
        """Like read() but the argument must be a file-like object.

708 709 710 711
        The `f' argument must be iterable, returning one line at a time.
        Optional second argument is the `source' specifying the name of the
        file being read. If not given, it is taken from f.name. If `f' has no
        `name' attribute, `<???>' is used.
Guido van Rossum's avatar
Guido van Rossum committed
712
        """
Fred Drake's avatar
Fred Drake committed
713
        if source is None:
Guido van Rossum's avatar
Guido van Rossum committed
714
            try:
715
                source = f.name
Guido van Rossum's avatar
Guido van Rossum committed
716
            except AttributeError:
Fred Drake's avatar
Fred Drake committed
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
                source = '<???>'
        self._read(f, source)

    def read_string(self, string, source='<string>'):
        """Read configuration from a given string."""
        sfile = io.StringIO(string)
        self.read_file(sfile, source)

    def read_dict(self, dictionary, source='<dict>'):
        """Read configuration from a dictionary.

        Keys are section names, values are dictionaries with keys and values
        that should be present in the section. If the used dictionary type
        preserves order, sections and their keys will be added in order.

732 733 734
        All types held in the dictionary are converted to strings during
        reading, including section names, option names and keys.

Fred Drake's avatar
Fred Drake committed
735 736 737 738 739
        Optional second argument is the `source' specifying the name of the
        dictionary being read.
        """
        elements_added = set()
        for section, keys in dictionary.items():
740
            section = str(section)
Fred Drake's avatar
Fred Drake committed
741 742
            try:
                self.add_section(section)
743
            except (DuplicateSectionError, ValueError):
Fred Drake's avatar
Fred Drake committed
744 745
                if self._strict and section in elements_added:
                    raise
746
            elements_added.add(section)
Fred Drake's avatar
Fred Drake committed
747
            for key, value in keys.items():
748
                key = self.optionxform(str(key))
749 750
                if value is not None:
                    value = str(value)
Fred Drake's avatar
Fred Drake committed
751 752 753 754 755 756 757 758 759 760
                if self._strict and (section, key) in elements_added:
                    raise DuplicateOptionError(section, key, source)
                elements_added.add((section, key))
                self.set(section, key, value)

    def readfp(self, fp, filename=None):
        """Deprecated, use read_file instead."""
        warnings.warn(
            "This method will be removed in future versions.  "
            "Use 'parser.read_file()' instead.",
761
            DeprecationWarning, stacklevel=2
Fred Drake's avatar
Fred Drake committed
762 763
        )
        self.read_file(fp, source=filename)
Guido van Rossum's avatar
Guido van Rossum committed
764

765
    def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
766 767 768 769
        """Get an option value for a given section.

        If `vars' is provided, it must be a dictionary. The option is looked up
        in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
770 771 772
        If the key is not found and `fallback' is provided, it is used as
        a fallback value. `None' can be provided as a `fallback' value.

773 774 775 776 777 778
        If interpolation is enabled and the optional argument `raw' is False,
        all interpolations are expanded in the return values.

        Arguments `raw', `vars', and `fallback' are keyword only.

        The section DEFAULT is special.
779 780 781 782
        """
        try:
            d = self._unify_values(section, vars)
        except NoSectionError:
783
            if fallback is _UNSET:
784
                raise
785
            else:
786
                return fallback
787 788
        option = self.optionxform(option)
        try:
789
            value = d[option]
790
        except KeyError:
791
            if fallback is _UNSET:
792
                raise NoOptionError(option, section)
793
            else:
794
                return fallback
795

796 797 798 799 800
        if raw or value is None:
            return value
        else:
            return self._interpolation.before_get(self, section, option, value,
                                                  d)
Tim Peters's avatar
Tim Peters committed
801

802 803
    def _get(self, section, conv, option, **kwargs):
        return conv(self.get(section, option, **kwargs))
804

805 806
    def _get_conv(self, section, option, conv, *, raw=False, vars=None,
                  fallback=_UNSET, **kwargs):
807
        try:
808 809
            return self._get(section, conv, option, raw=raw, vars=vars,
                             **kwargs)
810
        except (NoSectionError, NoOptionError):
811
            if fallback is _UNSET:
812
                raise
813 814 815 816 817 818 819
            return fallback

    # getint, getfloat and getboolean provided directly for backwards compat
    def getint(self, section, option, *, raw=False, vars=None,
               fallback=_UNSET, **kwargs):
        return self._get_conv(section, option, int, raw=raw, vars=vars,
                              fallback=fallback, **kwargs)
820

821
    def getfloat(self, section, option, *, raw=False, vars=None,
822 823 824
                 fallback=_UNSET, **kwargs):
        return self._get_conv(section, option, float, raw=raw, vars=vars,
                              fallback=fallback, **kwargs)
825

826
    def getboolean(self, section, option, *, raw=False, vars=None,
827 828 829
                   fallback=_UNSET, **kwargs):
        return self._get_conv(section, option, self._convert_to_boolean,
                              raw=raw, vars=vars, fallback=fallback, **kwargs)
830

831
    def items(self, section=_UNSET, raw=False, vars=None):
832 833 834 835 836 837 838 839 840 841
        """Return a list of (name, value) tuples for each option in a section.

        All % interpolations are expanded in the return values, based on the
        defaults passed into the constructor, unless the optional argument
        `raw' is true.  Additional substitutions may be provided using the
        `vars' argument, which must be a dictionary whose contents overrides
        any pre-existing defaults.

        The section DEFAULT is special.
        """
842 843
        if section is _UNSET:
            return super().items()
844 845 846 847 848 849
        d = self._defaults.copy()
        try:
            d.update(self._sections[section])
        except KeyError:
            if section != self.default_section:
                raise NoSectionError(section)
850
        orig_keys = list(d.keys())
851 852 853 854
        # Update with the entry specific variables
        if vars:
            for key, value in vars.items():
                d[self.optionxform(key)] = value
855 856
        value_getter = lambda option: self._interpolation.before_get(self,
            section, option, d[option], d)
857
        if raw:
858
            value_getter = lambda option: d[option]
859
        return [(option, value_getter(option)) for option in orig_keys]
860

861 862 863 864 865 866 867 868 869 870 871 872 873
    def popitem(self):
        """Remove a section from the parser and return it as
        a (section_name, section_proxy) tuple. If no section is present, raise
        KeyError.

        The section DEFAULT is never returned because it cannot be removed.
        """
        for key in self.sections():
            value = self[key]
            del self[key]
            return key, value
        raise KeyError

874
    def optionxform(self, optionstr):
875
        return optionstr.lower()
876

877
    def has_option(self, section, option):
878 879 880
        """Check for the existence of a given option in a given section.
        If the specified `section' is None or an empty string, DEFAULT is
        assumed. If the specified `section' does not exist, returns False."""
881
        if not section or section == self.default_section:
882
            option = self.optionxform(option)
883 884
            return option in self._defaults
        elif section not in self._sections:
885
            return False
886
        else:
887
            option = self.optionxform(option)
888 889
            return (option in self._sections[section]
                    or option in self._defaults)
890

891
    def set(self, section, option, value=None):
892
        """Set an option."""
893 894 895 896
        if value:
            value = self._interpolation.before_set(self, section, option,
                                                   value)
        if not section or section == self.default_section:
897
            sectdict = self._defaults
898 899
        else:
            try:
900
                sectdict = self._sections[section]
901
            except KeyError:
902
                raise NoSectionError(section) from None
903
        sectdict[self.optionxform(option)] = value
904

905 906 907 908 909 910 911 912 913 914
    def write(self, fp, space_around_delimiters=True):
        """Write an .ini-format representation of the configuration state.

        If `space_around_delimiters' is True (the default), delimiters
        between keys and values are surrounded by spaces.
        """
        if space_around_delimiters:
            d = " {} ".format(self._delimiters[0])
        else:
            d = self._delimiters[0]
915
        if self._defaults:
916
            self._write_section(fp, self.default_section,
917
                                    self._defaults.items(), d)
918
        for section in self._sections:
919 920 921 922 923 924 925
            self._write_section(fp, section,
                                self._sections[section].items(), d)

    def _write_section(self, fp, section_name, section_items, delimiter):
        """Write a single section to the specified `fp'."""
        fp.write("[{}]\n".format(section_name))
        for key, value in section_items:
926 927
            value = self._interpolation.before_write(self, section_name, key,
                                                     value)
928
            if value is not None or not self._allow_no_value:
929 930 931 932 933
                value = delimiter + str(value).replace('\n', '\n\t')
            else:
                value = ""
            fp.write("{}{}\n".format(key, value))
        fp.write("\n")
934

935
    def remove_option(self, section, option):
936
        """Remove an option."""
937
        if not section or section == self.default_section:
938
            sectdict = self._defaults
939 940
        else:
            try:
941
                sectdict = self._sections[section]
942
            except KeyError:
943
                raise NoSectionError(section) from None
944
        option = self.optionxform(option)
945
        existed = option in sectdict
946
        if existed:
947
            del sectdict[option]
948 949
        return existed

950
    def remove_section(self, section):
951
        """Remove a file section."""
952
        existed = section in self._sections
953
        if existed:
954
            del self._sections[section]
955
            del self._proxies[section]
956
        return existed
957

958
    def __getitem__(self, key):
959
        if key != self.default_section and not self.has_section(key):
960
            raise KeyError(key)
961
        return self._proxies[key]
962 963 964 965

    def __setitem__(self, key, value):
        # To conform with the mapping protocol, overwrites existing values in
        # the section.
966 967
        if key in self and self[key] is value:
            return
968 969
        # XXX this is not atomic if read_dict fails at any point. Then again,
        # no update method in configparser is atomic in this implementation.
970 971
        if key == self.default_section:
            self._defaults.clear()
972 973
        elif key in self._sections:
            self._sections[key].clear()
974 975 976
        self.read_dict({key: value})

    def __delitem__(self, key):
977
        if key == self.default_section:
978 979 980 981 982 983
            raise ValueError("Cannot remove the default section.")
        if not self.has_section(key):
            raise KeyError(key)
        self.remove_section(key)

    def __contains__(self, key):
984
        return key == self.default_section or self.has_section(key)
985 986 987 988 989 990

    def __len__(self):
        return len(self._sections) + 1 # the default section

    def __iter__(self):
        # XXX does it break when underlying container state changed?
991
        return itertools.chain((self.default_section,), self._sections.keys())
992

993
    def _read(self, fp, fpname):
994 995
        """Parse a sectioned configuration file.

Fred Drake's avatar
Fred Drake committed
996 997
        Each section in a configuration file contains a header, indicated by
        a name in square brackets (`[]'), plus key/value options, indicated by
998 999 1000
        `name' and `value' delimited with a specific substring (`=' or `:' by
        default).

Fred Drake's avatar
Fred Drake committed
1001 1002 1003
        Values can span multiple lines, as long as they are indented deeper
        than the first line of the value. Depending on the parser's mode, blank
        lines may be treated as parts of multiline values or ignored.
1004 1005

        Configuration files may include comments, prefixed by specific
Fred Drake's avatar
Fred Drake committed
1006 1007
        characters (`#' and `;' by default). Comments may appear on their own
        in an otherwise empty line or may be entered in lines holding values or
1008
        section names.
1009
        """
Fred Drake's avatar
Fred Drake committed
1010
        elements_added = set()
1011
        cursect = None                        # None, or a dictionary
Fred Drake's avatar
Fred Drake committed
1012
        sectname = None
1013 1014
        optname = None
        lineno = 0
1015
        indent_level = 0
1016
        e = None                              # None, or an exception
1017
        for lineno, line in enumerate(fp, start=1):
1018
            comment_start = sys.maxsize
1019
            # strip inline comments
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
            inline_prefixes = {p: -1 for p in self._inline_comment_prefixes}
            while comment_start == sys.maxsize and inline_prefixes:
                next_prefixes = {}
                for prefix, index in inline_prefixes.items():
                    index = line.find(prefix, index+1)
                    if index == -1:
                        continue
                    next_prefixes[prefix] = index
                    if index == 0 or (index > 0 and line[index-1].isspace()):
                        comment_start = min(comment_start, index)
                inline_prefixes = next_prefixes
1031 1032 1033 1034 1035
            # strip full line comments
            for prefix in self._comment_prefixes:
                if line.strip().startswith(prefix):
                    comment_start = 0
                    break
1036 1037
            if comment_start == sys.maxsize:
                comment_start = None
1038 1039
            value = line[:comment_start].strip()
            if not value:
1040
                if self._empty_lines_in_values:
1041 1042
                    # add empty line to the value, but only if there was no
                    # comment on the line
1043 1044 1045 1046 1047
                    if (comment_start is None and
                        cursect is not None and
                        optname and
                        cursect[optname] is not None):
                        cursect[optname].append('') # newlines added at join
1048 1049 1050
                else:
                    # empty line marks end of value
                    indent_level = sys.maxsize
1051 1052
                continue
            # continuation line?
1053 1054 1055 1056 1057
            first_nonspace = self.NONSPACECRE.search(line)
            cur_indent_level = first_nonspace.start() if first_nonspace else 0
            if (cursect is not None and optname and
                cur_indent_level > indent_level):
                cursect[optname].append(value)
Barry Warsaw's avatar
Barry Warsaw committed
1058
            # a section header or option header?
1059
            else:
1060
                indent_level = cur_indent_level
Barry Warsaw's avatar
Barry Warsaw committed
1061
                # is it a section header?
1062
                mo = self.SECTCRE.match(value)
Barry Warsaw's avatar
Barry Warsaw committed
1063 1064
                if mo:
                    sectname = mo.group('header')
1065
                    if sectname in self._sections:
Fred Drake's avatar
Fred Drake committed
1066 1067 1068
                        if self._strict and sectname in elements_added:
                            raise DuplicateSectionError(sectname, fpname,
                                                        lineno)
1069
                        cursect = self._sections[sectname]
Fred Drake's avatar
Fred Drake committed
1070
                        elements_added.add(sectname)
1071
                    elif sectname == self.default_section:
1072
                        cursect = self._defaults
Barry Warsaw's avatar
Barry Warsaw committed
1073
                    else:
1074
                        cursect = self._dict()
1075
                        self._sections[sectname] = cursect
1076
                        self._proxies[sectname] = SectionProxy(self, sectname)
Fred Drake's avatar
Fred Drake committed
1077
                        elements_added.add(sectname)
Barry Warsaw's avatar
Barry Warsaw committed
1078 1079 1080 1081
                    # So sections can't start with a continuation line
                    optname = None
                # no section header in the file?
                elif cursect is None:
1082
                    raise MissingSectionHeaderError(fpname, lineno, line)
Barry Warsaw's avatar
Barry Warsaw committed
1083 1084
                # an option line?
                else:
1085
                    mo = self._optcre.match(value)
Barry Warsaw's avatar
Barry Warsaw committed
1086
                    if mo:
1087
                        optname, vi, optval = mo.group('option', 'vi', 'value')
1088 1089
                        if not optname:
                            e = self._handle_error(e, fpname, lineno, line)
1090
                        optname = self.optionxform(optname.rstrip())
Fred Drake's avatar
Fred Drake committed
1091 1092 1093 1094 1095
                        if (self._strict and
                            (sectname, optname) in elements_added):
                            raise DuplicateOptionError(sectname, optname,
                                                       fpname, lineno)
                        elements_added.add((sectname, optname))
1096 1097 1098 1099
                        # This check is fine because the OPTCRE cannot
                        # match if it would set optval to None
                        if optval is not None:
                            optval = optval.strip()
1100 1101 1102
                            cursect[optname] = [optval]
                        else:
                            # valueless option handling
1103
                            cursect[optname] = None
Barry Warsaw's avatar
Barry Warsaw committed
1104
                    else:
1105
                        # a non-fatal parsing error occurred. set up the
Barry Warsaw's avatar
Barry Warsaw committed
1106 1107 1108
                        # exception but keep going. the exception will be
                        # raised at the end of the file and will contain a
                        # list of all bogus lines
1109
                        e = self._handle_error(e, fpname, lineno, line)
1110
        self._join_multiline_values()
Barry Warsaw's avatar
Barry Warsaw committed
1111 1112 1113
        # if any parsing errors occurred, raise an exception
        if e:
            raise e
1114

1115
    def _join_multiline_values(self):
1116 1117 1118 1119
        defaults = self.default_section, self._defaults
        all_sections = itertools.chain((defaults,),
                                       self._sections.items())
        for section, options in all_sections:
1120 1121
            for name, val in options.items():
                if isinstance(val, list):
1122 1123 1124 1125
                    val = '\n'.join(val).rstrip()
                options[name] = self._interpolation.before_read(self,
                                                                section,
                                                                name, val)
1126

1127 1128 1129 1130 1131 1132
    def _read_defaults(self, defaults):
        """Read the defaults passed in the initializer.
        Note: values can be non-string."""
        for key, value in defaults.items():
            self._defaults[self.optionxform(key)] = value

1133 1134 1135 1136 1137 1138
    def _handle_error(self, exc, fpname, lineno, line):
        if not exc:
            exc = ParsingError(fpname)
        exc.append(lineno, repr(line))
        return exc

1139
    def _unify_values(self, section, vars):
1140 1141 1142
        """Create a sequence of lookups with 'vars' taking priority over
        the 'section' which takes priority over the DEFAULTSECT.

1143
        """
1144
        sectiondict = {}
1145
        try:
1146
            sectiondict = self._sections[section]
1147
        except KeyError:
1148
            if section != self.default_section:
1149
                raise NoSectionError(section) from None
1150
        # Update with the entry specific variables
1151
        vardict = {}
1152 1153 1154 1155
        if vars:
            for key, value in vars.items():
                if value is not None:
                    value = str(value)
1156 1157
                vardict[self.optionxform(key)] = value
        return _ChainMap(vardict, sectiondict, self._defaults)
1158 1159 1160 1161 1162 1163 1164 1165

    def _convert_to_boolean(self, value):
        """Return a boolean value translating from other types if necessary.
        """
        if value.lower() not in self.BOOLEAN_STATES:
            raise ValueError('Not a boolean: %s' % value)
        return self.BOOLEAN_STATES[value.lower()]

1166
    def _validate_value_types(self, *, section="", option="", value=""):
1167 1168 1169 1170 1171 1172 1173 1174 1175
        """Raises a TypeError for non-string values.

        The only legal non-string value if we allow valueless
        options is None, so we need to check if the value is a
        string if:
        - we do not allow valueless options, or
        - we allow valueless options but the value is not None

        For compatibility reasons this method is not used in classic set()
1176 1177
        for RawConfigParsers. It is invoked in every case for mapping protocol
        access and in ConfigParser.set().
1178
        """
1179 1180 1181 1182
        if not isinstance(section, str):
            raise TypeError("section names must be strings")
        if not isinstance(option, str):
            raise TypeError("option keys must be strings")
1183 1184 1185 1186
        if not self._allow_no_value or value:
            if not isinstance(value, str):
                raise TypeError("option values must be strings")

1187 1188 1189 1190
    @property
    def converters(self):
        return self._converters

1191

1192
class ConfigParser(RawConfigParser):
1193
    """ConfigParser implementing interpolation."""
1194

1195
    _DEFAULT_INTERPOLATION = BasicInterpolation()
1196

1197
    def set(self, section, option, value=None):
1198 1199
        """Set an option.  Extends RawConfigParser.set by validating type and
        interpolation syntax on the value."""
1200
        self._validate_value_types(option=option, value=value)
1201
        super().set(section, option, value)
1202

1203 1204 1205 1206 1207 1208 1209
    def add_section(self, section):
        """Create a new section in the configuration.  Extends
        RawConfigParser.add_section by validating if the section name is
        a string."""
        self._validate_value_types(section=section)
        super().add_section(section)

1210 1211
    def _read_defaults(self, defaults):
        """Reads the defaults passed in the initializer, implicitly converting
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
        values to strings like the rest of the API.

        Does not perform interpolation for backwards compatibility.
        """
        try:
            hold_interpolation = self._interpolation
            self._interpolation = Interpolation()
            self.read_dict({self.default_section: defaults})
        finally:
            self._interpolation = hold_interpolation
1222

1223

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
class SafeConfigParser(ConfigParser):
    """ConfigParser alias for backwards compatibility purposes."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        warnings.warn(
            "The SafeConfigParser class has been renamed to ConfigParser "
            "in Python 3.2. This alias will be removed in future versions."
            " Use ConfigParser directly instead.",
            DeprecationWarning, stacklevel=2
        )


1237 1238 1239
class SectionProxy(MutableMapping):
    """A proxy for a single section from a parser."""

1240 1241
    def __init__(self, parser, name):
        """Creates a view on a section of the specified `name` in `parser`."""
1242
        self._parser = parser
1243
        self._name = name
1244 1245 1246 1247
        for conv in parser.converters:
            key = 'get' + conv
            getter = functools.partial(self.get, _impl=getattr(parser, key))
            setattr(self, key, getter)
1248 1249

    def __repr__(self):
1250
        return '<Section: {}>'.format(self._name)
1251 1252

    def __getitem__(self, key):
1253
        if not self._parser.has_option(self._name, key):
1254
            raise KeyError(key)
1255
        return self._parser.get(self._name, key)
1256 1257

    def __setitem__(self, key, value):
1258
        self._parser._validate_value_types(option=key, value=value)
1259
        return self._parser.set(self._name, key, value)
1260 1261

    def __delitem__(self, key):
1262 1263
        if not (self._parser.has_option(self._name, key) and
                self._parser.remove_option(self._name, key)):
1264 1265 1266
            raise KeyError(key)

    def __contains__(self, key):
1267
        return self._parser.has_option(self._name, key)
1268 1269

    def __len__(self):
1270
        return len(self._options())
1271 1272

    def __iter__(self):
1273 1274 1275 1276 1277 1278 1279
        return self._options().__iter__()

    def _options(self):
        if self._name != self._parser.default_section:
            return self._parser.options(self._name)
        else:
            return self._parser.defaults()
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289

    @property
    def parser(self):
        # The parser object of the proxy is read-only.
        return self._parser

    @property
    def name(self):
        # The name of the section on a proxy is read-only.
        return self._name
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363

    def get(self, option, fallback=None, *, raw=False, vars=None,
            _impl=None, **kwargs):
        """Get an option value.

        Unless `fallback` is provided, `None` will be returned if the option
        is not found.

        """
        # If `_impl` is provided, it should be a getter method on the parser
        # object that provides the desired type conversion.
        if not _impl:
            _impl = self._parser.get
        return _impl(self._name, option, raw=raw, vars=vars,
                     fallback=fallback, **kwargs)


class ConverterMapping(MutableMapping):
    """Enables reuse of get*() methods between the parser and section proxies.

    If a parser class implements a getter directly, the value for the given
    key will be ``None``. The presence of the converter name here enables
    section proxies to find and use the implementation on the parser class.
    """

    GETTERCRE = re.compile(r"^get(?P<name>.+)$")

    def __init__(self, parser):
        self._parser = parser
        self._data = {}
        for getter in dir(self._parser):
            m = self.GETTERCRE.match(getter)
            if not m or not callable(getattr(self._parser, getter)):
                continue
            self._data[m.group('name')] = None   # See class docstring.

    def __getitem__(self, key):
        return self._data[key]

    def __setitem__(self, key, value):
        try:
            k = 'get' + key
        except TypeError:
            raise ValueError('Incompatible key: {} (type: {})'
                             ''.format(key, type(key)))
        if k == 'get':
            raise ValueError('Incompatible key: cannot use "" as a name')
        self._data[key] = value
        func = functools.partial(self._parser._get_conv, conv=value)
        func.converter = value
        setattr(self._parser, k, func)
        for proxy in self._parser.values():
            getter = functools.partial(proxy.get, _impl=func)
            setattr(proxy, k, getter)

    def __delitem__(self, key):
        try:
            k = 'get' + (key or None)
        except TypeError:
            raise KeyError(key)
        del self._data[key]
        for inst in itertools.chain((self._parser,), self._parser.values()):
            try:
                delattr(inst, k)
            except AttributeError:
                # don't raise since the entry was present in _data, silently
                # clean up
                continue

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)