configparser.py 25.4 KB
Newer Older
1 2 3 4
"""Configuration file parser.

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

The option values can contain format strings which refer to other values in
the same section, or values in a special [DEFAULT] section.
9 10 11 12 13 14 15 16 17 18 19 20 21

For example:

    something: %(dir)s/whatever

would resolve the "%(dir)s" to the value of dir.  All reference
expansions are done late, on demand.

Intrinsic defaults can be specified by passing them into the
ConfigParser constructor as a dictionary.

class:

22
ConfigParser -- responsible for parsing a list of
23 24 25 26
                configuration files, and managing the parsed database.

    methods:

27 28 29 30
    __init__(defaults=None)
        create the parser and specify a dictionary of intrinsic defaults.  The
        keys must be strings, the values must be appropriate for %()s string
        interpolation.  Note that `__name__' is always an intrinsic default;
31
        its value is the section's name.
32

33 34
    sections()
        return all the configuration section names, sans DEFAULT
35

36 37 38
    has_section(section)
        return whether the given section exists

39 40 41
    has_option(section, option)
        return whether the given option exists in the given section

42 43
    options(section)
        return list of configuration options for the named section
44

45
    read(filenames)
Guido van Rossum's avatar
Guido van Rossum committed
46 47
        read and parse the list of named configuration files, given by
        name.  A single filename is also allowed.  Non-existing files
Fred Drake's avatar
Fred Drake committed
48
        are ignored.  Return list of successfully read files.
Guido van Rossum's avatar
Guido van Rossum committed
49 50 51 52

    readfp(fp, filename=None)
        read and parse one configuration file, given as a file object.
        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 57 58 59 60
        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.
61

62 63
    getint(section, options)
        like get(), but convert value to an integer
64

65 66
    getfloat(section, options)
        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 71
        insensitively defined as 0, false, no, off for False, and 1, true,
        yes, on for True).  Returns False or True.
72

73
    items(section, raw=False, vars=None)
74 75 76
        return a list of tuples with (name, value) for each option
        in the section.

77
    remove_section(section)
Tim Peters's avatar
Tim Peters committed
78
        remove the given file section and all its options
79 80

    remove_option(section, option)
Tim Peters's avatar
Tim Peters committed
81
        remove the given option from the given section
82 83 84 85 86

    set(section, option, value)
        set the given option

    write(fp)
Tim Peters's avatar
Tim Peters committed
87
        write the configuration state in .ini format
88 89
"""

90 91 92 93 94 95
try:
    from collections import OrderedDict as _default_dict
except ImportError:
    # fallback for setup.py which hasn't yet built _collections
    _default_dict = dict

Barry Warsaw's avatar
Barry Warsaw committed
96
import re
97

98 99 100
__all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError",
           "InterpolationError", "InterpolationDepthError",
           "InterpolationSyntaxError", "ParsingError",
101 102
           "MissingSectionHeaderError",
           "ConfigParser", "SafeConfigParser", "RawConfigParser",
103
           "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
104

105 106
DEFAULTSECT = "DEFAULT"

107 108
MAX_INTERPOLATION_DEPTH = 10

109

Tim Peters's avatar
Tim Peters committed
110

111
# exception classes
112
class Error(Exception):
113 114
    """Base class for ConfigParser exceptions."""

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    def _get_message(self):
        """Getter for 'message'; needed only to override deprecation in
        BaseException."""
        return self.__message

    def _set_message(self, value):
        """Setter for 'message'; needed only to override deprecation in
        BaseException."""
        self.__message = value

    # BaseException.message has been deprecated since Python 2.6.  To prevent
    # DeprecationWarning from popping up over this pre-existing attribute, use
    # a new property that takes lookup precedence.
    message = property(_get_message, _set_message)

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 141
    """Raised when no section matches a requested option."""

142
    def __init__(self, section):
143
        Error.__init__(self, 'No section: %r' % (section,))
144
        self.section = section
145 146

class DuplicateSectionError(Error):
147 148
    """Raised when a section is multiply-created."""

149
    def __init__(self, section):
150
        Error.__init__(self, "Section %r already exists" % section)
151
        self.section = section
