0001"""
0002This implements the public `ConfigParser` interface, but with some
0003additional enhancements.
0004
0005
0006"""
0007
0008import codecs
0009import os
0010from UserDict import DictMixin
0011import string
0012from initools import iniparser
0013
0014class Error(Exception):
0015    pass
0016
0017class NoSectionError(Error):
0018    """Exception raised when a specified section is not found."""
0019
0020class DuplicateSectionError(Error):
0021    """Exception raised if add_section() is called with the name of a
0022    section that is already present."""
0023
0024class NoOptionError(Error):
0025    """Exception raised when a specified option is not found in the
0026    specified section."""
0027
0028class InterpolationError(Error):
0029    """Base class for exceptions raised when problems occur performing
0030    string interpolation."""
0031
0032    def __init__(self, option, section, msg):
0033        Error.__init__(self, msg)
0034        self.option = option
0035        self.section = section
0036
0037class InterpolationDepthError(InterpolationError):
0038    """Exception raised when string interpolation cannot be completed
0039    because the number of iterations exceeds
0040    MAX_INTERPOLATION_DEPTH. Subclass of InterpolationError."""
0041
0042class InterpolationMissingOptionError(InterpolationError):
0043    """Exception raised when an option referenced from a value does
0044    not exist. Subclass of InterpolationError."""
0045
0046    def __init__(self, option, section, rawval, reference, msg=None):
0047        if msg is None:
0048            msg = ("Bad value substitution:\n"
0049                   "\tsection: [%s]\n"
0050                   "\toption : %s\n"
0051                   "\tkey    : %s\n"
0052                   "\trawval : %s\n"
0053                   % (section, option, reference, rawval))
0054        InterpolationError.__init__(self, option, section, msg)
0055        self.reference = reference
0056
0057class InterpolationSyntaxError(InterpolationError):
0058    """Exception raised when the source text into which substitutions
0059    are made does not conform to the required syntax. Subclass of
0060    InterpolationError."""
0061
0062"""Exception raised when errors occur attempting to parse a file."""
0063ParsingError = iniparser.ParseError
0064
0065class MissingSectionHeaderError(ParsingError):
0066    """Exception raised when attempting to parse a file which has no
0067    section headers."""
0068
0069## The maximum depth for recursive interpolation for get() when the
0070## raw parameter is false. This is relevant only for the ConfigParser
0071## class.
0072MAX_INTERPOLATION_DEPTH = 10
0073
0074class _NoDefault: pass
0075
0076class RawConfigParser(object):
0077
0078    # If this is true then %(DEFAULT_KEY)s will be substituted
0079    # in values:
0080    percent_expand = False
0081    # If this is true then ${section:value} will be substituted
0082    # in values:
0083    dollar_expand = False
0084    # If this is true, then .set(section_that_does_not_exist, ...)
0085    # will fail; otherwise the section will be implicitly created
0086    safe_set = False
0087    # If this is true then a global section is allowed (options
0088    # defined before any section is defined)
0089    global_section = False
0090    # If this is true, then option names are case sensitive:
0091    case_sensitive = False
0092    # If this is true, then section names are case sensitive:
0093    section_case_sensitive = True
0094    # This is the encoding to expect the file to be in:
0095    encoding = 'utf8'
0096    # If true, then comments will be allowed on the same line
0097    # as a value.  Otherwise comments can only be on their
0098    # own lines
0099    inline_comments = True
0100    # When writing a config file out, this will be used to
0101    # indent continuation lines:
0102    continuation_indent = '\t'
0103    # If this is true, then every section will appear to have
0104    # the values from [DEFAULT] in it
0105    inherit_defaults = True
0106    # This can be True or a string to indicate the name of the
0107    # config option for extending a section or config file.
0108    extendable = False
0109    # If extendable is True, then this value will be used:
0110    default_extend_name = 'extends'
0111    # An extends in these sections will be applied globally;
0112    # in other sections they will only apply to the section
0113    # itself
0114    global_extend_section_names = ['', 'global', 'DEFAULT']
0115    # If a extend is in a section, it will draw from some section;
0116    # if there is no default section then it will draw from this
0117    # section.  Use '__name__' to indicate the same name as the
0118    # section itself.
0119    default_extend_section_name = 'main'
0120
0121    def __init__(self, defaults=None,
0122                 encoding=_NoDefault,
0123                 percent_expand=_NoDefault,
0124                 safe_set=_NoDefault,
0125                 dollar_expand=_NoDefault,
0126                 case_sensitive=_NoDefault,
0127                 section_case_sensitive=_NoDefault,
0128                 global_section=_NoDefault,
0129                 inline_comments=_NoDefault,
0130                 inherit_defaults=_NoDefault,
0131                 extendable=_NoDefault):
0132        if encoding is not _NoDefault:
0133            self.encoding = encoding
0134        if percent_expand is not _NoDefault:
0135            self.percent_expand = percent_expand
0136        if safe_set is not _NoDefault:
0137            self.safe_set = safe_set
0138        if dollar_expand is not _NoDefault:
0139            self.dollar_expand = dollar_expand
0140        if case_sensitive is not _NoDefault:
0141            self.case_sensitive = case_sensitive
0142        if section_case_sensitive is not _NoDefault:
0143            self.section_case_sensitive = section_case_sensitive
0144        if global_section is not _NoDefault:
0145            self.global_section = global_section
0146        if inline_comments is not _NoDefault:
0147            self.inline_comments = inline_comments
0148        if inherit_defaults is not _NoDefault:
0149            self.inherit_defaults = inherit_defaults
0150        if extendable is not _NoDefault:
0151            self.extendable = extendable
0152        if self.extendable:
0153            if isinstance(self.extendable, basestring):
0154                self._extends_name = self.extendable
0155            else:
0156                self._extends_name = self.default_extend_name
0157        self._pre_normalized_keys = {}
0158        self._pre_normalized_sections = {}
0159        self._key_file_positions = {}
0160        self._key_comments = {}
0161        self._section_order = []
0162        self._section_key_order = {}
0163        self._section_comments = {}
0164        self._values = {}
0165        self.add_section('DEFAULT')
0166        if defaults is not None:
0167            for name, value in defaults.items():
0168                self.set('DEFAULT', name, value)
0169
0170    def defaults(self):
0171        """Return a dictionary containing the instance-wide defaults."""
0172        default = self.sectionxform('DEFAULT')
0173        return self._values.get(default, {})
0174
0175    def sections(self):
0176        """Return a list of the sections available; DEFAULT is not
0177        included in the list."""
0178        return [self._pre_normalized_sections[sec]
0179                for sec in self._section_order
0180                if sec != self.sectionxform('DEFAULT')]
0181
0182    def add_section(self, section, comment=None):
0183        """Add a section named section to the instance.
0184
0185        If a section by the given name already exists,
0186        DuplicateSectionError is raised.
0187        """
0188        sec = self.sectionxform(section)
0189        if sec in self._values:
0190            if self.sectionxform('DEFAULT') == sec:
0191                # Ignore this one case of duplicates
0192                return
0193            raise DuplicateSectionError(
0194                "A section [%s] already exists"
0195                % sec)
0196        self._pre_normalized_sections[sec] = section
0197        if sec not in self._section_order:
0198            self._section_order.append(sec)
0199        self._section_key_order[sec] = []
0200        self._section_comments[sec] = comment
0201        self._values[sec] = {}
0202
0203    def has_section(self, section):
0204        """Indicates whether the named section is present in the
0205        configuration.
0206
0207        The DEFAULT section is not acknowledged.
0208        """
0209        if section == 'DEFAULT':
0210            return False
0211        return self.sectionxform(section) in self._values
0212
0213    def options(self, section):
0214        """Returns a list of options available in the specified
0215        section."""
0216        sec = self.sectionxform(section)
0217        if sec not in self._values:
0218            raise NoSectionError(
0219                "Section [%s] does not exist" % sec)
0220        v = [self._pre_normalized_keys[(sec, op)]
0221             for op in self._section_key_order[sec]]
0222        if self.inherit_defaults and self.sectionxform('DEFAULT') != sec:
0223            v = v[:]
0224            v.extend(self.options('DEFAULT'))
0225        return v
0226
0227    def has_option(self, section, option):
0228        """If the given section exists, and contains the given option,
0229        return True; otherwise return False."""
0230        sec = self.sectionxform(section)
0231        if self.inherit_defaults and self.sectionxform('DEFAULT') != sec:
0232            if self.has_option('DEFAULT', option):
0233                return True
0234        if sec not in self._values:
0235            return False
0236        return self.optionxform(option) in self._values[sec]
0237
0238    def read(self, filenames, extending=False, map_sections=None):
0239        """Attempt to read and parse a list of filenames, returning a
0240        list of filenames which were successfully parsed.
0241
0242        If filenames is a string or Unicode string, it is treated as a
0243        single filename. If a file named in filenames cannot be
0244        opened, that file will be ignored. This is designed so that
0245        you can specify a list of potential configuration file
0246        locations (for example, the current directory, the user's home
0247        directory, and some system-wide directory), and all existing
0248        configuration files in the list will be read. If none of the
0249        named files exist, the ConfigParser instance will contain an
0250        empty dataset. An application which requires initial values to
0251        be loaded from a file should load the required file or files
0252        using readfp() before calling read() for any optional files:
0253
0254          import ConfigParser, os
0255
0256          config = ConfigParser.ConfigParser()
0257          config.readfp(open('defaults.cfg'))
0258          config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')])
0259
0260        If ``extending`` is true (default false) then the values
0261        picked up from the file will *not* override the values already
0262        present (that means that the file being loaded is extended by
0263        the already loaded file).
0264        """
0265        found = []
0266        if isinstance(filenames, basestring):
0267            filenames = [filenames]
0268        for fn in filenames:
0269            if not os.path.exists(fn):
0270                continue
0271            found.append(fn)
0272            fp = open(fn)
0273            try:
0274                self.readfp(fp, fn, extending=extending,
0275                            map_sections=map_sections)
0276            finally:
0277                fp.close()
0278        return found
0279
0280    def readfp(self, fp, filename='<???>', extending=False,
0281               map_sections=None):
0282        """Read and parse configuration data from the file or
0283        file-like object in fp
0284
0285        Only the readline() method is used. If filename is omitted and
0286        fp has a name attribute, that is used for filename; the
0287        default is '<???>'.
0288        """
0289        parser = _ConfigParserParser(self, extending=extending,
0290                                     map_sections=map_sections)
0291        parser.loadfile(fp, filename=filename,
0292                        encoding=self.encoding)
0293        if self.extendable:
0294            self._process_extends()
0295
0296    def _process_extends(self):
0297        """
0298        Figure out if there's any extends options in the config
0299        file, and if so then process those options and remove them.
0300        """
0301        extend = self.optionxform(self._extends_name)
0302        glob_sections = map(
0303            self.sectionxform, self.global_extend_section_names)
0304        reads = []
0305        for section, values in self._values.iteritems():
0306            if extend in values:
0307                value = self.getfilename(section, extend)
0308                if self.sectionxform(section) in glob_sections:
0309                    reads.append((value, None))
0310                else:
0311                    if '#' in value:
0312                        value, inc_section = value.split('#', 1)
0313                    else:
0314                        inc_section = self.default_extend_section_name
0315                    if inc_section == '__name__':
0316                        inc_section = section
0317                    reads.append((value, {inc_section: section}))
0318                self.remove_option(section, extend)
0319        for filename, map_sections in reads:
0320            self.read(filename, extending=True,
0321                      map_sections=map_sections)
0322
0323    def get(self, section, option, raw=False, vars=None, _recursion=0):
0324        """Get an option value for the named section.
0325
0326        If self.percent_expand is true, then all the '%'
0327        interpolations are expanded, using the optional vars.  If raw
0328        is True, then no interpolation is done."""
0329        if _recursion > MAX_INTERPOLATION_DEPTH:
0330            raise InterpolationDepthError(
0331                section, option,
0332                "Maximum recursion depth for interpolation exceded")
0333        sec = self.sectionxform(section)
0334        op = self.optionxform(option)
0335        if sec not in self._values:
0336            raise NoSectionError(
0337                "Section [%s] not found (when looking for option %r)"
0338                % (section, option))
0339        values = self._values[sec]
0340        if op not in values:
0341            if (self.inherit_defaults
0342                and self.sectionxform('DEFAULT') != sec
0343                and self.has_option('DEFAULT', op)):
0344                value = self.get('DEFAULT', op, raw=True)
0345            else:
0346                raise NoOptionError(
0347                    "Option %r not found (in section [%s])"
0348                    % (option, section))
0349        else:
0350            value = values[op]
0351        if raw:
0352            return value
0353        if self.percent_expand:
0354            value = self._do_percent_expansion(
0355                section, option, value, vars, _recursion)
0356        if self.dollar_expand:
0357            value = self._do_dollar_expansion(
0358                section, option, value, vars, _recursion)
0359        return value
0360
0361    def _do_percent_expansion(self, section, option, value,
0362                              vars, _recursion):
0363        if vars is None:
0364            vars = {}
0365        vars = _OptionWrapper(self, vars, section, option, _recursion)
0366        if not isinstance(value, basestring):
0367            raise TypeError(
0368                "Cannot interpolate a non-string [%s] option %s=%r"
0369                % (section, option, value))
0370        try:
0371            return value % vars
0372        except KeyError, e:
0373            var = e.args[0]
0374            raise InterpolationMissingOptionError(
0375                option, section, value, var,
0376                "Variable %s not found in [%s] option %s=%s%s"
0377                % (var, section, option, value,
0378                   self._get_location_info(section, option)))
0379        except ValueError, e:
0380            raise InterpolationSyntaxError(
0381                option, section,
0382                "%s in [%s] option %s=%s%s"
0383                % (e, section, option, value,
0384                   self._get_location_info(section, option)))
0385
0386    def _do_dollar_expansion(self, section, option, value,
0387                             vars, _recursion):
0388        if vars is None:
0389            vars = {}
0390        vars = _SectionOptionWrapper(self, vars, section, option, _recursion)
0391        if not isinstance(value, basestring):
0392            raise TypeError(
0393                "Cannot interpolate a non-string [%s] option %s=%r"
0394                % (section, option, value))
0395        try:
0396            tmpl = string.Template(value)
0397            return tmpl.substitute(vars)
0398        except KeyError, e:
0399            var = e.args[0]
0400            raise InterpolationMissingOptionError(
0401                option, section, value, var,
0402                "Variable %s not found in [%s] option %s=%s%s"
0403                % (var, section, option, value,
0404                   self._get_location_info(section, option)))
0405        except ValueError, e:
0406            raise InterpolationSyntaxError(
0407                option, section,
0408                "%s in [%s] option %s=%s%s"
0409                % (e, section, option, value,
0410                   self._get_location_info(section, option)))
0411
0412    def _get_location_info(self, section, option):
0413        location = self._key_file_positions.get(
0414            (self.sectionxform(section), self.optionxform(option)),
0415            (None, None))
0416        if location[0]:
0417            extra = ' (located at %s' % location[0]
0418            if location[1]:
0419                extra += ':%s' % location[1]
0420            extra += ')'
0421        else:
0422            extra = ''
0423        return extra
0424
0425    def getint(self, section, option):
0426        """A convenience method which coerces the option in the
0427        specified section to an integer."""
0428        value = self.get(section, option)
0429        try:
0430            return int(value)
0431        except ValueError:
0432            loc_info = self._get_location_info(section, option)
0433            raise ValueError(
0434                "Could not convert option %s=%s (in [%s]) to an integer%s"
0435                % (option, value, section, loc_info))
0436
0437    def getfloat(self, section, option):
0438        """A convenience method which coerces the option in the
0439        specified section to a floating point number."""
0440        value = self.get(section, option)
0441        try:
0442            return float(value)
0443        except ValueError:
0444            raise ValueError(
0445                "Could not convert options %s=%s (in [%s]) to a float%s"
0446                % (option, value, section,
0447                   self._get_location_info(section, option)))
0448
0449    def getboolean(self, section, option):
0450        """A convenience method which coerces the option in the
0451        specified section to a Boolean value.
0452
0453        Note that the accepted values for the option are "1", "yes",
0454        "true", and "on", which cause this method to return True, and
0455        "0", "no", "false", and "off", which cause it to return
0456        False. These string values are checked in a case-insensitive
0457        manner. Any other value will cause it to raise ValueError.
0458        """
0459        value = self.get(section, option)
0460        value = value.strip().lower()
0461        if value in ('1', 'y', 'yes', 't', 'true', 'on'):
0462            return True
0463        elif value in ('0', 'n', 'no', 'f', 'false', 'off'):
0464            return False
0465        raise ValueError(
0466            "Could not convert option %s=%s (in [%s] to a boolean "
0467            "(use true/false)%s"
0468            % (option, value, section,
0469               self._get_location_info(section, option)))
0470
0471    def getfilename(self, section, option):
0472        """Returns the value of the option, interpreted as a filename
0473        relative to the location of where the option was defined.
0474
0475        Raises a ValueError if the option doesn't have an associated
0476        filename and the path is not absolute.
0477        """
0478        value = self.get(section, option)
0479        filename = self._key_file_positions.get(
0480            (self.sectionxform(section), self.optionxform(option)),
0481            (None, None))[0]
0482        if filename is None:
0483            if os.path.isabs(value):
0484                return value
0485            raise ValueError(
0486                "Getting relative filename [%s] option %s=%s , but there "
0487                "is no recorded config file for option"
0488                % (section, option, value))
0489        value = os.path.join(os.path.dirname(filename), value)
0490        return value
0491
0492    def items(self, section, raw=False, vars=None):
0493        """Return a list of (name, value) pairs for each option in the
0494        given section.
0495
0496        If self.percent_expand is true, then all the '%'
0497        interpolations are expanded, using the optional vars.  If raw
0498        is True, then no interpolation is done.
0499        """
0500        result = []
0501        for op in self.options(section):
0502            result.append((op, self.get(section, op, raw=raw, vars=vars)))
0503        return result
0504
0505    def allitems(self, raw=False, vars=None):
0506        result = {}
0507        for section in self.sections():
0508            result[section] = self.items(section, raw=raw, vars=vars)
0509        return result
0510
0511    def set(self, section, option, value, filename=None,
0512            line_number=None, comments=None):
0513        """If the given section exists, set the given option to the
0514        specified value; otherwise raise NoSectionError.
0515
0516        While it is possible to use RawConfigParser (or ConfigParser
0517        with raw parameters set to true) for internal storage of
0518        non-string values, full functionality (including interpolation
0519        and output to files) can only be achieved using string values.
0520
0521        If self.safe_set is true, then only string values will be
0522        allowed.
0523        """
0524        if not isinstance(value, basestring) and self.safe_set:
0525            raise TypeError(
0526                "You can only set options to string values "
0527                "(you tried to set %s=%r in [%s])"
0528                % (option, value, section))
0529        sec = self.sectionxform(section)
0530        if sec not in self._values:
0531            raise NoSectionError(
0532                'There is no section [%s] (when setting option %r)'
0533                % (sec, option))
0534        op = self.optionxform(option)
0535        self._pre_normalized_keys[(sec, op)] = option
0536        if op in self._section_key_order[sec]:
0537            self._section_key_order[sec].remove(op)
0538        self._section_key_order[sec].append(op)
0539        if comments is None:
0540            if (sec, op) in self._key_comments:
0541                del self._key_comments[(sec, op)]
0542        else:
0543            self._key_comments[(sec, op)] = value
0544        if filename is None:
0545            if (sec, op) in self._key_file_positions:
0546                del self._key_file_positions[(sec, op)]
0547        else:
0548            self._key_file_positions[(sec, op)] = (filename, line_number)
0549        self._values[sec][op] = value
0550
0551    def write(self, fileobject):
0552        """Write a representation of the configuration to the
0553        specified file object.
0554
0555        This representation can be parsed by a future read() call.
0556        """
0557        f = fileobject
0558        if self.encoding:
0559            # @@: output encoding, errors?
0560            f = codecs.EncodedFile(f, self.encoding)
0561        for sec in self._section_order:
0562            section = self._pre_normalized_sections[sec]
0563            comment = self._section_comments.get(sec)
0564            if comment:
0565                f.write(comment+'\n')
0566            f.write('[%s]\n' % section)
0567            for op in self._section_key_order[sec]:
0568                option = self._pre_normalized_keys[(sec, op)]
0569                comment = self._key_comments.get((sec, op))
0570                if comment:
0571                    f.write(comment+'\n')
0572                f.write('%s = ' % option)
0573                lines = self._values[sec][op].splitlines()
0574                f.write(lines[0])
0575                for line in lines[1:]:
0576                    f.write('\n%s%s' % (self.continuation_indent, line))
0577                f.write('\n')
0578                if comment:
0579                    f.write('\n')
0580            f.write('\n')
0581
0582    def remove_option(self, section, option):
0583        """Remove the specified option from the specified section.
0584
0585        If the section does not exist, raise NoSectionError. If the
0586        option existed to be removed, return True; otherwise return
0587        False.
0588        """
0589        sec = self.sectionxform(section)
0590        if sec not in self._values:
0591            raise NoSectionError(
0592                'No section [%s] (while trying to remove %r)'
0593                % (section, option))
0594        op = self.optionxform(option)
0595        if op in self._values[sec]:
0596            del self._values[sec][op]
0597            del self._pre_normalized_keys[(sec, op)]
0598            if (sec, op) in self._key_comments:
0599                del self._key_comments[(sec, op)]
0600            if (sec, op) in self._key_file_positions:
0601                del self._key_file_positions[(sec, op)]
0602            self._section_key_order[sec].remove(op)
0603            return True
0604        else:
0605            return False
0606
0607    def remove_section(self, section):
0608        """Remove the specified section from the configuration.
0609
0610        If the section in fact existed, return True. Otherwise return
0611        False.
0612        """
0613        sec = self.sectionxform(section)
0614        if sec not in self._values:
0615            return False
0616        for key in self._pre_normalized_keys:
0617            if key[0] == sec:
0618                del self._pre_normalized_keys[key]
0619        del self._pre_normalized_sections[sec]
0620        for key in self._key_file_positions:
0621            if key[0] == sec:
0622                del self._key_file_positions[key]
0623        for key in self._key_comments:
0624            if key[0] == sec:
0625                del self._key_comments[key]
0626        self._section_order.remove(sec)
0627        del self._section_key_order[sec]
0628        if sec in self._section_comments:
0629            del self._section_comments[sec]
0630        del self._values[sec]
0631
0632    def optionxform(self, option):
0633        """Transforms the option name option as found in an input file
0634        or as passed in by client code to the form that should be used
0635        in the internal structures.
0636
0637        The default implementation returns a lower-case version of
0638        option; subclasses may override this or client code can set an
0639        attribute of this name on instances to affect this
0640        behavior. Setting this to str(), for example, would make
0641        option names case sensitive.
0642        """
0643        if not self.case_sensitive:
0644            return option.lower()
0645        else:
0646            return option
0647
0648    def sectionxform(self, option):
0649        """Transforms the section name option as found in an input file
0650        or as passed in by client code to the form that should be used
0651        in the internal structures.
0652
0653        The default implementation returns a lower-case version of
0654        option; subclasses may override this or client code can set an
0655        attribute of this name on instances to affect this
0656        behavior. Setting this to str(), for example, would make
0657        option names case sensitive.
0658        """
0659        if not self.section_case_sensitive:
0660            return option.lower()
0661        else:
0662            return option
0663
0664    def asdict(self):
0665        return self._values.copy()
0666
0667    def sectiondict(self, section):
0668        return self._values.get(self.sectionxform(section), {}).copy()
0669
0670class ConfigParser(RawConfigParser):
0671
0672    percent_expand = True
0673
0674class SafeConfigParser(ConfigParser):
0675
0676    safe_set = True
0677
0678class _ConfigParserParser(iniparser.INIParser):
0679    """
0680    This parser feeds the parsed values into a ConfigParser object
0681    """
0682
0683    def __init__(self, cp, extending, map_sections=None):
0684        self.cp = cp
0685        self.extending = extending
0686        self.map_sections = map_sections
0687        self.last_comment = None
0688        self.section = None
0689
0690    def assignment(self, name, content):
0691        if self.section is None and not self.cp.global_section:
0692            self.parse_error("Missing Section",
0693                             exception=MissingSectionHeaderError)
0694        content = content.strip(' \t')
0695        if not name:
0696            self.parse_error('No name given for option')
0697        if self.cp.inline_comments:
0698            lines = content.splitlines()
0699            result = []
0700            for line in lines:
0701                semi_pos = line.find(';')
0702                hash_pos = line.find('#')
0703                if semi_pos == -1 and hash_pos == -1:
0704                    comment = None
0705                elif semi_pos == -1:
0706                    line, comment = line.split('#', 1)
0707                elif hash_pos == -1:
0708                    line, comment = line.split(';', 1)
0709                elif hash_pos < semi_pos:
0710                    line, comment = line.split('#', 1)
0711                else:
0712                    line, comment = line.split(';', 1)
0713                if comment is not None:
0714                    line = line.rstrip()
0715                    comment = comment.lstrip()
0716                    self.add_comment(comment)
0717                result.append(line)
0718            content = '\n'.join(result)
0719        if self.extending:
0720            if self.cp.has_option(self.section, name):
0721                # Don't write over options
0722                return
0723        if self.map_sections is not None:
0724            if self.section in self.map_sections:
0725                section = self.map_sections[self.section]
0726            else:
0727                return
0728        else:
0729            section = self.section
0730        self.cp.set(
0731            section, name, content,
0732            filename=self.filename,
0733            line_number=self.lineno,
0734            comments=self.last_comment)
0735        self.last_comment = None
0736
0737    def new_section(self, section):
0738        if not self.cp.has_section(section):
0739            self.cp.add_section(section, comment=self.last_comment)
0740            self.last_comment = None
0741        if not section:
0742            self.parse_error('Empty section name ([])')
0743        self.section = section
0744
0745    def add_comment(self, comment):
0746        if self.last_comment is None:
0747            self.last_comment = comment
0748        else:
0749            self.last_comment = self.last_comment + '\n' + comment
0750
0751
0752class _OptionWrapper(DictMixin):
0753
0754    """
0755    This produces the dictionary used for percent substitution in
0756    values.
0757
0758    Substitution is recursive.
0759    """
0760
0761    def __init__(self, parser, extra_vars, section, option,
0762                 _recursion):
0763        self.parser = parser
0764        self.extra_vars = extra_vars
0765        self.normal_extra_vars = {}
0766        for name, value in extra_vars.iteritems():
0767            self.normal_extra_vars[parser.optionxform(name)] = value
0768        self.section = section
0769        self.option = option
0770        self._recursion = _recursion
0771
0772    def __getitem__(self, item):
0773        if item == '__name__':
0774            return self.section
0775        item = self.parser.optionxform(item)
0776        if item in self.normal_extra_vars:
0777            return self.normal_extra_vars[item]
0778        if self.parser.has_option(self.section, item):
0779            return self.parser.get(self.section, item,
0780                                   vars=self.extra_vars,
0781                                   _recursion=self._recursion+1)
0782        if item in self.parser.defaults():
0783            return self.parser.defaults()[item]
0784        raise KeyError(item)
0785
0786class _SectionOptionWrapper(_OptionWrapper):
0787    """
0788    This provides the dict wrapper for dollar substitution.  Unlike
0789    percent substitution, you can reference values from arbitrary
0790    sections.
0791    """
0792
0793    def __getitem__(self, item):
0794        if ':' in item:
0795            # Explicit section:
0796            section, item = item.split(':', 1)
0797            return self.parser.get(section, item, vars=self.extra_vars,
0798                                   _recursion=self._recursion+1)
0799        return _OptionWrapper.__getitem__(self, item)