codekingpro/portable-devtools
114k
1"""Configuration file parser.2 3A configuration file consists of sections, lead by a "[section]" header,4and followed by "name: value" entries, with continuations and such in5the style of RFC 822.6 7Intrinsic defaults can be specified by passing them into the8ConfigParser constructor as a dictionary.9 10class:11 12ConfigParser -- responsible for parsing a list of13 configuration files, and managing the parsed database.14 15 methods:16 17 __init__(defaults=None, dict_type=_default_dict, allow_no_value=False,18 delimiters=('=', ':'), comment_prefixes=('#', ';'),19 inline_comment_prefixes=None, strict=True,20 empty_lines_in_values=True, default_section='DEFAULT',21 interpolation=<unset>, converters=<unset>,22 allow_unnamed_section=False):23 Create the parser. When `defaults` is given, it is initialized into the24 dictionary or intrinsic defaults. The keys must be strings, the values25 must be appropriate for %()s string interpolation.26 27 When `dict_type` is given, it will be used to create the dictionary28 objects for the list of sections, for the options within a section, and29 for the default values.30 31 When `delimiters` is given, it will be used as the set of substrings32 that divide keys from values.33 34 When `comment_prefixes` is given, it will be used as the set of35 substrings that prefix comments in empty lines. Comments can be36 indented.37 38 When `inline_comment_prefixes` is given, it will be used as the set of39 substrings that prefix comments in non-empty lines.40 41 When `strict` is True, the parser won't allow for any section or option42 duplicates while reading from a single source (file, string or43 dictionary). Default is True.44 45 When `empty_lines_in_values` is False (default: True), each empty line46 marks the end of an option. Otherwise, internal empty lines of47 a multiline option are kept as part of the value.48 49 When `allow_no_value` is True (default: False), options without50 values are accepted; the value presented for these is None.51 52 When `default_section` is given, the name of the special section is53 named accordingly. By default it is called ``"DEFAULT"`` but this can54 be customized to point to any other valid section name. Its current55 value can be retrieved using the ``parser_instance.default_section``56 attribute and may be modified at runtime.57 58 When `interpolation` is given, it should be an Interpolation subclass59 instance. It will be used as the handler for option value60 pre-processing when using getters. RawConfigParser objects don't do61 any sort of interpolation, whereas ConfigParser uses an instance of62 BasicInterpolation. The library also provides a ``zc.buildout``63 inspired ExtendedInterpolation implementation.64 65 When `converters` is given, it should be a dictionary where each key66 represents the name of a type converter and each value is a callable67 implementing the conversion from string to the desired datatype. Every68 converter gets its corresponding get*() method on the parser object and69 section proxies.70 71 When `allow_unnamed_section` is True (default: False), options72 without section are accepted: the section for these is73 ``configparser.UNNAMED_SECTION``.74 75 sections()76 Return all the configuration section names, sans DEFAULT.77 78 has_section(section)79 Return whether the given section exists.80 81 has_option(section, option)82 Return whether the given option exists in the given section.83 84 options(section)85 Return list of configuration options for the named section.86 87 read(filenames, encoding=None)88 Read and parse the iterable of named configuration files, given by89 name. A single filename is also allowed. Non-existing files90 are ignored. Return list of successfully read files.91 92 read_file(f, filename=None)93 Read and parse one configuration file, given as a file object.94 The filename defaults to f.name; it is only used in error95 messages (if f has no `name` attribute, the string `<???>` is used).96 97 read_string(string)98 Read configuration from a given string.99 100 read_dict(dictionary)101 Read configuration from a dictionary. Keys are section names,102 values are dictionaries with keys and values that should be present103 in the section. If the used dictionary type preserves order, sections104 and their keys will be added in order. Values are automatically105 converted to strings.106 107 get(section, option, raw=False, vars=None, fallback=_UNSET)108 Return a string value for the named option. All % interpolations are109 expanded in the return values, based on the defaults passed into the110 constructor and the DEFAULT section. Additional substitutions may be111 provided using the `vars` argument, which must be a dictionary whose112 contents override any pre-existing defaults. If `option` is a key in113 `vars`, the value from `vars` is used.114 115 getint(section, options, raw=False, vars=None, fallback=_UNSET)116 Like get(), but convert value to an integer.117 118 getfloat(section, options, raw=False, vars=None, fallback=_UNSET)119 Like get(), but convert value to a float.120 121 getboolean(section, options, raw=False, vars=None, fallback=_UNSET)122 Like get(), but convert value to a boolean (currently case123 insensitively defined as 0, false, no, off for False, and 1, true,124 yes, on for True). Returns False or True.125 126 items(section=_UNSET, raw=False, vars=None)127 If section is given, return a list of tuples with (name, value) for128 each option in the section. Otherwise, return a list of tuples with129 (section_name, section_proxy) for each section, including DEFAULTSECT.130 131 remove_section(section)132 Remove the given file section and all its options.133 134 remove_option(section, option)135 Remove the given option from the given section.136 137 set(section, option, value)138 Set the given option.139 140 write(fp, space_around_delimiters=True)141 Write the configuration state in .ini format. If142 `space_around_delimiters` is True (the default), delimiters143 between keys and values are surrounded by spaces.144"""145 146# Do not import dataclasses; overhead is unacceptable (gh-117703)147 148from collections.abc import Iterable, MutableMapping149from collections import ChainMap as _ChainMap150import contextlib151import functools152import io153import itertools154import os155import re156import sys157 158__all__ = ("NoSectionError", "DuplicateOptionError", "DuplicateSectionError",159 "NoOptionError", "InterpolationError", "InterpolationDepthError",160 "InterpolationMissingOptionError", "InterpolationSyntaxError",161 "ParsingError", "MissingSectionHeaderError",162 "MultilineContinuationError", "UnnamedSectionDisabledError",163 "InvalidWriteError", "ConfigParser", "RawConfigParser",164 "Interpolation", "BasicInterpolation", "ExtendedInterpolation",165 "SectionProxy", "ConverterMapping",166 "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH", "UNNAMED_SECTION")167 168_default_dict = dict169DEFAULTSECT = "DEFAULT"170 171MAX_INTERPOLATION_DEPTH = 10172 173 174 175# exception classes176class Error(Exception):177 """Base class for ConfigParser exceptions."""178 179 def __init__(self, msg=''):180 self.message = msg181 Exception.__init__(self, msg)182 183 def __repr__(self):184 return self.message185 186 __str__ = __repr__187 188 189class NoSectionError(Error):190 """Raised when no section matches a requested option."""191 192 def __init__(self, section):193 Error.__init__(self, 'No section: %r' % (section,))194 self.section = section195 self.args = (section, )196 197 198class DuplicateSectionError(Error):199 """Raised when a section is repeated in an input source.200 201 Possible repetitions that raise this exception are: multiple creation202 using the API or in strict parsers when a section is found more than once203 in a single input file, string or dictionary.204 """205 206 def __init__(self, section, source=None, lineno=None):207 msg = [repr(section), " already exists"]208 if source is not None:209 message = ["While reading from ", repr(source)]210 if lineno is not None:211 message.append(" [line {0:2d}]".format(lineno))212 message.append(": section ")213 message.extend(msg)214 msg = message215 else:216 msg.insert(0, "Section ")217 Error.__init__(self, "".join(msg))218 self.section = section219 self.source = source220 self.lineno = lineno221 self.args = (section, source, lineno)222 223 224class DuplicateOptionError(Error):225 """Raised by strict parsers when an option is repeated in an input source.226 227 Current implementation raises this exception only when an option is found228 more than once in a single file, string or dictionary.229 """230 231 def __init__(self, section, option, source=None, lineno=None):232 msg = [repr(option), " in section ", repr(section),233 " already exists"]234 if source is not None:235 message = ["While reading from ", repr(source)]236 if lineno is not None:237 message.append(" [line {0:2d}]".format(lineno))238 message.append(": option ")239 message.extend(msg)240 msg = message241 else:242 msg.insert(0, "Option ")243 Error.__init__(self, "".join(msg))244 self.section = section245 self.option = option246 self.source = source247 self.lineno = lineno248 self.args = (section, option, source, lineno)249 250 251class NoOptionError(Error):252 """A requested option was not found."""253 254 def __init__(self, option, section):255 Error.__init__(self, "No option %r in section: %r" %256 (option, section))257 self.option = option258 self.section = section259 self.args = (option, section)260 261 262class InterpolationError(Error):263 """Base class for interpolation-related exceptions."""264 265 def __init__(self, option, section, msg):266 Error.__init__(self, msg)267 self.option = option268 self.section = section269 self.args = (option, section, msg)270 271 272class InterpolationMissingOptionError(InterpolationError):273 """A string substitution required a setting which was not available."""274 275 def __init__(self, option, section, rawval, reference):276 msg = ("Bad value substitution: option {!r} in section {!r} contains "277 "an interpolation key {!r} which is not a valid option name. "278 "Raw value: {!r}".format(option, section, reference, rawval))279 InterpolationError.__init__(self, option, section, msg)280 self.reference = reference281 self.args = (option, section, rawval, reference)282 283 284class InterpolationSyntaxError(InterpolationError):285 """Raised when the source text contains invalid syntax.286 287 Current implementation raises this exception when the source text into288 which substitutions are made does not conform to the required syntax.289 """290 291 292class InterpolationDepthError(InterpolationError):293 """Raised when substitutions are nested too deeply."""294 295 def __init__(self, option, section, rawval):296 msg = ("Recursion limit exceeded in value substitution: option {!r} "297 "in section {!r} contains an interpolation key which "298 "cannot be substituted in {} steps. Raw value: {!r}"299 "".format(option, section, MAX_INTERPOLATION_DEPTH,300 rawval))301 InterpolationError.__init__(self, option, section, msg)302 self.args = (option, section, rawval)303 304 305class ParsingError(Error):306 """Raised when a configuration file does not follow legal syntax."""307 308 def __init__(self, source, *args):309 super().__init__(f'Source contains parsing errors: {source!r}')310 self.source = source311 self.errors = []312 self.args = (source, )313 if args:314 self.append(*args)315 316 def append(self, lineno, line):317 self.errors.append((lineno, line))318 self.message += '\n\t[line %2d]: %s' % (lineno, repr(line))319 320 def combine(self, others):321 for other in others:322 for error in other.errors:323 self.append(*error)324 return self325 326 @staticmethod327 def _raise_all(exceptions: Iterable['ParsingError']):328 """329 Combine any number of ParsingErrors into one and raise it.330 """331 exceptions = iter(exceptions)332 with contextlib.suppress(StopIteration):333 raise next(exceptions).combine(exceptions)334 335 336 337class MissingSectionHeaderError(ParsingError):338 """Raised when a key-value pair is found before any section header."""339 340 def __init__(self, filename, lineno, line):341 Error.__init__(342 self,343 'File contains no section headers.\nfile: %r, line: %d\n%r' %344 (filename, lineno, line))345 self.source = filename346 self.lineno = lineno347 self.line = line348 self.args = (filename, lineno, line)349 350 351class MultilineContinuationError(ParsingError):352 """Raised when a key without value is followed by continuation line"""353 def __init__(self, filename, lineno, line):354 Error.__init__(355 self,356 "Key without value continued with an indented line.\n"357 "file: %r, line: %d\n%r"358 %(filename, lineno, line))359 self.source = filename360 self.lineno = lineno361 self.line = line362 self.args = (filename, lineno, line)363 364 365class UnnamedSectionDisabledError(Error):366 """Raised when an attempt to use UNNAMED_SECTION is made with the367 feature disabled."""368 def __init__(self):369 Error.__init__(self, "Support for UNNAMED_SECTION is disabled.")370 371 372class _UnnamedSection:373 374 def __repr__(self):375 return "<UNNAMED_SECTION>"376 377class InvalidWriteError(Error):378 """Raised when attempting to write data that the parser would read back differently.379 ex: writing a key which begins with the section header pattern would read back as a380 new section """381 382 def __init__(self, msg=''):383 Error.__init__(self, msg)384 385 386UNNAMED_SECTION = _UnnamedSection()387 388 389# Used in parser getters to indicate the default behaviour when a specific390# option is not found it to raise an exception. Created to enable `None` as391# a valid fallback value.392_UNSET = object()393 394 395class Interpolation:396 """Dummy interpolation that passes the value through with no changes."""397 398 def before_get(self, parser, section, option, value, defaults):399 return value400 401 def before_set(self, parser, section, option, value):402 return value403 404 def before_read(self, parser, section, option, value):405 return value406 407 def before_write(self, parser, section, option, value):408 return value409 410 411class BasicInterpolation(Interpolation):412 """Interpolation as implemented in the classic ConfigParser.413 414 The option values can contain format strings which refer to other values in415 the same section, or values in the special default section.416 417 For example:418 419 something: %(dir)s/whatever420 421 would resolve the "%(dir)s" to the value of dir. All reference422 expansions are done late, on demand. If a user needs to use a bare % in423 a configuration file, she can escape it by writing %%. Other % usage424 is considered a user error and raises `InterpolationSyntaxError`."""425 426 _KEYCRE = re.compile(r"%\(([^)]+)\)s")427 428 def before_get(self, parser, section, option, value, defaults):429 L = []430 self._interpolate_some(parser, option, L, value, section, defaults, 1)431 return ''.join(L)432 433 def before_set(self, parser, section, option, value):434 tmp_value = value.replace('%%', '') # escaped percent signs435 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax436 if '%' in tmp_value:437 raise ValueError("invalid interpolation syntax in %r at "438 "position %d" % (value, tmp_value.find('%')))439 return value440 441 def _interpolate_some(self, parser, option, accum, rest, section, map,442 depth):443 rawval = parser.get(section, option, raw=True, fallback=rest)444 if depth > MAX_INTERPOLATION_DEPTH:445 raise InterpolationDepthError(option, section, rawval)446 while rest:447 p = rest.find("%")448 if p < 0:449 accum.append(rest)450 return451 if p > 0:452 accum.append(rest[:p])453 rest = rest[p:]454 # p is no longer used455 c = rest[1:2]456 if c == "%":457 accum.append("%")458 rest = rest[2:]459 elif c == "(":460 m = self._KEYCRE.match(rest)461 if m is None:462 raise InterpolationSyntaxError(option, section,463 "bad interpolation variable reference %r" % rest)464 var = parser.optionxform(m.group(1))465 rest = rest[m.end():]466 try:467 v = map[var]468 except KeyError:469 raise InterpolationMissingOptionError(470 option, section, rawval, var) from None471 if "%" in v:472 self._interpolate_some(parser, option, accum, v,473 section, map, depth + 1)474 else:475 accum.append(v)476 else:477 raise InterpolationSyntaxError(478 option, section,479 "'%%' must be followed by '%%' or '(', "480 "found: %r" % (rest,))481 482 483class ExtendedInterpolation(Interpolation):484 """Advanced variant of interpolation, supports the syntax used by485 `zc.buildout`. Enables interpolation between sections."""486 487 _KEYCRE = re.compile(r"\$\{([^}]+)\}")488 489 def before_get(self, parser, section, option, value, defaults):490 L = []491 self._interpolate_some(parser, option, L, value, section, defaults, 1)492 return ''.join(L)493 494 def before_set(self, parser, section, option, value):495 tmp_value = value.replace('$$', '') # escaped dollar signs496 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax497 if '$' in tmp_value:498 raise ValueError("invalid interpolation syntax in %r at "499 "position %d" % (value, tmp_value.find('$')))500 return value501 502 def _interpolate_some(self, parser, option, accum, rest, section, map,503 depth):504 rawval = parser.get(section, option, raw=True, fallback=rest)505 if depth > MAX_INTERPOLATION_DEPTH:506 raise InterpolationDepthError(option, section, rawval)507 while rest:508 p = rest.find("$")509 if p < 0:510 accum.append(rest)511 return512 if p > 0:513 accum.append(rest[:p])514 rest = rest[p:]515 # p is no longer used516 c = rest[1:2]517 if c == "$":518 accum.append("$")519 rest = rest[2:]520 elif c == "{":521 m = self._KEYCRE.match(rest)522 if m is None:523 raise InterpolationSyntaxError(option, section,524 "bad interpolation variable reference %r" % rest)525 path = m.group(1).split(':')526 rest = rest[m.end():]527 sect = section528 opt = option529 try:530 if len(path) == 1:531 opt = parser.optionxform(path[0])532 v = map[opt]533 elif len(path) == 2:534 sect = path[0]535 opt = parser.optionxform(path[1])536 v = parser.get(sect, opt, raw=True)537 else:538 raise InterpolationSyntaxError(539 option, section,540 "More than one ':' found: %r" % (rest,))541 except (KeyError, NoSectionError, NoOptionError):542 raise InterpolationMissingOptionError(543 option, section, rawval, ":".join(path)) from None544 if v is None:545 continue546 if "$" in v:547 self._interpolate_some(parser, opt, accum, v, sect,548 dict(parser.items(sect, raw=True)),549 depth + 1)550 else:551 accum.append(v)552 else:553 raise InterpolationSyntaxError(554 option, section,555 "'$' must be followed by '$' or '{', "556 "found: %r" % (rest,))557 558 559class _ReadState:560 elements_added : set[str]561 cursect : dict[str, str] | None = None562 sectname : str | None = None563 optname : str | None = None564 lineno : int = 0565 indent_level : int = 0566 errors : list[ParsingError]567 568 def __init__(self):569 self.elements_added = set()570 self.errors = list()571 572 573class _Line(str):574 __slots__ = 'clean', 'has_comments'575 576 def __new__(cls, val, *args, **kwargs):577 return super().__new__(cls, val)578 579 def __init__(self, val, comments):580 trimmed = val.strip()581 self.clean = comments.strip(trimmed)582 self.has_comments = trimmed != self.clean583 584 585class _CommentSpec:586 def __init__(self, full_prefixes, inline_prefixes):587 full_patterns = (588 # prefix at the beginning of a line589 fr'^({re.escape(prefix)}).*'590 for prefix in full_prefixes591 )592 inline_patterns = (593 # prefix at the beginning of the line or following a space594 fr'(^|\s)({re.escape(prefix)}.*)'595 for prefix in inline_prefixes596 )597 self.pattern = re.compile('|'.join(itertools.chain(full_patterns, inline_patterns)))598 599 def strip(self, text):600 return self.pattern.sub('', text).rstrip()601 602 def wrap(self, text):603 return _Line(text, self)604 605 606class RawConfigParser(MutableMapping):607 """ConfigParser that does not do interpolation."""608 609 # Regular expressions for parsing section headers and options610 _SECT_TMPL = r"""611 \[ # [612 (?P<header>.+) # very permissive!613 \] # ]614 """615 _OPT_TMPL = r"""616 (?P<option>.*?) # very permissive!617 \s*(?P<vi>{delim})\s* # any number of space/tab,618 # followed by any of the619 # allowed delimiters,620 # followed by any space/tab621 (?P<value>.*)$ # everything up to eol622 """623 _OPT_NV_TMPL = r"""624 (?P<option>.*?) # very permissive!625 \s*(?: # any number of space/tab,626 (?P<vi>{delim})\s* # optionally followed by627 # any of the allowed628 # delimiters, followed by any629 # space/tab630 (?P<value>.*))?$ # everything up to eol631 """632 # Interpolation algorithm to be used if the user does not specify another633 _DEFAULT_INTERPOLATION = Interpolation()634 # Compiled regular expression for matching sections635 SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)636 # Compiled regular expression for matching options with typical separators637 OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE)638 # Compiled regular expression for matching options with optional values639 # delimited using typical separators640 OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE)641 # Compiled regular expression for matching leading whitespace in a line642 NONSPACECRE = re.compile(r"\S")643 # Possible boolean values in the configuration.644 BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True,645 '0': False, 'no': False, 'false': False, 'off': False}646 647 def __init__(self, defaults=None, dict_type=_default_dict,648 allow_no_value=False, *, delimiters=('=', ':'),649 comment_prefixes=('#', ';'), inline_comment_prefixes=None,650 strict=True, empty_lines_in_values=True,651 default_section=DEFAULTSECT,652 interpolation=_UNSET, converters=_UNSET,653 allow_unnamed_section=False,):654 655 self._dict = dict_type656 self._sections = self._dict()657 self._defaults = self._dict()658 self._converters = ConverterMapping(self)659 self._proxies = self._dict()660 self._proxies[default_section] = SectionProxy(self, default_section)661 self._delimiters = tuple(delimiters)662 if delimiters == ('=', ':'):663 self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE664 else:665 d = "|".join(re.escape(d) for d in delimiters)666 if allow_no_value:667 self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d),668 re.VERBOSE)669 else:670 self._optcre = re.compile(self._OPT_TMPL.format(delim=d),671 re.VERBOSE)672 self._comments = _CommentSpec(comment_prefixes or (), inline_comment_prefixes or ())673 self._strict = strict674 self._allow_no_value = allow_no_value675 self._empty_lines_in_values = empty_lines_in_values676 self.default_section=default_section677 self._interpolation = interpolation678 if self._interpolation is _UNSET:679 self._interpolation = self._DEFAULT_INTERPOLATION680 if self._interpolation is None:681 self._interpolation = Interpolation()682 if not isinstance(self._interpolation, Interpolation):683 raise TypeError(684 f"interpolation= must be None or an instance of Interpolation;"685 f" got an object of type {type(self._interpolation)}"686 )687 if converters is not _UNSET:688 self._converters.update(converters)689 if defaults:690 self._read_defaults(defaults)691 self._allow_unnamed_section = allow_unnamed_section692 693 def defaults(self):694 return self._defaults695 696 def sections(self):697 """Return a list of section names, excluding [DEFAULT]"""698 # self._sections will never have [DEFAULT] in it699 return list(self._sections.keys())700 701 def add_section(self, section):702 """Create a new section in the configuration.703 704 Raise DuplicateSectionError if a section by the specified name705 already exists. Raise ValueError if name is DEFAULT.706 """707 if section == self.default_section:708 raise ValueError('Invalid section name: %r' % section)709 710 if section is UNNAMED_SECTION:711 if not self._allow_unnamed_section:712 raise UnnamedSectionDisabledError713 714 if section in self._sections:715 raise DuplicateSectionError(section)716 self._sections[section] = self._dict()717 self._proxies[section] = SectionProxy(self, section)718 719 def has_section(self, section):720 """Indicate whether the named section is present in the configuration.721 722 The DEFAULT section is not acknowledged.723 """724 return section in self._sections725 726 def options(self, section):727 """Return a list of option names for the given section name."""728 try:729 opts = self._sections[section].copy()730 except KeyError:731 raise NoSectionError(section) from None732 opts.update(self._defaults)733 return list(opts.keys())734 735 def read(self, filenames, encoding=None):736 """Read and parse a filename or an iterable of filenames.737 738 Files that cannot be opened are silently ignored; this is739 designed so that you can specify an iterable of potential740 configuration file locations (e.g. current directory, user's741 home directory, systemwide directory), and all existing742 configuration files in the iterable will be read. A single743 filename may also be given.744 745 Return list of successfully read files.746 """747 if isinstance(filenames, (str, bytes, os.PathLike)):748 filenames = [filenames]749 encoding = io.text_encoding(encoding)750 read_ok = []751 for filename in filenames:752 try:753 with open(filename, encoding=encoding) as fp:754 self._read(fp, filename)755 except OSError:756 continue757 if isinstance(filename, os.PathLike):758 filename = os.fspath(filename)759 read_ok.append(filename)760 return read_ok761 762 def read_file(self, f, source=None):763 """Like read() but the argument must be a file-like object.764 765 The `f` argument must be iterable, returning one line at a time.766 Optional second argument is the `source` specifying the name of the767 file being read. If not given, it is taken from f.name. If `f` has no768 `name` attribute, `<???>` is used.769 """770 if source is None:771 try:772 source = f.name773 except AttributeError:774 source = '<???>'775 self._read(f, source)776 777 def read_string(self, string, source='<string>'):778 """Read configuration from a given string."""779 sfile = io.StringIO(string)780 self.read_file(sfile, source)781 782 def read_dict(self, dictionary, source='<dict>'):783 """Read configuration from a dictionary.784 785 Keys are section names, values are dictionaries with keys and values786 that should be present in the section. If the used dictionary type787 preserves order, sections and their keys will be added in order.788 789 All types held in the dictionary are converted to strings during790 reading, including section names, option names and keys.791 792 Optional second argument is the `source` specifying the name of the793 dictionary being read.794 """795 elements_added = set()796 for section, keys in dictionary.items():797 if section is not UNNAMED_SECTION:798 section = str(section)799 try:800 self.add_section(section)801 except (DuplicateSectionError, ValueError):802 if self._strict and section in elements_added:803 raise804 elements_added.add(section)805 for key, value in keys.items():806 key = self.optionxform(str(key))807 if value is not None:808 value = str(value)809 if self._strict and (section, key) in elements_added:810 raise DuplicateOptionError(section, key, source)811 elements_added.add((section, key))812 self.set(section, key, value)813 814 def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):815 """Get an option value for a given section.816 817 If `vars` is provided, it must be a dictionary. The option is looked up818 in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order.819 If the key is not found and `fallback` is provided, it is used as820 a fallback value. `None` can be provided as a `fallback` value.821 822 If interpolation is enabled and the optional argument `raw` is False,823 all interpolations are expanded in the return values.824 825 Arguments `raw`, `vars`, and `fallback` are keyword only.826 827 The section DEFAULT is special.828 """829 try:830 d = self._unify_values(section, vars)831 except NoSectionError:832 if fallback is _UNSET:833 raise834 else:835 return fallback836 option = self.optionxform(option)837 try:838 value = d[option]839 except KeyError:840 if fallback is _UNSET:841 raise NoOptionError(option, section)842 else:843 return fallback844 845 if raw or value is None:846 return value847 else:848 return self._interpolation.before_get(self, section, option, value,849 d)850 851 def _get(self, section, conv, option, **kwargs):852 return conv(self.get(section, option, **kwargs))853 854 def _get_conv(self, section, option, conv, *, raw=False, vars=None,855 fallback=_UNSET, **kwargs):856 try:857 return self._get(section, conv, option, raw=raw, vars=vars,858 **kwargs)859 except (NoSectionError, NoOptionError):860 if fallback is _UNSET:861 raise862 return fallback863 864 # getint, getfloat and getboolean provided directly for backwards compat865 def getint(self, section, option, *, raw=False, vars=None,866 fallback=_UNSET, **kwargs):867 return self._get_conv(section, option, int, raw=raw, vars=vars,868 fallback=fallback, **kwargs)869 870 def getfloat(self, section, option, *, raw=False, vars=None,871 fallback=_UNSET, **kwargs):872 return self._get_conv(section, option, float, raw=raw, vars=vars,873 fallback=fallback, **kwargs)874 875 def getboolean(self, section, option, *, raw=False, vars=None,876 fallback=_UNSET, **kwargs):877 return self._get_conv(section, option, self._convert_to_boolean,878 raw=raw, vars=vars, fallback=fallback, **kwargs)879 880 def items(self, section=_UNSET, raw=False, vars=None):881 """Return a list of (name, value) tuples for each option in a section.882 883 All % interpolations are expanded in the return values, based on the884 defaults passed into the constructor, unless the optional argument885 `raw` is true. Additional substitutions may be provided using the886 `vars` argument, which must be a dictionary whose contents overrides887 any pre-existing defaults.888 889 The section DEFAULT is special.890 """891 if section is _UNSET:892 return super().items()893 d = self._defaults.copy()894 try:895 d.update(self._sections[section])896 except KeyError:897 if section != self.default_section:898 raise NoSectionError(section)899 orig_keys = list(d.keys())900 # Update with the entry specific variables901 if vars:902 for key, value in vars.items():903 d[self.optionxform(key)] = value904 value_getter = lambda option: self._interpolation.before_get(self,905 section, option, d[option], d)906 if raw:907 value_getter = lambda option: d[option]908 return [(option, value_getter(option)) for option in orig_keys]909 910 def popitem(self):911 """Remove a section from the parser and return it as912 a (section_name, section_proxy) tuple. If no section is present, raise913 KeyError.914 915 The section DEFAULT is never returned because it cannot be removed.916 """917 for key in self.sections():918 value = self[key]919 del self[key]920 return key, value921 raise KeyError922 923 def optionxform(self, optionstr):924 return optionstr.lower()925 926 def has_option(self, section, option):927 """Check for the existence of a given option in a given section.928 If the specified `section` is None or an empty string, DEFAULT is929 assumed. If the specified `section` does not exist, returns False."""930 if not section or section == self.default_section:931 option = self.optionxform(option)932 return option in self._defaults933 elif section not in self._sections:934 return False935 else:936 option = self.optionxform(option)937 return (option in self._sections[section]938 or option in self._defaults)939 940 def set(self, section, option, value=None):941 """Set an option."""942 if value:943 value = self._interpolation.before_set(self, section, option,944 value)945 if not section or section == self.default_section:946 sectdict = self._defaults947 else:948 try:949 sectdict = self._sections[section]950 except KeyError:951 raise NoSectionError(section) from None952 sectdict[self.optionxform(option)] = value953 954 def write(self, fp, space_around_delimiters=True):955 """Write an .ini-format representation of the configuration state.956 957 If `space_around_delimiters` is True (the default), delimiters958 between keys and values are surrounded by spaces.959 960 Please note that comments in the original configuration file are not961 preserved when writing the configuration back.962 """963 if space_around_delimiters:964 d = " {} ".format(self._delimiters[0])965 else:966 d = self._delimiters[0]967 if self._defaults:968 self._write_section(fp, self.default_section,969 self._defaults.items(), d)970 if UNNAMED_SECTION in self._sections and self._sections[UNNAMED_SECTION]:971 self._write_section(fp, UNNAMED_SECTION, self._sections[UNNAMED_SECTION].items(), d, unnamed=True)972 973 for section in self._sections:974 if section is UNNAMED_SECTION:975 continue976 self._write_section(fp, section,977 self._sections[section].items(), d)978 979 def _write_section(self, fp, section_name, section_items, delimiter, unnamed=False):980 """Write a single section to the specified 'fp'."""981 if not unnamed:982 fp.write("[{}]\n".format(section_name))983 for key, value in section_items:984 self._validate_key_contents(key)985 value = self._interpolation.before_write(self, section_name, key,986 value)987 if value is not None or not self._allow_no_value:988 value = delimiter + str(value).replace('\n', '\n\t')989 else:990 value = ""991 fp.write("{}{}\n".format(key, value))992 fp.write("\n")993 994 def remove_option(self, section, option):995 """Remove an option."""996 if not section or section == self.default_section:997 sectdict = self._defaults998 else:999 try:1000 sectdict = self._sections[section]1001 except KeyError:1002 raise NoSectionError(section) from None1003 option = self.optionxform(option)1004 existed = option in sectdict1005 if existed:1006 del sectdict[option]1007 return existed1008 1009 def remove_section(self, section):1010 """Remove a file section."""1011 existed = section in self._sections1012 if existed:1013 del self._sections[section]1014 del self._proxies[section]1015 return existed1016 1017 def __getitem__(self, key):1018 if key != self.default_section and not self.has_section(key):1019 raise KeyError(key)1020 return self._proxies[key]1021 1022 def __setitem__(self, key, value):1023 # To conform with the mapping protocol, overwrites existing values in1024 # the section.1025 if key in self and self[key] is value:1026 return1027 # XXX this is not atomic if read_dict fails at any point. Then again,1028 # no update method in configparser is atomic in this implementation.1029 if key == self.default_section:1030 self._defaults.clear()1031 elif key in self._sections:1032 self._sections[key].clear()1033 self.read_dict({key: value})1034 1035 def __delitem__(self, key):1036 if key == self.default_section:1037 raise ValueError("Cannot remove the default section.")1038 if not self.has_section(key):1039 raise KeyError(key)1040 self.remove_section(key)1041 1042 def __contains__(self, key):1043 return key == self.default_section or self.has_section(key)1044 1045 def __len__(self):1046 return len(self._sections) + 1 # the default section1047 1048 def __iter__(self):1049 # XXX does it break when underlying container state changed?1050 return itertools.chain((self.default_section,), self._sections.keys())1051 1052 def _read(self, fp, fpname):1053 """Parse a sectioned configuration file.1054 1055 Each section in a configuration file contains a header, indicated by1056 a name in square brackets (`[]`), plus key/value options, indicated by1057 `name` and `value` delimited with a specific substring (`=` or `:` by1058 default).1059 1060 Values can span multiple lines, as long as they are indented deeper1061 than the first line of the value. Depending on the parser's mode, blank1062 lines may be treated as parts of multiline values or ignored.1063 1064 Configuration files may include comments, prefixed by specific1065 characters (`#` and `;` by default). Comments may appear on their own1066 in an otherwise empty line or may be entered in lines holding values or1067 section names. Please note that comments get stripped off when reading configuration files.1068 """1069 try:1070 ParsingError._raise_all(self._read_inner(fp, fpname))1071 finally:1072 self._join_multiline_values()1073 1074 def _read_inner(self, fp, fpname):1075 st = _ReadState()1076 1077 for st.lineno, line in enumerate(map(self._comments.wrap, fp), start=1):1078 if not line.clean:1079 if self._empty_lines_in_values:1080 # add empty line to the value, but only if there was no1081 # comment on the line1082 if (not line.has_comments and1083 st.cursect is not None and1084 st.optname and1085 st.cursect[st.optname] is not None):1086 st.cursect[st.optname].append('') # newlines added at join1087 else:1088 # empty line marks end of value1089 st.indent_level = sys.maxsize1090 continue1091 1092 first_nonspace = self.NONSPACECRE.search(line)1093 st.cur_indent_level = first_nonspace.start() if first_nonspace else 01094 1095 if self._handle_continuation_line(st, line, fpname):1096 continue1097 1098 self._handle_rest(st, line, fpname)1099 1100 return st.errors1101 1102 def _handle_continuation_line(self, st, line, fpname):1103 # continuation line?1104 is_continue = (st.cursect is not None and st.optname and1105 st.cur_indent_level > st.indent_level)1106 if is_continue:1107 if st.cursect[st.optname] is None:1108 raise MultilineContinuationError(fpname, st.lineno, line)1109 st.cursect[st.optname].append(line.clean)1110 return is_continue1111 1112 def _handle_rest(self, st, line, fpname):1113 # a section header or option header?1114 if self._allow_unnamed_section and st.cursect is None:1115 self._handle_header(st, UNNAMED_SECTION, fpname)1116 1117 st.indent_level = st.cur_indent_level1118 # is it a section header?1119 mo = self.SECTCRE.match(line.clean)1120 1121 if not mo and st.cursect is None:1122 raise MissingSectionHeaderError(fpname, st.lineno, line)1123 1124 self._handle_header(st, mo.group('header'), fpname) if mo else self._handle_option(st, line, fpname)1125 1126 def _handle_header(self, st, sectname, fpname):1127 st.sectname = sectname1128 if st.sectname in self._sections:1129 if self._strict and st.sectname in st.elements_added:1130 raise DuplicateSectionError(st.sectname, fpname,1131 st.lineno)1132 st.cursect = self._sections[st.sectname]1133 st.elements_added.add(st.sectname)1134 elif st.sectname == self.default_section:1135 st.cursect = self._defaults1136 else:1137 st.cursect = self._dict()1138 self._sections[st.sectname] = st.cursect1139 self._proxies[st.sectname] = SectionProxy(self, st.sectname)1140 st.elements_added.add(st.sectname)1141 # So sections can't start with a continuation line1142 st.optname = None1143 1144 def _handle_option(self, st, line, fpname):1145 # an option line?1146 st.indent_level = st.cur_indent_level1147 1148 mo = self._optcre.match(line.clean)1149 if not mo:1150 # a non-fatal parsing error occurred. set up the1151 # exception but keep going. the exception will be1152 # raised at the end of the file and will contain a1153 # list of all bogus lines1154 st.errors.append(ParsingError(fpname, st.lineno, line))1155 return1156 1157 st.optname, vi, optval = mo.group('option', 'vi', 'value')1158 if not st.optname:1159 st.errors.append(ParsingError(fpname, st.lineno, line))1160 st.optname = self.optionxform(st.optname.rstrip())1161 if (self._strict and1162 (st.sectname, st.optname) in st.elements_added):1163 raise DuplicateOptionError(st.sectname, st.optname,1164 fpname, st.lineno)1165 st.elements_added.add((st.sectname, st.optname))1166 # This check is fine because the OPTCRE cannot1167 # match if it would set optval to None1168 if optval is not None:1169 optval = optval.strip()1170 st.cursect[st.optname] = [optval]1171 else:1172 # valueless option handling1173 st.cursect[st.optname] = None1174 1175 def _join_multiline_values(self):1176 defaults = self.default_section, self._defaults1177 all_sections = itertools.chain((defaults,),1178 self._sections.items())1179 for section, options in all_sections:1180 for name, val in options.items():1181 if isinstance(val, list):1182 val = '\n'.join(val).rstrip()1183 options[name] = self._interpolation.before_read(self,1184 section,1185 name, val)1186 1187 def _read_defaults(self, defaults):1188 """Read the defaults passed in the initializer.1189 Note: values can be non-string."""1190 for key, value in defaults.items():1191 self._defaults[self.optionxform(key)] = value1192 1193 def _unify_values(self, section, vars):1194 """Create a sequence of lookups with 'vars' taking priority over1195 the 'section' which takes priority over the DEFAULTSECT.1196 1197 """1198 sectiondict = {}1199 try:1200 sectiondict = self._sections[section]