152 153

class NoOptionError(Error):
154 155
    """A requested option was not found."""

156
    def __init__(self, option, section):
157
        Error.__init__(self, "No option %r in section: %r" %
158 159 160
                       (option, section))
        self.option = option
        self.section = section
161 162

class InterpolationError(Error):
163
    """Base class for interpolation-related exceptions."""
164

165 166
    def __init__(self, option, section, msg):
        Error.__init__(self, msg)
167 168
        self.option = option
        self.section = section
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183
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

class InterpolationSyntaxError(InterpolationError):
184 185
    """Raised when the source text into which substitutions are made
    does not conform to the required syntax."""
186

187
class InterpolationDepthError(InterpolationError):
188 189
    """Raised when substitutions are nested too deeply."""

190
    def __init__(self, option, section, rawval):
191 192 193 194 195 196
        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)
Barry Warsaw's avatar
Barry Warsaw committed
197 198

class ParsingError(Error):
199 200
    """Raised when a configuration file does not follow legal syntax."""

Barry Warsaw's avatar
Barry Warsaw committed
201 202 203 204 205 206 207
    def __init__(self, filename):
        Error.__init__(self, 'File contains parsing errors: %s' % filename)
        self.filename = filename
        self.errors = []

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

210
class MissingSectionHeaderError(ParsingError):
211 212
    """Raised when a key-value pair is found before any section header."""

213 214 215
    def __init__(self, filename, lineno, line):
        Error.__init__(
            self,
216
            'File contains no section headers.\nfile: %s, line: %d\n%r' %
217 218 219 220 221
            (filename, lineno, line))
        self.filename = filename
        self.lineno = lineno
        self.line = line

222

223
class RawConfigParser:
224 225
    def __init__(self, defaults=None, dict_type=_default_dict,
                 allow_no_value=False):
226 227 228
        self._dict = dict_type
        self._sections = self._dict()
        self._defaults = self._dict()
229 230 231 232
        if allow_no_value:
            self._optcre = self.OPTCRE_NV
        else:
            self._optcre = self.OPTCRE
233 234 235
        if defaults:
            for key, value in defaults.items():
                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 list(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
Christian Heimes's avatar
Christian Heimes committed
249 250
        already exists. Raise ValueError if name is DEFAULT or any of it's
        case-insensitive variants.
251
        """
Christian Heimes's avatar
Christian Heimes committed
252 253 254
        if section.lower() == "default":
            raise ValueError('Invalid section name: %s' % section)

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 263
        The DEFAULT section is not acknowledged.
        """
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 271
        except KeyError:
            raise NoSectionError(section)
272
        opts.update(self._defaults)
273
        if '__name__' in opts:
274
            del opts['__name__']
275
        return list(opts.keys())
276 277

    def read(self, filenames):
Guido van Rossum's avatar
Guido van Rossum committed
278
        """Read and parse a filename or a list of filenames.
Tim Peters's avatar
Tim Peters committed
279

Guido van Rossum's avatar
Guido van Rossum committed
280
        Files that cannot be opened are silently ignored; this is
281
        designed so that you can specify a list of potential
Guido van Rossum's avatar
Guido van Rossum committed
282 283 284 285
        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.
Fred Drake's avatar
Fred Drake committed
286 287

        Return list of successfully read files.
Guido van Rossum's avatar
Guido van Rossum committed
288
        """
289
        if isinstance(filenames, str):
290
            filenames = [filenames]
Fred Drake's avatar
Fred Drake committed
291
        read_ok = []
Guido van Rossum's avatar
Guido van Rossum committed
292 293 294 295 296
        for filename in filenames:
            try:
                fp = open(filename)
            except IOError:
                continue
297
            self._read(fp, filename)
298
            fp.close()
Fred Drake's avatar
Fred Drake committed
299 300
            read_ok.append(filename)
        return read_ok
301

Guido van Rossum's avatar
Guido van Rossum committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315
    def readfp(self, fp, filename=None):
        """Like read() but the argument must be a file-like object.

        The `fp' argument must have a `readline' method.  Optional
        second argument is the `filename', which if not given, is
        taken from fp.name.  If fp has no `name' attribute, `<???>' is
        used.

        """
        if filename is None:
            try:
                filename = fp.name
            except AttributeError:
                filename = '<???>'
316
        self._read(fp, filename)
Guido van Rossum's avatar
Guido van Rossum committed
317

318 319 320
    def get(self, section, option):
        opt = self.optionxform(option)
        if section not in self._sections:
321
            if section != DEFAULTSECT:
322
                raise NoSectionError(section)
323 324 325 326 327 328 329 330 331
            if opt in self._defaults:
                return self._defaults[opt]
            else:
                raise NoOptionError(option, section)
        elif opt in self._sections[section]:
            return self._sections[section][opt]
        elif opt in self._defaults:
            return self._defaults[opt]
        else:
332
            raise NoOptionError(option, section)
333

334
    def items(self, section):
335
        try:
336
            d2 = self._sections[section]
337 338 339
        except KeyError:
            if section != DEFAULTSECT:
                raise NoSectionError(section)
340
            d2 = self._dict()
341 342
        d = self._defaults.copy()
        d.update(d2)
343 344
        if "__name__" in d:
            del d["__name__"]
345
        return d.items()
Tim Peters's avatar
Tim Peters committed
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 357 358
    _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
                       '0': False, 'no': False, 'false': False, 'off': False}

