| """Configuration file parser. | 
 |  | 
 | A configuration file consists of sections, lead by a "[section]" header, | 
 | and followed by "name: value" entries, with continuations and such in | 
 | the style of RFC 822. | 
 |  | 
 | Intrinsic defaults can be specified by passing them into the | 
 | ConfigParser constructor as a dictionary. | 
 |  | 
 | class: | 
 |  | 
 | ConfigParser -- responsible for parsing a list of | 
 |                     configuration files, and managing the parsed database. | 
 |  | 
 |     methods: | 
 |  | 
 |     __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, | 
 |              delimiters=('=', ':'), comment_prefixes=('#', ';'), | 
 |              inline_comment_prefixes=None, strict=True, | 
 |              empty_lines_in_values=True): | 
 |         Create the parser. When `defaults' is given, it is initialized into the | 
 |         dictionary or intrinsic defaults. The keys must be strings, the values | 
 |         must be appropriate for %()s string interpolation. | 
 |  | 
 |         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 | 
 |         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. | 
 |  | 
 |         When `strict` is True, the parser won't allow for any section or option | 
 |         duplicates while reading from a single source (file, string or | 
 |         dictionary). Default is True. | 
 |  | 
 |         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. | 
 |  | 
 |     sections() | 
 |         Return all the configuration section names, sans DEFAULT. | 
 |  | 
 |     has_section(section) | 
 |         Return whether the given section exists. | 
 |  | 
 |     has_option(section, option) | 
 |         Return whether the given option exists in the given section. | 
 |  | 
 |     options(section) | 
 |         Return list of configuration options for the named section. | 
 |  | 
 |     read(filenames, encoding=None) | 
 |         Read and parse the list of named configuration files, given by | 
 |         name.  A single filename is also allowed.  Non-existing files | 
 |         are ignored.  Return list of successfully read files. | 
 |  | 
 |     read_file(f, filename=None) | 
 |         Read and parse one configuration file, given as a file object. | 
 |         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 | 
 |         and their keys will be added in order. Values are automatically | 
 |         converted to strings. | 
 |  | 
 |     get(section, option, raw=False, vars=None, fallback=_UNSET) | 
 |         Return a string value for the named option.  All % interpolations are | 
 |         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 | 
 |         contents override any pre-existing defaults. If `option' is a key in | 
 |         `vars', the value from `vars' is used. | 
 |  | 
 |     getint(section, options, raw=False, vars=None, fallback=_UNSET) | 
 |         Like get(), but convert value to an integer. | 
 |  | 
 |     getfloat(section, options, raw=False, vars=None, fallback=_UNSET) | 
 |         Like get(), but convert value to a float. | 
 |  | 
 |     getboolean(section, options, raw=False, vars=None, fallback=_UNSET) | 
 |         Like get(), but convert value to a boolean (currently case | 
 |         insensitively defined as 0, false, no, off for False, and 1, true, | 
 |         yes, on for True).  Returns False or True. | 
 |  | 
 |     items(section=_UNSET, raw=False, vars=None) | 
 |         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. | 
 |  | 
 |     remove_section(section) | 
 |         Remove the given file section and all its options. | 
 |  | 
 |     remove_option(section, option) | 
 |         Remove the given option from the given section. | 
 |  | 
 |     set(section, option, value) | 
 |         Set the given option. | 
 |  | 
 |     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. | 
 | """ | 
 |  | 
 | from collections.abc import MutableMapping | 
 | from collections import OrderedDict as _default_dict, ChainMap as _ChainMap | 
 | import functools | 
 | import io | 
 | import itertools | 
 | import re | 
 | import sys | 
 | import warnings | 
 |  | 
 | __all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", | 
 |            "NoOptionError", "InterpolationError", "InterpolationDepthError", | 
 |            "InterpolationSyntaxError", "ParsingError", | 
 |            "MissingSectionHeaderError", | 
 |            "ConfigParser", "SafeConfigParser", "RawConfigParser", | 
 |            "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] | 
 |  | 
 | DEFAULTSECT = "DEFAULT" | 
 |  | 
 | MAX_INTERPOLATION_DEPTH = 10 | 
 |  | 
 |  | 
 |  | 
 | # exception classes | 
 | class Error(Exception): | 
 |     """Base class for ConfigParser exceptions.""" | 
 |  | 
 |     def __init__(self, msg=''): | 
 |         self.message = msg | 
 |         Exception.__init__(self, msg) | 
 |  | 
 |     def __repr__(self): | 
 |         return self.message | 
 |  | 
 |     __str__ = __repr__ | 
 |  | 
 |  | 
 | class NoSectionError(Error): | 
 |     """Raised when no section matches a requested option.""" | 
 |  | 
 |     def __init__(self, section): | 
 |         Error.__init__(self, 'No section: %r' % (section,)) | 
 |         self.section = section | 
 |         self.args = (section, ) | 
 |  | 
 |  | 
 | class DuplicateSectionError(Error): | 
 |     """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: | 
 |             message = ["While reading from ", repr(source)] | 
 |             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)) | 
 |         self.section = section | 
 |         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: | 
 |             message = ["While reading from ", repr(source)] | 
 |             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) | 
 |  | 
 |  | 
 | class NoOptionError(Error): | 
 |     """A requested option was not found.""" | 
 |  | 
 |     def __init__(self, option, section): | 
 |         Error.__init__(self, "No option %r in section: %r" % | 
 |                        (option, section)) | 
 |         self.option = option | 
 |         self.section = section | 
 |         self.args = (option, section) | 
 |  | 
 |  | 
 | class InterpolationError(Error): | 
 |     """Base class for interpolation-related exceptions.""" | 
 |  | 
 |     def __init__(self, option, section, msg): | 
 |         Error.__init__(self, msg) | 
 |         self.option = option | 
 |         self.section = section | 
 |         self.args = (option, section, msg) | 
 |  | 
 |  | 
 | class InterpolationMissingOptionError(InterpolationError): | 
 |     """A string substitution required a setting which was not available.""" | 
 |  | 
 |     def __init__(self, option, section, rawval, reference): | 
 |         msg = ("Bad value substitution:\n" | 
 |                "\tsection: [%s]\n" | 
 |                "\toption : %s\n" | 
 |                "\tkey    : %s\n" | 
 |                "\trawval : %s\n" | 
 |                % (section, option, reference, rawval)) | 
 |         InterpolationError.__init__(self, option, section, msg) | 
 |         self.reference = reference | 
 |         self.args = (option, section, rawval, reference) | 
 |  | 
 |  | 
 | class InterpolationSyntaxError(InterpolationError): | 
 |     """Raised when the source text contains invalid syntax. | 
 |  | 
 |     Current implementation raises this exception when the source text into | 
 |     which substitutions are made does not conform to the required syntax. | 
 |     """ | 
 |  | 
 |  | 
 | class InterpolationDepthError(InterpolationError): | 
 |     """Raised when substitutions are nested too deeply.""" | 
 |  | 
 |     def __init__(self, option, section, rawval): | 
 |         msg = ("Value interpolation too deeply recursive:\n" | 
 |                "\tsection: [%s]\n" | 
 |                "\toption : %s\n" | 
 |                "\trawval : %s\n" | 
 |                % (section, option, rawval)) | 
 |         InterpolationError.__init__(self, option, section, msg) | 
 |         self.args = (option, section, rawval) | 
 |  | 
 |  | 
 | class ParsingError(Error): | 
 |     """Raised when a configuration file does not follow legal syntax.""" | 
 |  | 
 |     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 | 
 |         Error.__init__(self, 'Source contains parsing errors: %r' % source) | 
 |         self.source = source | 
 |         self.errors = [] | 
 |         self.args = (source, ) | 
 |  | 
 |     @property | 
 |     def filename(self): | 
 |         """Deprecated, use `source'.""" | 
 |         warnings.warn( | 
 |             "The 'filename' attribute will be removed in future versions.  " | 
 |             "Use 'source' instead.", | 
 |             DeprecationWarning, stacklevel=2 | 
 |         ) | 
 |         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.", | 
 |             DeprecationWarning, stacklevel=2 | 
 |         ) | 
 |         self.source = value | 
 |  | 
 |     def append(self, lineno, line): | 
 |         self.errors.append((lineno, line)) | 
 |         self.message += '\n\t[line %2d]: %s' % (lineno, line) | 
 |  | 
 |  | 
 | class MissingSectionHeaderError(ParsingError): | 
 |     """Raised when a key-value pair is found before any section header.""" | 
 |  | 
 |     def __init__(self, filename, lineno, line): | 
 |         Error.__init__( | 
 |             self, | 
 |             'File contains no section headers.\nfile: %r, line: %d\n%r' % | 
 |             (filename, lineno, line)) | 
 |         self.source = filename | 
 |         self.lineno = lineno | 
 |         self.line = line | 
 |         self.args = (filename, lineno, line) | 
 |  | 
 |  | 
 | # 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() | 
 |  | 
 |  | 
 | 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): | 
 |     """Interpolation as implemented in the classic ConfigParser. | 
 |  | 
 |     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 | 
 |     a configuration file, she can escape it by writing %%. Other % usage | 
 |     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): | 
 |         if depth > MAX_INTERPOLATION_DEPTH: | 
 |             raise InterpolationDepthError(option, section, rest) | 
 |         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( | 
 |                         option, section, rest, var) | 
 |                 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 " | 
 |                              "position %d" % (value, tmp_value.find('$'))) | 
 |         return value | 
 |  | 
 |     def _interpolate_some(self, parser, option, accum, rest, section, map, | 
 |                           depth): | 
 |         if depth > MAX_INTERPOLATION_DEPTH: | 
 |             raise InterpolationDepthError(option, section, rest) | 
 |         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) | 
 |                 path = m.group(1).split(':') | 
 |                 rest = rest[m.end():] | 
 |                 sect = section | 
 |                 opt = option | 
 |                 try: | 
 |                     if len(path) == 1: | 
 |                         opt = parser.optionxform(path[0]) | 
 |                         v = map[opt] | 
 |                     elif len(path) == 2: | 
 |                         sect = path[0] | 
 |                         opt = parser.optionxform(path[1]) | 
 |                         v = parser.get(sect, opt, raw=True) | 
 |                     else: | 
 |                         raise InterpolationSyntaxError( | 
 |                             option, section, | 
 |                             "More than one ':' found: %r" % (rest,)) | 
 |                 except (KeyError, NoSectionError, NoOptionError): | 
 |                     raise InterpolationMissingOptionError( | 
 |                         option, section, rest, ":".join(path)) | 
 |                 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,)) | 
 |  | 
 |  | 
 | class LegacyInterpolation(Interpolation): | 
 |     """Deprecated interpolation used in old versions of ConfigParser. | 
 |     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( | 
 |                         option, section, rawval, e.args[0]) | 
 |             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) | 
 |  | 
 |  | 
 | class RawConfigParser(MutableMapping): | 
 |     """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 | 
 |         """ | 
 |     # Interpolation algorithm to be used if the user does not specify another | 
 |     _DEFAULT_INTERPOLATION = Interpolation() | 
 |     # 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") | 
 |     # Possible boolean values in the configuration. | 
 |     BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, | 
 |                       '0': False, 'no': False, 'false': False, 'off': False} | 
 |  | 
 |     def __init__(self, defaults=None, dict_type=_default_dict, | 
 |                  allow_no_value=False, *, delimiters=('=', ':'), | 
 |                  comment_prefixes=('#', ';'), inline_comment_prefixes=None, | 
 |                  strict=True, empty_lines_in_values=True, | 
 |                  default_section=DEFAULTSECT, | 
 |                  interpolation=_UNSET): | 
 |  | 
 |         self._dict = dict_type | 
 |         self._sections = self._dict() | 
 |         self._defaults = self._dict() | 
 |         self._proxies = self._dict() | 
 |         self._proxies[default_section] = SectionProxy(self, default_section) | 
 |         if defaults: | 
 |             for key, value in defaults.items(): | 
 |                 self._defaults[self.optionxform(key)] = value | 
 |         self._delimiters = tuple(delimiters) | 
 |         if delimiters == ('=', ':'): | 
 |             self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE | 
 |         else: | 
 |             d = "|".join(re.escape(d) for d in delimiters) | 
 |             if allow_no_value: | 
 |                 self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), | 
 |                                           re.VERBOSE) | 
 |             else: | 
 |                 self._optcre = re.compile(self._OPT_TMPL.format(delim=d), | 
 |                                           re.VERBOSE) | 
 |         self._comment_prefixes = tuple(comment_prefixes or ()) | 
 |         self._inline_comment_prefixes = tuple(inline_comment_prefixes or ()) | 
 |         self._strict = strict | 
 |         self._allow_no_value = allow_no_value | 
 |         self._empty_lines_in_values = empty_lines_in_values | 
 |         self.default_section=default_section | 
 |         self._interpolation = interpolation | 
 |         if self._interpolation is _UNSET: | 
 |             self._interpolation = self._DEFAULT_INTERPOLATION | 
 |         if self._interpolation is None: | 
 |             self._interpolation = Interpolation() | 
 |  | 
 |     def defaults(self): | 
 |         return self._defaults | 
 |  | 
 |     def sections(self): | 
 |         """Return a list of section names, excluding [DEFAULT]""" | 
 |         # self._sections will never have [DEFAULT] in it | 
 |         return list(self._sections.keys()) | 
 |  | 
 |     def add_section(self, section): | 
 |         """Create a new section in the configuration. | 
 |  | 
 |         Raise DuplicateSectionError if a section by the specified name | 
 |         already exists. Raise ValueError if name is DEFAULT. | 
 |         """ | 
 |         if section == self.default_section: | 
 |             raise ValueError('Invalid section name: %r' % section) | 
 |  | 
 |         if section in self._sections: | 
 |             raise DuplicateSectionError(section) | 
 |         self._sections[section] = self._dict() | 
 |         self._proxies[section] = SectionProxy(self, section) | 
 |  | 
 |     def has_section(self, section): | 
 |         """Indicate whether the named section is present in the configuration. | 
 |  | 
 |         The DEFAULT section is not acknowledged. | 
 |         """ | 
 |         return section in self._sections | 
 |  | 
 |     def options(self, section): | 
 |         """Return a list of option names for the given section name.""" | 
 |         try: | 
 |             opts = self._sections[section].copy() | 
 |         except KeyError: | 
 |             raise NoSectionError(section) | 
 |         opts.update(self._defaults) | 
 |         return list(opts.keys()) | 
 |  | 
 |     def read(self, filenames, encoding=None): | 
 |         """Read and parse a filename or a list of filenames. | 
 |  | 
 |         Files that cannot be opened are silently ignored; this is | 
 |         designed so that you can specify a list of potential | 
 |         configuration file locations (e.g. current directory, user's | 
 |         home directory, systemwide directory), and all existing | 
 |         configuration files in the list will be read.  A single | 
 |         filename may also be given. | 
 |  | 
 |         Return list of successfully read files. | 
 |         """ | 
 |         if isinstance(filenames, str): | 
 |             filenames = [filenames] | 
 |         read_ok = [] | 
 |         for filename in filenames: | 
 |             try: | 
 |                 with open(filename, encoding=encoding) as fp: | 
 |                     self._read(fp, filename) | 
 |             except OSError: | 
 |                 continue | 
 |             read_ok.append(filename) | 
 |         return read_ok | 
 |  | 
 |     def read_file(self, f, source=None): | 
 |         """Like read() but the argument must be a file-like object. | 
 |  | 
 |         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. | 
 |         """ | 
 |         if source is None: | 
 |             try: | 
 |                 source = f.name | 
 |             except AttributeError: | 
 |                 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. | 
 |  | 
 |         All types held in the dictionary are converted to strings during | 
 |         reading, including section names, option names and keys. | 
 |  | 
 |         Optional second argument is the `source' specifying the name of the | 
 |         dictionary being read. | 
 |         """ | 
 |         elements_added = set() | 
 |         for section, keys in dictionary.items(): | 
 |             section = str(section) | 
 |             try: | 
 |                 self.add_section(section) | 
 |             except (DuplicateSectionError, ValueError): | 
 |                 if self._strict and section in elements_added: | 
 |                     raise | 
 |             elements_added.add(section) | 
 |             for key, value in keys.items(): | 
 |                 key = self.optionxform(str(key)) | 
 |                 if value is not None: | 
 |                     value = str(value) | 
 |                 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.", | 
 |             DeprecationWarning, stacklevel=2 | 
 |         ) | 
 |         self.read_file(fp, source=filename) | 
 |  | 
 |     def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET): | 
 |         """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. | 
 |         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. | 
 |  | 
 |         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. | 
 |         """ | 
 |         try: | 
 |             d = self._unify_values(section, vars) | 
 |         except NoSectionError: | 
 |             if fallback is _UNSET: | 
 |                 raise | 
 |             else: | 
 |                 return fallback | 
 |         option = self.optionxform(option) | 
 |         try: | 
 |             value = d[option] | 
 |         except KeyError: | 
 |             if fallback is _UNSET: | 
 |                 raise NoOptionError(option, section) | 
 |             else: | 
 |                 return fallback | 
 |  | 
 |         if raw or value is None: | 
 |             return value | 
 |         else: | 
 |             return self._interpolation.before_get(self, section, option, value, | 
 |                                                   d) | 
 |  | 
 |     def _get(self, section, conv, option, **kwargs): | 
 |         return conv(self.get(section, option, **kwargs)) | 
 |  | 
 |     def getint(self, section, option, *, raw=False, vars=None, | 
 |                fallback=_UNSET): | 
 |         try: | 
 |             return self._get(section, int, option, raw=raw, vars=vars) | 
 |         except (NoSectionError, NoOptionError): | 
 |             if fallback is _UNSET: | 
 |                 raise | 
 |             else: | 
 |                 return fallback | 
 |  | 
 |     def getfloat(self, section, option, *, raw=False, vars=None, | 
 |                  fallback=_UNSET): | 
 |         try: | 
 |             return self._get(section, float, option, raw=raw, vars=vars) | 
 |         except (NoSectionError, NoOptionError): | 
 |             if fallback is _UNSET: | 
 |                 raise | 
 |             else: | 
 |                 return fallback | 
 |  | 
 |     def getboolean(self, section, option, *, raw=False, vars=None, | 
 |                    fallback=_UNSET): | 
 |         try: | 
 |             return self._get(section, self._convert_to_boolean, option, | 
 |                              raw=raw, vars=vars) | 
 |         except (NoSectionError, NoOptionError): | 
 |             if fallback is _UNSET: | 
 |                 raise | 
 |             else: | 
 |                 return fallback | 
 |  | 
 |     def items(self, section=_UNSET, raw=False, vars=None): | 
 |         """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. | 
 |         """ | 
 |         if section is _UNSET: | 
 |             return super().items() | 
 |         d = self._defaults.copy() | 
 |         try: | 
 |             d.update(self._sections[section]) | 
 |         except KeyError: | 
 |             if section != self.default_section: | 
 |                 raise NoSectionError(section) | 
 |         # Update with the entry specific variables | 
 |         if vars: | 
 |             for key, value in vars.items(): | 
 |                 d[self.optionxform(key)] = value | 
 |         value_getter = lambda option: self._interpolation.before_get(self, | 
 |             section, option, d[option], d) | 
 |         if raw: | 
 |             value_getter = lambda option: d[option] | 
 |         return [(option, value_getter(option)) for option in d.keys()] | 
 |  | 
 |     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 | 
 |  | 
 |     def optionxform(self, optionstr): | 
 |         return optionstr.lower() | 
 |  | 
 |     def has_option(self, section, option): | 
 |         """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.""" | 
 |         if not section or section == self.default_section: | 
 |             option = self.optionxform(option) | 
 |             return option in self._defaults | 
 |         elif section not in self._sections: | 
 |             return False | 
 |         else: | 
 |             option = self.optionxform(option) | 
 |             return (option in self._sections[section] | 
 |                     or option in self._defaults) | 
 |  | 
 |     def set(self, section, option, value=None): | 
 |         """Set an option.""" | 
 |         if value: | 
 |             value = self._interpolation.before_set(self, section, option, | 
 |                                                    value) | 
 |         if not section or section == self.default_section: | 
 |             sectdict = self._defaults | 
 |         else: | 
 |             try: | 
 |                 sectdict = self._sections[section] | 
 |             except KeyError: | 
 |                 raise NoSectionError(section) | 
 |         sectdict[self.optionxform(option)] = value | 
 |  | 
 |     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] | 
 |         if self._defaults: | 
 |             self._write_section(fp, self.default_section, | 
 |                                     self._defaults.items(), d) | 
 |         for section in self._sections: | 
 |             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: | 
 |             value = self._interpolation.before_write(self, section_name, key, | 
 |                                                      value) | 
 |             if value is not None or not self._allow_no_value: | 
 |                 value = delimiter + str(value).replace('\n', '\n\t') | 
 |             else: | 
 |                 value = "" | 
 |             fp.write("{}{}\n".format(key, value)) | 
 |         fp.write("\n") | 
 |  | 
 |     def remove_option(self, section, option): | 
 |         """Remove an option.""" | 
 |         if not section or section == self.default_section: | 
 |             sectdict = self._defaults | 
 |         else: | 
 |             try: | 
 |                 sectdict = self._sections[section] | 
 |             except KeyError: | 
 |                 raise NoSectionError(section) | 
 |         option = self.optionxform(option) | 
 |         existed = option in sectdict | 
 |         if existed: | 
 |             del sectdict[option] | 
 |         return existed | 
 |  | 
 |     def remove_section(self, section): | 
 |         """Remove a file section.""" | 
 |         existed = section in self._sections | 
 |         if existed: | 
 |             del self._sections[section] | 
 |             del self._proxies[section] | 
 |         return existed | 
 |  | 
 |     def __getitem__(self, key): | 
 |         if key != self.default_section and not self.has_section(key): | 
 |             raise KeyError(key) | 
 |         return self._proxies[key] | 
 |  | 
 |     def __setitem__(self, key, value): | 
 |         # To conform with the mapping protocol, overwrites existing values in | 
 |         # the section. | 
 |  | 
 |         # XXX this is not atomic if read_dict fails at any point. Then again, | 
 |         # no update method in configparser is atomic in this implementation. | 
 |         if key == self.default_section: | 
 |             self._defaults.clear() | 
 |         elif key in self._sections: | 
 |             self._sections[key].clear() | 
 |         self.read_dict({key: value}) | 
 |  | 
 |     def __delitem__(self, key): | 
 |         if key == self.default_section: | 
 |             raise ValueError("Cannot remove the default section.") | 
 |         if not self.has_section(key): | 
 |             raise KeyError(key) | 
 |         self.remove_section(key) | 
 |  | 
 |     def __contains__(self, key): | 
 |         return key == self.default_section or self.has_section(key) | 
 |  | 
 |     def __len__(self): | 
 |         return len(self._sections) + 1 # the default section | 
 |  | 
 |     def __iter__(self): | 
 |         # XXX does it break when underlying container state changed? | 
 |         return itertools.chain((self.default_section,), self._sections.keys()) | 
 |  | 
 |     def _read(self, fp, fpname): | 
 |         """Parse a sectioned configuration file. | 
 |  | 
 |         Each section in a configuration file contains a header, indicated by | 
 |         a name in square brackets (`[]'), plus key/value options, indicated by | 
 |         `name' and `value' delimited with a specific substring (`=' or `:' by | 
 |         default). | 
 |  | 
 |         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. | 
 |  | 
 |         Configuration files may include comments, prefixed by specific | 
 |         characters (`#' and `;' by default). Comments may appear on their own | 
 |         in an otherwise empty line or may be entered in lines holding values or | 
 |         section names. | 
 |         """ | 
 |         elements_added = set() | 
 |         cursect = None                        # None, or a dictionary | 
 |         sectname = None | 
 |         optname = None | 
 |         lineno = 0 | 
 |         indent_level = 0 | 
 |         e = None                              # None, or an exception | 
 |         for lineno, line in enumerate(fp, start=1): | 
 |             comment_start = sys.maxsize | 
 |             # strip inline comments | 
 |             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 | 
 |             # strip full line comments | 
 |             for prefix in self._comment_prefixes: | 
 |                 if line.strip().startswith(prefix): | 
 |                     comment_start = 0 | 
 |                     break | 
 |             if comment_start == sys.maxsize: | 
 |                 comment_start = None | 
 |             value = line[:comment_start].strip() | 
 |             if not value: | 
 |                 if self._empty_lines_in_values: | 
 |                     # add empty line to the value, but only if there was no | 
 |                     # comment on the line | 
 |                     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 | 
 |                 else: | 
 |                     # empty line marks end of value | 
 |                     indent_level = sys.maxsize | 
 |                 continue | 
 |             # continuation line? | 
 |             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) | 
 |             # a section header or option header? | 
 |             else: | 
 |                 indent_level = cur_indent_level | 
 |                 # is it a section header? | 
 |                 mo = self.SECTCRE.match(value) | 
 |                 if mo: | 
 |                     sectname = mo.group('header') | 
 |                     if sectname in self._sections: | 
 |                         if self._strict and sectname in elements_added: | 
 |                             raise DuplicateSectionError(sectname, fpname, | 
 |                                                         lineno) | 
 |                         cursect = self._sections[sectname] | 
 |                         elements_added.add(sectname) | 
 |                     elif sectname == self.default_section: | 
 |                         cursect = self._defaults | 
 |                     else: | 
 |                         cursect = self._dict() | 
 |                         self._sections[sectname] = cursect | 
 |                         self._proxies[sectname] = SectionProxy(self, sectname) | 
 |                         elements_added.add(sectname) | 
 |                     # So sections can't start with a continuation line | 
 |                     optname = None | 
 |                 # no section header in the file? | 
 |                 elif cursect is None: | 
 |                     raise MissingSectionHeaderError(fpname, lineno, line) | 
 |                 # an option line? | 
 |                 else: | 
 |                     mo = self._optcre.match(value) | 
 |                     if mo: | 
 |                         optname, vi, optval = mo.group('option', 'vi', 'value') | 
 |                         if not optname: | 
 |                             e = self._handle_error(e, fpname, lineno, line) | 
 |                         optname = self.optionxform(optname.rstrip()) | 
 |                         if (self._strict and | 
 |                             (sectname, optname) in elements_added): | 
 |                             raise DuplicateOptionError(sectname, optname, | 
 |                                                        fpname, lineno) | 
 |                         elements_added.add((sectname, optname)) | 
 |                         # This check is fine because the OPTCRE cannot | 
 |                         # match if it would set optval to None | 
 |                         if optval is not None: | 
 |                             optval = optval.strip() | 
 |                             cursect[optname] = [optval] | 
 |                         else: | 
 |                             # valueless option handling | 
 |                             cursect[optname] = None | 
 |                     else: | 
 |                         # a non-fatal parsing error occurred. set up the | 
 |                         # exception but keep going. the exception will be | 
 |                         # raised at the end of the file and will contain a | 
 |                         # list of all bogus lines | 
 |                         e = self._handle_error(e, fpname, lineno, line) | 
 |         # if any parsing errors occurred, raise an exception | 
 |         if e: | 
 |             raise e | 
 |         self._join_multiline_values() | 
 |  | 
 |     def _join_multiline_values(self): | 
 |         defaults = self.default_section, self._defaults | 
 |         all_sections = itertools.chain((defaults,), | 
 |                                        self._sections.items()) | 
 |         for section, options in all_sections: | 
 |             for name, val in options.items(): | 
 |                 if isinstance(val, list): | 
 |                     val = '\n'.join(val).rstrip() | 
 |                 options[name] = self._interpolation.before_read(self, | 
 |                                                                 section, | 
 |                                                                 name, val) | 
 |  | 
 |     def _handle_error(self, exc, fpname, lineno, line): | 
 |         if not exc: | 
 |             exc = ParsingError(fpname) | 
 |         exc.append(lineno, repr(line)) | 
 |         return exc | 
 |  | 
 |     def _unify_values(self, section, vars): | 
 |         """Create a sequence of lookups with 'vars' taking priority over | 
 |         the 'section' which takes priority over the DEFAULTSECT. | 
 |  | 
 |         """ | 
 |         sectiondict = {} | 
 |         try: | 
 |             sectiondict = self._sections[section] | 
 |         except KeyError: | 
 |             if section != self.default_section: | 
 |                 raise NoSectionError(section) | 
 |         # Update with the entry specific variables | 
 |         vardict = {} | 
 |         if vars: | 
 |             for key, value in vars.items(): | 
 |                 if value is not None: | 
 |                     value = str(value) | 
 |                 vardict[self.optionxform(key)] = value | 
 |         return _ChainMap(vardict, sectiondict, self._defaults) | 
 |  | 
 |     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()] | 
 |  | 
 |     def _validate_value_types(self, *, section="", option="", value=""): | 
 |         """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() | 
 |         for RawConfigParsers. It is invoked in every case for mapping protocol | 
 |         access and in ConfigParser.set(). | 
 |         """ | 
 |         if not isinstance(section, str): | 
 |             raise TypeError("section names must be strings") | 
 |         if not isinstance(option, str): | 
 |             raise TypeError("option keys must be strings") | 
 |         if not self._allow_no_value or value: | 
 |             if not isinstance(value, str): | 
 |                 raise TypeError("option values must be strings") | 
 |  | 
 |  | 
 | class ConfigParser(RawConfigParser): | 
 |     """ConfigParser implementing interpolation.""" | 
 |  | 
 |     _DEFAULT_INTERPOLATION = BasicInterpolation() | 
 |  | 
 |     def set(self, section, option, value=None): | 
 |         """Set an option.  Extends RawConfigParser.set by validating type and | 
 |         interpolation syntax on the value.""" | 
 |         self._validate_value_types(option=option, value=value) | 
 |         super().set(section, option, value) | 
 |  | 
 |     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) | 
 |  | 
 |  | 
 | 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 | 
 |         ) | 
 |  | 
 |  | 
 | class SectionProxy(MutableMapping): | 
 |     """A proxy for a single section from a parser.""" | 
 |  | 
 |     def __init__(self, parser, name): | 
 |         """Creates a view on a section of the specified `name` in `parser`.""" | 
 |         self._parser = parser | 
 |         self._name = name | 
 |  | 
 |     def __repr__(self): | 
 |         return '<Section: {}>'.format(self._name) | 
 |  | 
 |     def __getitem__(self, key): | 
 |         if not self._parser.has_option(self._name, key): | 
 |             raise KeyError(key) | 
 |         return self._parser.get(self._name, key) | 
 |  | 
 |     def __setitem__(self, key, value): | 
 |         self._parser._validate_value_types(option=key, value=value) | 
 |         return self._parser.set(self._name, key, value) | 
 |  | 
 |     def __delitem__(self, key): | 
 |         if not (self._parser.has_option(self._name, key) and | 
 |                 self._parser.remove_option(self._name, key)): | 
 |             raise KeyError(key) | 
 |  | 
 |     def __contains__(self, key): | 
 |         return self._parser.has_option(self._name, key) | 
 |  | 
 |     def __len__(self): | 
 |         return len(self._options()) | 
 |  | 
 |     def __iter__(self): | 
 |         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() | 
 |  | 
 |     def get(self, option, fallback=None, *, raw=False, vars=None): | 
 |         return self._parser.get(self._name, option, raw=raw, vars=vars, | 
 |                                 fallback=fallback) | 
 |  | 
 |     def getint(self, option, fallback=None, *, raw=False, vars=None): | 
 |         return self._parser.getint(self._name, option, raw=raw, vars=vars, | 
 |                                    fallback=fallback) | 
 |  | 
 |     def getfloat(self, option, fallback=None, *, raw=False, vars=None): | 
 |         return self._parser.getfloat(self._name, option, raw=raw, vars=vars, | 
 |                                      fallback=fallback) | 
 |  | 
 |     def getboolean(self, option, fallback=None, *, raw=False, vars=None): | 
 |         return self._parser.getboolean(self._name, option, raw=raw, vars=vars, | 
 |                                        fallback=fallback) | 
 |  | 
 |     @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 |