Home | History | Annotate | Download | only in Lib
      1 """Configuration file parser.
      2 
      3 A setup file consists of sections, lead by a "[section]" header,
      4 and followed by "name: value" entries, with continuations and such in
      5 the style of RFC 822.
      6 
      7 The option values can contain format strings which refer to other values in
      8 the same section, or values in a special [DEFAULT] section.
      9 
     10 For example:
     11 
     12     something: %(dir)s/whatever
     13 
     14 would resolve the "%(dir)s" to the value of dir.  All reference
     15 expansions are done late, on demand.
     16 
     17 Intrinsic defaults can be specified by passing them into the
     18 ConfigParser constructor as a dictionary.
     19 
     20 class:
     21 
     22 ConfigParser -- responsible for parsing a list of
     23                 configuration files, and managing the parsed database.
     24 
     25     methods:
     26 
     27     __init__(defaults=None)
     28         create the parser and specify a dictionary of intrinsic defaults.  The
     29         keys must be strings, the values must be appropriate for %()s string
     30         interpolation.  Note that `__name__' is always an intrinsic default;
     31         its value is the section's name.
     32 
     33     sections()
     34         return all the configuration section names, sans DEFAULT
     35 
     36     has_section(section)
     37         return whether the given section exists
     38 
     39     has_option(section, option)
     40         return whether the given option exists in the given section
     41 
     42     options(section)
     43         return list of configuration options for the named section
     44 
     45     read(filenames)
     46         read and parse the list of named configuration files, given by
     47         name.  A single filename is also allowed.  Non-existing files
     48         are ignored.  Return list of successfully read files.
     49 
     50     readfp(fp, filename=None)
     51         read and parse one configuration file, given as a file object.
     52         The filename defaults to fp.name; it is only used in error
     53         messages (if fp has no `name' attribute, the string `<???>' is used).
     54 
     55     get(section, option, raw=False, vars=None)
     56         return a string value for the named option.  All % interpolations are
     57         expanded in the return values, based on the defaults passed into the
     58         constructor and the DEFAULT section.  Additional substitutions may be
     59         provided using the `vars' argument, which must be a dictionary whose
     60         contents override any pre-existing defaults.
     61 
     62     getint(section, options)
     63         like get(), but convert value to an integer
     64 
     65     getfloat(section, options)
     66         like get(), but convert value to a float
     67 
     68     getboolean(section, options)
     69         like get(), but convert value to a boolean (currently case
     70         insensitively defined as 0, false, no, off for False, and 1, true,
     71         yes, on for True).  Returns False or True.
     72 
     73     items(section, raw=False, vars=None)
     74         return a list of tuples with (name, value) for each option
     75         in the section.
     76 
     77     remove_section(section)
     78         remove the given file section and all its options
     79 
     80     remove_option(section, option)
     81         remove the given option from the given section
     82 
     83     set(section, option, value)
     84         set the given option
     85 
     86     write(fp)
     87         write the configuration state in .ini format
     88 """
     89 
     90 try:
     91     from collections import OrderedDict as _default_dict
     92 except ImportError:
     93     # fallback for setup.py which hasn't yet built _collections
     94     _default_dict = dict
     95 
     96 import re
     97 
     98 __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError",
     99            "InterpolationError", "InterpolationDepthError",
    100            "InterpolationSyntaxError", "ParsingError",
    101            "MissingSectionHeaderError",
    102            "ConfigParser", "SafeConfigParser", "RawConfigParser",
    103            "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
    104 
    105 DEFAULTSECT = "DEFAULT"
    106 
    107 MAX_INTERPOLATION_DEPTH = 10
    108 
    109 
    110 
    111 # exception classes

    112 class Error(Exception):
    113     """Base class for ConfigParser exceptions."""
    114 
    115     def _get_message(self):
    116         """Getter for 'message'; needed only to override deprecation in
    117         BaseException."""
    118         return self.__message
    119 
    120     def _set_message(self, value):
    121         """Setter for 'message'; needed only to override deprecation in
    122         BaseException."""
    123         self.__message = value
    124 
    125     # BaseException.message has been deprecated since Python 2.6.  To prevent

    126     # DeprecationWarning from popping up over this pre-existing attribute, use

    127     # a new property that takes lookup precedence.

    128     message = property(_get_message, _set_message)
    129 
    130     def __init__(self, msg=''):
    131         self.message = msg
    132         Exception.__init__(self, msg)
    133 
    134     def __repr__(self):
    135         return self.message
    136 
    137     __str__ = __repr__
    138 
    139 class NoSectionError(Error):
    140     """Raised when no section matches a requested option."""
    141 
    142     def __init__(self, section):
    143         Error.__init__(self, 'No section: %r' % (section,))
    144         self.section = section
    145 
    146 class DuplicateSectionError(Error):
    147     """Raised when a section is multiply-created."""
    148 
    149     def __init__(self, section):
    150         Error.__init__(self, "Section %r already exists" % section)
    151         self.section = section
    152 
    153 class NoOptionError(Error):
    154     """A requested option was not found."""
    155 
    156     def __init__(self, option, section):
    157         Error.__init__(self, "No option %r in section: %r" %
    158                        (option, section))
    159         self.option = option
    160         self.section = section
    161 
    162 class InterpolationError(Error):
    163     """Base class for interpolation-related exceptions."""
    164 
    165     def __init__(self, option, section, msg):
    166         Error.__init__(self, msg)
    167         self.option = option
    168         self.section = section
    169 
    170 class InterpolationMissingOptionError(InterpolationError):
    171     """A string substitution required a setting which was not available."""
    172 
    173     def __init__(self, option, section, rawval, reference):
    174         msg = ("Bad value substitution:\n"
    175                "\tsection: [%s]\n"
    176                "\toption : %s\n"
    177                "\tkey    : %s\n"
    178                "\trawval : %s\n"
    179                % (section, option, reference, rawval))
    180         InterpolationError.__init__(self, option, section, msg)
    181         self.reference = reference
    182 
    183 class InterpolationSyntaxError(InterpolationError):
    184     """Raised when the source text into which substitutions are made
    185     does not conform to the required syntax."""
    186 
    187 class InterpolationDepthError(InterpolationError):
    188     """Raised when substitutions are nested too deeply."""
    189 
    190     def __init__(self, option, section, rawval):
    191         msg = ("Value interpolation too deeply recursive:\n"
    192                "\tsection: [%s]\n"
    193                "\toption : %s\n"
    194                "\trawval : %s\n"
    195                % (section, option, rawval))
    196         InterpolationError.__init__(self, option, section, msg)
    197 
    198 class ParsingError(Error):
    199     """Raised when a configuration file does not follow legal syntax."""
    200 
    201     def __init__(self, filename):
    202         Error.__init__(self, 'File contains parsing errors: %s' % filename)
    203         self.filename = filename
    204         self.errors = []
    205 
    206     def append(self, lineno, line):
    207         self.errors.append((lineno, line))
    208         self.message += '\n\t[line %2d]: %s' % (lineno, line)
    209 
    210 class MissingSectionHeaderError(ParsingError):
    211     """Raised when a key-value pair is found before any section header."""
    212 
    213     def __init__(self, filename, lineno, line):
    214         Error.__init__(
    215             self,
    216             'File contains no section headers.\nfile: %s, line: %d\n%r' %
    217             (filename, lineno, line))
    218         self.filename = filename
    219         self.lineno = lineno
    220         self.line = line
    221 
    222 
    223 class RawConfigParser:
    224     def __init__(self, defaults=None, dict_type=_default_dict,
    225                  allow_no_value=False):
    226         self._dict = dict_type
    227         self._sections = self._dict()
    228         self._defaults = self._dict()
    229         if allow_no_value:
    230             self._optcre = self.OPTCRE_NV
    231         else:
    232             self._optcre = self.OPTCRE
    233         if defaults:
    234             for key, value in defaults.items():
    235                 self._defaults[self.optionxform(key)] = value
    236 
    237     def defaults(self):
    238         return self._defaults
    239 
    240     def sections(self):
    241         """Return a list of section names, excluding [DEFAULT]"""
    242         # self._sections will never have [DEFAULT] in it

    243         return self._sections.keys()
    244 
    245     def add_section(self, section):
    246         """Create a new section in the configuration.
    247 
    248         Raise DuplicateSectionError if a section by the specified name
    249         already exists. Raise ValueError if name is DEFAULT or any of it's
    250         case-insensitive variants.
    251         """
    252         if section.lower() == "default":
    253             raise ValueError, 'Invalid section name: %s' % section
    254 
    255         if section in self._sections:
    256             raise DuplicateSectionError(section)
    257         self._sections[section] = self._dict()
    258 
    259     def has_section(self, section):
    260         """Indicate whether the named section is present in the configuration.
    261 
    262         The DEFAULT section is not acknowledged.
    263         """
    264         return section in self._sections
    265 
    266     def options(self, section):
    267         """Return a list of option names for the given section name."""
    268         try:
    269             opts = self._sections[section].copy()
    270         except KeyError:
    271             raise NoSectionError(section)
    272         opts.update(self._defaults)
    273         if '__name__' in opts:
    274             del opts['__name__']
    275         return opts.keys()
    276 
    277     def read(self, filenames):
    278         """Read and parse a filename or a list of filenames.
    279 
    280         Files that cannot be opened are silently ignored; this is
    281         designed so that you can specify a list of potential
    282         configuration file locations (e.g. current directory, user's
    283         home directory, systemwide directory), and all existing
    284         configuration files in the list will be read.  A single
    285         filename may also be given.
    286 
    287         Return list of successfully read files.
    288         """
    289         if isinstance(filenames, basestring):
    290             filenames = [filenames]
    291         read_ok = []
    292         for filename in filenames:
    293             try:
    294                 fp = open(filename)
    295             except IOError:
    296                 continue
    297             self._read(fp, filename)
    298             fp.close()
    299             read_ok.append(filename)
    300         return read_ok
    301 
    302     def readfp(self, fp, filename=None):
    303         """Like read() but the argument must be a file-like object.
    304 
    305         The `fp' argument must have a `readline' method.  Optional
    306         second argument is the `filename', which if not given, is
    307         taken from fp.name.  If fp has no `name' attribute, `<???>' is
    308         used.
    309 
    310         """
    311         if filename is None:
    312             try:
    313                 filename = fp.name
    314             except AttributeError:
    315                 filename = '<???>'
    316         self._read(fp, filename)
    317 
    318     def get(self, section, option):
    319         opt = self.optionxform(option)
    320         if section not in self._sections:
    321             if section != DEFAULTSECT:
    322                 raise NoSectionError(section)
    323             if opt in self._defaults:
    324                 return self._defaults[opt]
    325             else:
    326                 raise NoOptionError(option, section)
    327         elif opt in self._sections[section]:
    328             return self._sections[section][opt]
    329         elif opt in self._defaults:
    330             return self._defaults[opt]
    331         else:
    332             raise NoOptionError(option, section)
    333 
    334     def items(self, section):
    335         try:
    336             d2 = self._sections[section]
    337         except KeyError:
    338             if section != DEFAULTSECT:
    339                 raise NoSectionError(section)
    340             d2 = self._dict()
    341         d = self._defaults.copy()
    342         d.update(d2)
    343         if "__name__" in d:
    344             del d["__name__"]
    345         return d.items()
    346 
    347     def _get(self, section, conv, option):
    348         return conv(self.get(section, option))
    349 
    350     def getint(self, section, option):
    351         return self._get(section, int, option)
    352 
    353     def getfloat(self, section, option):
    354         return self._get(section, float, option)
    355 
    356     _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
    357                        '0': False, 'no': False, 'false': False, 'off': False}
    358 
    359     def getboolean(self, section, option):
    360         v = self.get(section, option)
    361         if v.lower() not in self._boolean_states:
    362             raise ValueError, 'Not a boolean: %s' % v
    363         return self._boolean_states[v.lower()]
    364 
    365     def optionxform(self, optionstr):
    366         return optionstr.lower()
    367 
    368     def has_option(self, section, option):
    369         """Check for the existence of a given option in a given section."""
    370         if not section or section == DEFAULTSECT:
    371             option = self.optionxform(option)
    372             return option in self._defaults
    373         elif section not in self._sections:
    374             return False
    375         else:
    376             option = self.optionxform(option)
    377             return (option in self._sections[section]
    378                     or option in self._defaults)
    379 
    380     def set(self, section, option, value=None):
    381         """Set an option."""
    382         if not section or section == DEFAULTSECT:
    383             sectdict = self._defaults
    384         else:
    385             try:
    386                 sectdict = self._sections[section]
    387             except KeyError:
    388                 raise NoSectionError(section)
    389         sectdict[self.optionxform(option)] = value
    390 
    391     def write(self, fp):
    392         """Write an .ini-format representation of the configuration state."""
    393         if self._defaults:
    394             fp.write("[%s]\n" % DEFAULTSECT)
    395             for (key, value) in self._defaults.items():
    396                 fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
    397             fp.write("\n")
    398         for section in self._sections:
    399             fp.write("[%s]\n" % section)
    400             for (key, value) in self._sections[section].items():
    401                 if key == "__name__":
    402                     continue
    403                 if (value is not None) or (self._optcre == self.OPTCRE):
    404                     key = " = ".join((key, str(value).replace('\n', '\n\t')))
    405                 fp.write("%s\n" % (key))
    406             fp.write("\n")
    407 
    408     def remove_option(self, section, option):
    409         """Remove an option."""
    410         if not section or section == DEFAULTSECT:
    411             sectdict = self._defaults
    412         else:
    413             try:
    414                 sectdict = self._sections[section]
    415             except KeyError:
    416                 raise NoSectionError(section)
    417         option = self.optionxform(option)
    418         existed = option in sectdict
    419         if existed:
    420             del sectdict[option]
    421         return existed
    422 
    423     def remove_section(self, section):
    424         """Remove a file section."""
    425         existed = section in self._sections
    426         if existed:
    427             del self._sections[section]
    428         return existed
    429 
    430     #
    431     # Regular expressions for parsing section headers and options.
    432     #
    433     SECTCRE = re.compile(
    434         r'\['                                 # [
    435         r'(?P<header>[^]]+)'                  # very permissive!
    436         r'\]'                                 # ]
    437         )
    438     OPTCRE = re.compile(
    439         r'(?P<option>[^:=\s][^:=]*)'          # very permissive!
    440         r'\s*(?P<vi>[:=])\s*'                 # any number of space/tab,
    441                                               # followed by separator
    442                                               # (either : or =), followed
    443                                               # by any # space/tab
    444         r'(?P<value>.*)$'                     # everything up to eol
    445         )
    446     OPTCRE_NV = re.compile(
    447         r'(?P<option>[^:=\s][^:=]*)'          # very permissive!
    448         r'\s*(?:'                             # any number of space/tab,
    449         r'(?P<vi>[:=])\s*'                    # optionally followed by
    450                                               # separator (either : or
    451                                               # =), followed by any #
    452                                               # space/tab
    453         r'(?P<value>.*))?$'                   # everything up to eol
    454         )
    455 
    456     def _read(self, fp, fpname):
    457         """Parse a sectioned setup file.
    458 
    459         The sections in setup file contains a title line at the top,
    460         indicated by a name in square brackets (`[]'), plus key/value
    461         options lines, indicated by `name: value' format lines.
    462         Continuations are represented by an embedded newline then
    463         leading whitespace.  Blank lines, lines beginning with a '#',
    464         and just about everything else are ignored.
    465         """
    466         cursect = None                        # None, or a dictionary
    467         optname = None
    468         lineno = 0
    469         e = None                              # None, or an exception
    470         while True:
    471             line = fp.readline()
    472             if not line:
    473                 break
    474             lineno = lineno + 1
    475             # comment or blank line?
    476             if line.strip() == '' or line[0] in '#;':
    477                 continue
    478             if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
    479                 # no leading whitespace
    480                 continue
    481             # continuation line?
    482             if line[0].isspace() and cursect is not None and optname:
    483                 value = line.strip()
    484                 if value:
    485                     cursect[optname].append(value)
    486             # a section header or option header?
    487             else:
    488                 # is it a section header?
    489                 mo = self.SECTCRE.match(line)
    490                 if mo:
    491                     sectname = mo.group('header')
    492                     if sectname in self._sections:
    493                         cursect = self._sections[sectname]
    494                     elif sectname == DEFAULTSECT:
    495                         cursect = self._defaults
    496                     else:
    497                         cursect = self._dict()
    498                         cursect['__name__'] = sectname
    499                         self._sections[sectname] = cursect
    500                     # So sections can't start with a continuation line
    501                     optname = None
    502                 # no section header in the file?
    503                 elif cursect is None:
    504                     raise MissingSectionHeaderError(fpname, lineno, line)
    505                 # an option line?
    506                 else:
    507                     mo = self._optcre.match(line)
    508                     if mo:
    509                         optname, vi, optval = mo.group('option', 'vi', 'value')
    510                         optname = self.optionxform(optname.rstrip())
    511                         # This check is fine because the OPTCRE cannot
    512                         # match if it would set optval to None
    513                         if optval is not None:
    514                             if vi in ('=', ':') and ';' in optval:
    515                                 # ';' is a comment delimiter only if it follows
    516                                 # a spacing character
    517                                 pos = optval.find(';')
    518                                 if pos != -1 and optval[pos-1].isspace():
    519                                     optval = optval[:pos]
    520                             optval = optval.strip()
    521                             # allow empty values
    522                             if optval == '""':
    523                                 optval = ''
    524                             cursect[optname] = [optval]
    525                         else:
    526                             # valueless option handling
    527                             cursect[optname] = optval
    528                     else:
    529                         # a non-fatal parsing error occurred.  set up the
    530                         # exception but keep going. the exception will be
    531                         # raised at the end of the file and will contain a
    532                         # list of all bogus lines
    533                         if not e:
    534                             e = ParsingError(fpname)
    535                         e.append(lineno, repr(line))
    536         # if any parsing errors occurred, raise an exception
    537         if e:
    538             raise e
    539 
    540         # join the multi-line values collected while reading
    541         all_sections = [self._defaults]
    542         all_sections.extend(self._sections.values())
    543         for options in all_sections:
    544             for name, val in options.items():
    545                 if isinstance(val, list):
    546                     options[name] = '\n'.join(val)
    547 
    548 import UserDict as _UserDict
    549 
    550 class _Chainmap(_UserDict.DictMixin):
    551     """Combine multiple mappings for successive lookups.
    552 
    553     For example, to emulate Python's normal lookup sequence:
    554 
    555         import __builtin__
    556         pylookup = _Chainmap(locals(), globals(), vars(__builtin__))
    557     """
    558 
    559     def __init__(self, *maps):
    560         self._maps = maps
    561 
    562     def __getitem__(self, key):
    563         for mapping in self._maps:
    564             try:
    565                 return mapping[key]
    566             except KeyError:
    567                 pass
    568         raise KeyError(key)
    569 
    570     def keys(self):
    571         result = []
    572         seen = set()
    573         for mapping in self_maps:
    574             for key in mapping:
    575                 if key not in seen:
    576                     result.append(key)
    577                     seen.add(key)
    578         return result
    579 
    580 class ConfigParser(RawConfigParser):
    581 
    582     def get(self, section, option, raw=False, vars=None):
    583         """Get an option value for a given section.
    584 
    585         If `vars' is provided, it must be a dictionary. The option is looked up
    586         in `vars' (if provided), `section', and in `defaults' in that order.
    587 
    588         All % interpolations are expanded in the return values, unless the
    589         optional argument `raw' is true. Values for interpolation keys are
    590         looked up in the same manner as the option.
    591 
    592         The section DEFAULT is special.
    593         """
    594         sectiondict = {}
    595         try:
    596             sectiondict = self._sections[section]
    597         except KeyError:
    598             if section != DEFAULTSECT:
    599                 raise NoSectionError(section)
    600         # Update with the entry specific variables
    601         vardict = {}
    602         if vars:
    603             for key, value in vars.items():
    604                 vardict[self.optionxform(key)] = value
    605         d = _Chainmap(vardict, sectiondict, self._defaults)
    606         option = self.optionxform(option)
    607         try:
    608             value = d[option]
    609         except KeyError:
    610             raise NoOptionError(option, section)
    611 
    612         if raw or value is None:
    613             return value
    614         else:
    615             return self._interpolate(section, option, value, d)
    616 
    617     def items(self, section, raw=False, vars=None):
    618         """Return a list of tuples with (name, value) for each option
    619         in the section.
    620 
    621         All % interpolations are expanded in the return values, based on the
    622         defaults passed into the constructor, unless the optional argument
    623         `raw' is true.  Additional substitutions may be provided using the
    624         `vars' argument, which must be a dictionary whose contents overrides
    625         any pre-existing defaults.
    626 
    627         The section DEFAULT is special.
    628         """
    629         d = self._defaults.copy()
    630         try:
    631             d.update(self._sections[section])
    632         except KeyError:
    633             if section != DEFAULTSECT:
    634                 raise NoSectionError(section)
    635         # Update with the entry specific variables
    636         if vars:
    637             for key, value in vars.items():
    638                 d[self.optionxform(key)] = value
    639         options = d.keys()
    640         if "__name__" in options:
    641             options.remove("__name__")
    642         if raw:
    643             return [(option, d[option])
    644                     for option in options]
    645         else:
    646             return [(option, self._interpolate(section, option, d[option], d))
    647                     for option in options]
    648 
    649     def _interpolate(self, section, option, rawval, vars):
    650         # do the string interpolation
    651         value = rawval
    652         depth = MAX_INTERPOLATION_DEPTH
    653         while depth:                    # Loop through this until it's done
    654             depth -= 1
    655             if value and "%(" in value:
    656                 value = self._KEYCRE.sub(self._interpolation_replace, value)
    657                 try:
    658                     value = value % vars
    659                 except KeyError, e:
    660                     raise InterpolationMissingOptionError(
    661                         option, section, rawval, e.args[0])
    662             else:
    663                 break
    664         if value and "%(" in value:
    665             raise InterpolationDepthError(option, section, rawval)
    666         return value
    667 
    668     _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
    669 
    670     def _interpolation_replace(self, match):
    671         s = match.group(1)
    672         if s is None:
    673             return match.group()
    674         else:
    675             return "%%(%s)s" % self.optionxform(s)
    676 
    677 
    678 class SafeConfigParser(ConfigParser):
    679 
    680     def _interpolate(self, section, option, rawval, vars):
    681         # do the string interpolation
    682         L = []
    683         self._interpolate_some(option, L, rawval, section, vars, 1)
    684         return ''.join(L)
    685 
    686     _interpvar_re = re.compile(r"%\(([^)]+)\)s")
    687 
    688     def _interpolate_some(self, option, accum, rest, section, map, depth):
    689         if depth > MAX_INTERPOLATION_DEPTH:
    690             raise InterpolationDepthError(option, section, rest)
    691         while rest:
    692             p = rest.find("%")
    693             if p < 0:
    694                 accum.append(rest)
    695                 return
    696             if p > 0:
    697                 accum.append(rest[:p])
    698                 rest = rest[p:]
    699             # p is no longer used
    700             c = rest[1:2]
    701             if c == "%":
    702                 accum.append("%")
    703                 rest = rest[2:]
    704             elif c == "(":
    705                 m = self._interpvar_re.match(rest)
    706                 if m is None:
    707                     raise InterpolationSyntaxError(option, section,
    708                         "bad interpolation variable reference %r" % rest)
    709                 var = self.optionxform(m.group(1))
    710                 rest = rest[m.end():]
    711                 try:
    712                     v = map[var]
    713                 except KeyError:
    714                     raise InterpolationMissingOptionError(
    715                         option, section, rest, var)
    716                 if "%" in v:
    717                     self._interpolate_some(option, accum, v,
    718                                            section, map, depth + 1)
    719                 else:
    720                     accum.append(v)
    721             else:
    722                 raise InterpolationSyntaxError(
    723                     option, section,
    724                     "'%%' must be followed by '%%' or '(', found: %r" % (rest,))
    725 
    726     def set(self, section, option, value=None):
    727         """Set an option.  Extend ConfigParser.set: check for string values."""
    728         # The only legal non-string value if we allow valueless
    729         # options is None, so we need to check if the value is a
    730         # string if:
    731         # - we do not allow valueless options, or
    732         # - we allow valueless options but the value is not None
    733         if self._optcre is self.OPTCRE or value:
    734             if not isinstance(value, basestring):
    735                 raise TypeError("option values must be strings")
    736         if value is not None:
    737             # check for bad percent signs:
    738             # first, replace all "good" interpolations
    739             tmp_value = value.replace('%%', '')
    740             tmp_value = self._interpvar_re.sub('', tmp_value)
    741             # then, check if there's a lone percent sign left
    742             if '%' in tmp_value:
    743                 raise ValueError("invalid interpolation syntax in %r at "
    744                                 "position %d" % (value, tmp_value.find('%')))
    745         ConfigParser.set(self, section, option, value)
    746