359
    def getboolean(self, section, option):
Tim Peters's avatar
Tim Peters committed
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 369
    def has_option(self, section, option):
        """Check for the existence of a given option in a given section."""
370 371
        if not section or section == DEFAULTSECT:
            option = self.optionxform(option)
372 373
            return option in self._defaults
        elif section not in self._sections:
374
            return False
375
        else:
376
            option = self.optionxform(option)
377 378
            return (option in self._sections[section]
                    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 385
        else:
            try:
386
                sectdict = self._sections[section]
387 388
            except KeyError:
                raise NoSectionError(section)
389
        sectdict[self.optionxform(option)] = value
390 391 392

    def write(self, fp):
        """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 403 404 405 406
                    if value is None:
                        fp.write("%s\n" % (key))
                    else:
                        fp.write("%s = %s\n" %
                                 (key, str(value).replace('\n', '\n\t')))
407 408
            fp.write("\n")

409
    def remove_option(self, section, option):
410
        """Remove an option."""
411
        if not section or section == DEFAULTSECT:
412
            sectdict = self._defaults
413 414
        else:
            try:
415
                sectdict = self._sections[section]
416 417
            except KeyError:
                raise NoSectionError(section)
418
        option = self.optionxform(option)
419
        existed = option in sectdict
420
        if existed:
421
            del sectdict[option]
422 423
        return existed

424
    def remove_section(self, section):
425
        """Remove a file section."""
426
        existed = section in self._sections
427
        if existed:
428
            del self._sections[section]
429
        return existed
430

Barry Warsaw's avatar
Barry Warsaw committed
431
    #
432 433
    # Regular expressions for parsing section headers and options.
    #
434
    SECTCRE = re.compile(
Barry Warsaw's avatar
Barry Warsaw committed
435
        r'\['                                 # [
436
        r'(?P<header>[^]]+)'                  # very permissive!
Barry Warsaw's avatar
Barry Warsaw committed
437 438
        r'\]'                                 # ]
        )
439
    OPTCRE = re.compile(
440
        r'(?P<option>[^:=\s][^:=]*)'          # very permissive!
441
        r'\s*(?P<vi>[:=])\s*'                 # any number of space/tab,
Barry Warsaw's avatar
Barry Warsaw committed
442 443 444 445 446
                                              # followed by separator
                                              # (either : or =), followed
                                              # by any # space/tab
        r'(?P<value>.*)$'                     # everything up to eol
        )
447 448 449 450 451 452 453 454 455
    OPTCRE_NV = re.compile(
        r'(?P<option>[^:=\s][^:=]*)'          # very permissive!
        r'\s*(?:'                             # any number of space/tab,
        r'(?P<vi>[:=])\s*'                    # optionally followed by
                                              # separator (either : or
                                              # =), followed by any #
                                              # space/tab
        r'(?P<value>.*))?$'                   # everything up to eol
        )
Barry Warsaw's avatar
Barry Warsaw committed
456

457
    def _read(self, fp, fpname):
458 459 460 461 462
        """Parse a sectioned setup file.

        The sections in setup file contains a title line at the top,
        indicated by a name in square brackets (`[]'), plus key/value
        options lines, indicated by `name: value' format lines.
Andrew M. Kuchling's avatar
Andrew M. Kuchling committed
463
        Continuations are represented by an embedded newline then
464
        leading whitespace.  Blank lines, lines beginning with a '#',
Andrew M. Kuchling's avatar
Andrew M. Kuchling committed
465
        and just about everything else are ignored.
466
        """
Barry Warsaw's avatar
Barry Warsaw committed
467
        cursect = None                            # None, or a dictionary
468 469
        optname = None
        lineno = 0
Barry Warsaw's avatar
Barry Warsaw committed
470
        e = None                                  # None, or an exception
471
        while True:
472 473 474 475 476
            line = fp.readline()
            if not line:
                break
            lineno = lineno + 1
            # comment or blank line?
477
            if line.strip() == '' or line[0] in '#;':
478
                continue
479 480
            if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
                # no leading whitespace
481 482
                continue
            # continuation line?
483
            if line[0].isspace() and cursect is not None and optname:
484
                value = line.strip()
485
                if value:
486
                    cursect[optname] = "%s\n%s" % (cursect[optname], value)
Barry Warsaw's avatar
Barry Warsaw committed
487
            # a section header or option header?
488
            else:
Barry Warsaw's avatar
Barry Warsaw committed
489
                # is it a section header?
490
                mo = self.SECTCRE.match(line)
Barry Warsaw's avatar
Barry Warsaw committed
491 492
                if mo:
                    sectname = mo.group('header')
493 494
                    if sectname in self._sections:
                        cursect = self._sections[sectname]
Barry Warsaw's avatar
Barry Warsaw committed
495
                    elif sectname == DEFAULTSECT:
496
                        cursect = self._defaults
Barry Warsaw's avatar
Barry Warsaw committed
497
                    else:
498 499
                        cursect = self._dict()
                        cursect['__name__'] = sectname
500
                        self._sections[sectname] = cursect
Barry Warsaw's avatar
Barry Warsaw committed
501 502 503 504
                    # So sections can't start with a continuation line
                    optname = None
                # no section header in the file?
                elif cursect is None:
505
                    raise MissingSectionHeaderError(fpname, lineno, line)
Barry Warsaw's avatar
Barry Warsaw committed
506 507
                # an option line?
                else:
508
                    mo = self._optcre.match(line)
Barry Warsaw's avatar
Barry Warsaw committed
509
                    if mo:
510
                        optname, vi, optval = mo.group('option', 'vi', 'value')
511 512 513 514 515 516 517 518 519 520
                        # This check is fine because the OPTCRE cannot
                        # match if it would set optval to None
                        if optval is not None:
                            if vi in ('=', ':') and ';' in optval:
                                # ';' is a comment delimiter only if it follows
                                # a spacing character
                                pos = optval.find(';')
                                if pos != -1 and optval[pos-1].isspace():
                                    optval = optval[:pos]
                            optval = optval.strip()
Barry Warsaw's avatar
Barry Warsaw committed
521 522 523
                        # allow empty values
                        if optval == '""':
                            optval = ''
524
                        optname = self.optionxform(optname.rstrip())
525
                        cursect[optname] = optval
Barry Warsaw's avatar
Barry Warsaw committed
526 527 528 529 530 531
                    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
                        if not e:
Guido van Rossum's avatar
Guido van Rossum committed
532
                            e = ParsingError(fpname)
533
                        e.append(lineno, repr(line))
Barry Warsaw's avatar
Barry Warsaw committed
534 535 536
        # if any parsing errors occurred, raise an exception
        if e:
            raise e
537 538 539 540


class ConfigParser(RawConfigParser):

541
    def get(self, section, option, raw=False, vars=None):
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
        """Get an option value for a given 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.
        """
        d = self._defaults.copy()
        try:
            d.update(self._sections[section])
        except KeyError:
            if section != DEFAULTSECT:
                raise NoSectionError(section)
        # Update with the entry specific variables
559 560 561
        if vars:
            for key, value in vars.items():
                d[self.optionxform(key)] = value
562 563 564 565 566 567
        option = self.optionxform(option)
        try:
            value = d[option]
        except KeyError:
            raise NoOptionError(option, section)

568
        if raw or value is None:
569 570 571 572
            return value
        else:
            return self._interpolate(section, option, value, d)

573
    def items(self, section, raw=False, vars=None):
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
        """Return a list of tuples with (name, value) for each option
        in the 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.
        """
        d = self._defaults.copy()
        try:
            d.update(self._sections[section])
        except KeyError:
            if section != DEFAULTSECT:
                raise NoSectionError(section)
        # Update with the entry specific variables
        if vars:
593 594
            for key, value in vars.items():
                d[self.optionxform(key)] = value
595
        options = list(d.keys())
596 597
        if "__name__" in options:
            options.remove("__name__")
598
        if raw:
599 600
            return [(option, d[option])
                    for option in options]
601
        else:
602 603
            return [(option, self._interpolate(section, option, d[option], d))
                    for option in options]
604 605 606 607

    def _interpolate(self, section, option, rawval, vars):
        # do the string interpolation
        value = rawval
Tim Peters's avatar
Tim Peters committed
608
        depth = MAX_INTERPOLATION_DEPTH
609 610
        while depth:                    # Loop through this until it's done
            depth -= 1
611
            if value and "%(" in value:
Fred Drake's avatar
Fred Drake committed
612
                value = self._KEYCRE.sub(self._interpolation_replace, value)
613 614
                try:
                    value = value % vars
615
                except KeyError as e:
616
                    raise InterpolationMissingOptionError(
617
                        option, section, rawval, e.args[0])
618 619
            else:
                break
620
        if value and "%(" in value:
621 622
            raise InterpolationDepthError(option, section, rawval)
        return value
623

Fred Drake's avatar
Fred Drake committed
624 625 626 627 628 629 630 631 632
    _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")

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

633 634 635 636 637 638 639 640 641

class SafeConfigParser(ConfigParser):

    def _interpolate(self, section, option, rawval, vars):
        # do the string interpolation
        L = []
        self._interpolate_some(option, L, rawval, section, vars, 1)
        return ''.join(L)

642
    _interpvar_re = re.compile(r"%\(([^)]+)\)s")
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660

    def _interpolate_some(self, 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 == "(":
661
                m = self._interpvar_re.match(rest)
662
                if m is None:
663 664
                    raise InterpolationSyntaxError(option, section,
                        "bad interpolation variable reference %r" % rest)
Fred Drake's avatar
Fred Drake committed
665
                var = self.optionxform(m.group(1))
666 667 668 669
                rest = rest[m.end():]
                try:
                    v = map[var]
                except KeyError:
670 671
                    raise InterpolationMissingOptionError(
                        option, section, rest, var)
672 673 674 675 676 677 678
                if "%" in v:
                    self._interpolate_some(option, accum, v,
                                           section, map, depth + 1)
                else:
                    accum.append(v)
            else:
                raise InterpolationSyntaxError(
679
                    option, section,
680
                    "'%%' must be followed by '%%' or '(', found: %r" % (rest,))
681

682
    def set(self, section, option, value=None):
683
        """Set an option.  Extend ConfigParser.set: check for string values."""
684 685 686 687 688 689 690 691
        # 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
        if self._optcre is self.OPTCRE or value:
            if not isinstance(value, str):
                raise TypeError("option values must be strings")
692 693
        # check for bad percent signs:
        # first, replace all "good" interpolations
694 695
        tmp_value = value.replace('%%', '')
        tmp_value = self._interpvar_re.sub('', tmp_value)
696
        # then, check if there's a lone percent sign left
697 698
        percent_index = tmp_value.find('%')
        if percent_index != -1:
699
            raise ValueError("invalid interpolation syntax in %r at "
700
                             "position %d" % (value, percent_index))
701
        ConfigParser.set(self, section, option, value)