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 in
5the style of RFC 822.
6
7Intrinsic defaults can be specified by passing them into the
8ConfigParser constructor as a dictionary.
9
10class:
11
12ConfigParser -- responsible for parsing a list of
13 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 the
24 dictionary or intrinsic defaults. The keys must be strings, the values
25 must be appropriate for %()s string interpolation.
26
27 When `dict_type` is given, it will be used to create the dictionary
28 objects for the list of sections, for the options within a section, and
29 for the default values.
30
31 When `delimiters` is given, it will be used as the set of substrings
32 that divide keys from values.
33
34 When `comment_prefixes` is given, it will be used as the set of
35 substrings that prefix comments in empty lines. Comments can be
36 indented.
37
38 When `inline_comment_prefixes` is given, it will be used as the set of
39 substrings that prefix comments in non-empty lines.
40
41 When `strict` is True, the parser won't allow for any section or option
42 duplicates while reading from a single source (file, string or
43 dictionary). Default is True.
44
45 When `empty_lines_in_values` is False (default: True), each empty line
46 marks the end of an option. Otherwise, internal empty lines of
47 a multiline option are kept as part of the value.
48
49 When `allow_no_value` is True (default: False), options without
50 values are accepted; the value presented for these is None.
51
52 When `default_section` is given, the name of the special section is
53 named accordingly. By default it is called ``"DEFAULT"`` but this can
54 be customized to point to any other valid section name. Its current
55 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 subclass
59 instance. It will be used as the handler for option value
60 pre-processing when using getters. RawConfigParser objects don't do
61 any sort of interpolation, whereas ConfigParser uses an instance of
62 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 key
66 represents the name of a type converter and each value is a callable
67 implementing the conversion from string to the desired datatype. Every
68 converter gets its corresponding get*() method on the parser object and
69 section proxies.
70
71 When `allow_unnamed_section` is True (default: False), options
72 without section are accepted: the section for these is
73 ``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 by
89 name. A single filename is also allowed. Non-existing files
90 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 error
95 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 present
103 in the section. If the used dictionary type preserves order, sections
104 and their keys will be added in order. Values are automatically
105 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 are
109 expanded in the return values, based on the defaults passed into the
110 constructor and the DEFAULT section. Additional substitutions may be
111 provided using the `vars` argument, which must be a dictionary whose
112 contents override any pre-existing defaults. If `option` is a key in
113 `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 case
123 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) for
128 each option in the section. Otherwise, return a list of tuples with
129 (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. If
142 `space_around_delimiters` is True (the default), delimiters
143 between keys and values are surrounded by spaces.
144"""
145
146from __future__ import annotations
147
148import contextlib
149import functools
150import io
151import itertools
152import os
153import re
154import sys
155from collections import ChainMap as _ChainMap
156
157# Do not import dataclasses; overhead is unacceptable (gh-117703)
158from collections.abc import Iterable, MutableMapping
159
160__all__ = (
161 "NoSectionError",
162 "DuplicateOptionError",
163 "DuplicateSectionError",
164 "NoOptionError",
165 "InterpolationError",
166 "InterpolationDepthError",
167 "InterpolationMissingOptionError",
168 "InterpolationSyntaxError",
169 "ParsingError",
170 "MissingSectionHeaderError",
171 "MultilineContinuationError",
172 "UnnamedSectionDisabledError",
173 "InvalidWriteError",
174 "ConfigParser",
175 "RawConfigParser",
176 "Interpolation",
177 "BasicInterpolation",
178 "ExtendedInterpolation",
179 "SectionProxy",
180 "ConverterMapping",
181 "DEFAULTSECT",
182 "MAX_INTERPOLATION_DEPTH",
183 "UNNAMED_SECTION",
184)
185
186_default_dict = dict
187DEFAULTSECT = "DEFAULT"
188
189MAX_INTERPOLATION_DEPTH = 10
190
191
192# exception classes
193class Error(Exception):
194 """Base class for ConfigParser exceptions."""
195
196 def __init__(self, msg=''):
197 self.message = msg
198 Exception.__init__(self, msg)
199
200 def __repr__(self):
201 return self.message
202
203 __str__ = __repr__
204
205
206class NoSectionError(Error):
207 """Raised when no section matches a requested option."""
208
209 def __init__(self, section):
210 Error.__init__(self, 'No section: %r' % (section,))
211 self.section = section
212 self.args = (section,)
213
214
215class DuplicateSectionError(Error):
216 """Raised when a section is repeated in an input source.
217
218 Possible repetitions that raise this exception are: multiple creation
219 using the API or in strict parsers when a section is found more than once
220 in a single input file, string or dictionary.
221 """
222
223 def __init__(self, section, source=None, lineno=None):
224 msg = [repr(section), " already exists"]
225 if source is not None:
226 message = ["While reading from ", repr(source)]
227 if lineno is not None:
228 message.append(" [line {0:2d}]".format(lineno))
229 message.append(": section ")
230 message.extend(msg)
231 msg = message
232 else:
233 msg.insert(0, "Section ")
234 Error.__init__(self, "".join(msg))
235 self.section = section
236 self.source = source
237 self.lineno = lineno
238 self.args = (section, source, lineno)
239
240
241class DuplicateOptionError(Error):
242 """Raised by strict parsers when an option is repeated in an input source.
243
244 Current implementation raises this exception only when an option is found
245 more than once in a single file, string or dictionary.
246 """
247
248 def __init__(self, section, option, source=None, lineno=None):
249 msg = [repr(option), " in section ", repr(section), " already exists"]
250 if source is not None:
251 message = ["While reading from ", repr(source)]
252 if lineno is not None:
253 message.append(" [line {0:2d}]".format(lineno))
254 message.append(": option ")
255 message.extend(msg)
256 msg = message
257 else:
258 msg.insert(0, "Option ")
259 Error.__init__(self, "".join(msg))
260 self.section = section
261 self.option = option
262 self.source = source
263 self.lineno = lineno
264 self.args = (section, option, source, lineno)
265
266
267class NoOptionError(Error):
268 """A requested option was not found."""
269
270 def __init__(self, option, section):
271 Error.__init__(self, "No option %r in section: %r" % (option, section))
272 self.option = option
273 self.section = section
274 self.args = (option, section)
275
276
277class InterpolationError(Error):
278 """Base class for interpolation-related exceptions."""
279
280 def __init__(self, option, section, msg):
281 Error.__init__(self, msg)
282 self.option = option
283 self.section = section
284 self.args = (option, section, msg)
285
286
287class InterpolationMissingOptionError(InterpolationError):
288 """A string substitution required a setting which was not available."""
289
290 def __init__(self, option, section, rawval, reference):
291 msg = (
292 "Bad value substitution: option {!r} in section {!r} contains "
293 "an interpolation key {!r} which is not a valid option name. "
294 "Raw value: {!r}".format(option, section, reference, rawval)
295 )
296 InterpolationError.__init__(self, option, section, msg)
297 self.reference = reference
298 self.args = (option, section, rawval, reference)
299
300
301class InterpolationSyntaxError(InterpolationError):
302 """Raised when the source text contains invalid syntax.
303
304 Current implementation raises this exception when the source text into
305 which substitutions are made does not conform to the required syntax.
306 """
307
308
309class InterpolationDepthError(InterpolationError):
310 """Raised when substitutions are nested too deeply."""
311
312 def __init__(self, option, section, rawval):
313 msg = (
314 "Recursion limit exceeded in value substitution: option {!r} "
315 "in section {!r} contains an interpolation key which "
316 "cannot be substituted in {} steps. Raw value: {!r}"
317 "".format(option, section, MAX_INTERPOLATION_DEPTH, rawval)
318 )
319 InterpolationError.__init__(self, option, section, msg)
320 self.args = (option, section, rawval)
321
322
323class ParsingError(Error):
324 """Raised when a configuration file does not follow legal syntax."""
325
326 def __init__(self, source, *args):
327 super().__init__(f'Source contains parsing errors: {source!r}')
328 self.source = source
329 self.errors = []
330 self.args = (source,)
331 if args:
332 self.append(*args)
333
334 def append(self, lineno, line):
335 self.errors.append((lineno, line))
336 self.message += f'\n\t[line {lineno:2d}]: {line!r}'
337
338 def combine(self, others):
339 messages = [self.message]
340 for other in others:
341 for lineno, line in other.errors:
342 self.errors.append((lineno, line))
343 messages.append(f'\n\t[line {lineno:2d}]: {line!r}')
344 self.message = "".join(messages)
345 return self
346
347 @staticmethod
348 def _raise_all(exceptions: Iterable[ParsingError]):
349 """
350 Combine any number of ParsingErrors into one and raise it.
351 """
352 exceptions = iter(exceptions)
353 with contextlib.suppress(StopIteration):
354 raise next(exceptions).combine(exceptions)
355
356
357class MissingSectionHeaderError(ParsingError):
358 """Raised when a key-value pair is found before any section header."""
359
360 def __init__(self, filename, lineno, line):
361 Error.__init__(
362 self,
363 'File contains no section headers.\nfile: %r, line: %d\n%r'
364 % (filename, lineno, line),
365 )
366 self.source = filename
367 self.lineno = lineno
368 self.line = line
369 self.args = (filename, lineno, line)
370
371
372class MultilineContinuationError(ParsingError):
373 """Raised when a key without value is followed by continuation line"""
374
375 def __init__(self, filename, lineno, line):
376 Error.__init__(
377 self,
378 "Key without value continued with an indented line.\n"
379 "file: %r, line: %d\n%r" % (filename, lineno, line),
380 )
381 self.source = filename
382 self.lineno = lineno
383 self.line = line
384 self.args = (filename, lineno, line)
385
386
387class UnnamedSectionDisabledError(Error):
388 """Raised when an attempt to use UNNAMED_SECTION is made with the
389 feature disabled."""
390
391 def __init__(self):
392 Error.__init__(self, "Support for UNNAMED_SECTION is disabled.")
393
394
395class _UnnamedSection:
396 def __repr__(self):
397 return "<UNNAMED_SECTION>"
398
399
400class InvalidWriteError(Error):
401 """Raised when attempting to write data that the parser would read back differently.
402 ex: writing a key which begins with the section header pattern would read back as a
403 new section"""
404
405 def __init__(self, msg=''):
406 Error.__init__(self, msg)
407
408
409UNNAMED_SECTION = _UnnamedSection()
410
411
412# Used in parser getters to indicate the default behaviour when a specific
413# option is not found it to raise an exception. Created to enable `None` as
414# a valid fallback value.
415_UNSET = object()
416
417
418class Interpolation:
419 """Dummy interpolation that passes the value through with no changes."""
420
421 def before_get(self, parser, section, option, value, defaults):
422 return value
423
424 def before_set(self, parser, section, option, value):
425 return value
426
427 def before_read(self, parser, section, option, value):
428 return value
429
430 def before_write(self, parser, section, option, value):
431 return value
432
433
434class BasicInterpolation(Interpolation):
435 """Interpolation as implemented in the classic ConfigParser.
436
437 The option values can contain format strings which refer to other values in
438 the same section, or values in the special default section.
439
440 For example:
441
442 something: %(dir)s/whatever
443
444 would resolve the "%(dir)s" to the value of dir. All reference
445 expansions are done late, on demand. If a user needs to use a bare % in
446 a configuration file, she can escape it by writing %%. Other % usage
447 is considered a user error and raises `InterpolationSyntaxError`."""
448
449 _KEYCRE = re.compile(r"%\(([^)]+)\)s")
450
451 def before_get(self, parser, section, option, value, defaults):
452 L = []
453 self._interpolate_some(parser, option, L, value, section, defaults, 1)
454 return ''.join(L)
455
456 def before_set(self, parser, section, option, value):
457 tmp_value = value.replace('%%', '') # escaped percent signs
458 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
459 if '%' in tmp_value:
460 raise ValueError(
461 "invalid interpolation syntax in %r at "
462 "position %d" % (value, tmp_value.find('%'))
463 )
464 return value
465
466 def _interpolate_some(self, parser, option, accum, rest, section, map, depth):
467 rawval = parser.get(section, option, raw=True, fallback=rest)
468 if depth > MAX_INTERPOLATION_DEPTH:
469 raise InterpolationDepthError(option, section, rawval)
470 while rest:
471 p = rest.find("%")
472 if p < 0:
473 accum.append(rest)
474 return
475 if p > 0:
476 accum.append(rest[:p])
477 rest = rest[p:]
478 # p is no longer used
479 c = rest[1:2]
480 if c == "%":
481 accum.append("%")
482 rest = rest[2:]
483 elif c == "(":
484 m = self._KEYCRE.match(rest)
485 if m is None:
486 raise InterpolationSyntaxError(
487 option,
488 section,
489 "bad interpolation variable reference %r" % rest,
490 )
491 var = parser.optionxform(m.group(1))
492 rest = rest[m.end() :]
493 try:
494 v = map[var]
495 except KeyError:
496 raise InterpolationMissingOptionError(
497 option, section, rawval, var
498 ) from None
499 if "%" in v:
500 self._interpolate_some(
501 parser, option, accum, v, section, map, depth + 1
502 )
503 else:
504 accum.append(v)
505 else:
506 raise InterpolationSyntaxError(
507 option,
508 section,
509 "'%%' must be followed by '%%' or '(', found: %r" % (rest,),
510 )
511
512
513class ExtendedInterpolation(Interpolation):
514 """Advanced variant of interpolation, supports the syntax used by
515 `zc.buildout`. Enables interpolation between sections."""
516
517 _KEYCRE = re.compile(r"\$\{([^}]+)\}")
518
519 def before_get(self, parser, section, option, value, defaults):
520 L = []
521 self._interpolate_some(parser, option, L, value, section, defaults, 1)
522 return ''.join(L)
523
524 def before_set(self, parser, section, option, value):
525 tmp_value = value.replace('$$', '') # escaped dollar signs
526 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
527 if '$' in tmp_value:
528 raise ValueError(
529 "invalid interpolation syntax in %r at "
530 "position %d" % (value, tmp_value.find('$'))
531 )
532 return value
533
534 def _interpolate_some( # noqa: C901
535 self, parser, option, accum, rest, section, map, depth
536 ):
537 rawval = parser.get(section, option, raw=True, fallback=rest)
538 if depth > MAX_INTERPOLATION_DEPTH:
539 raise InterpolationDepthError(option, section, rawval)
540 while rest:
541 p = rest.find("$")
542 if p < 0:
543 accum.append(rest)
544 return
545 if p > 0:
546 accum.append(rest[:p])
547 rest = rest[p:]
548 # p is no longer used
549 c = rest[1:2]
550 if c == "$":
551 accum.append("$")
552 rest = rest[2:]
553 elif c == "{":
554 m = self._KEYCRE.match(rest)
555 if m is None:
556 raise InterpolationSyntaxError(
557 option,
558 section,
559 "bad interpolation variable reference %r" % rest,
560 )
561 path = m.group(1).split(':')
562 rest = rest[m.end() :]
563 sect = section
564 opt = option
565 try:
566 if len(path) == 1:
567 opt = parser.optionxform(path[0])
568 v = map[opt]
569 elif len(path) == 2:
570 sect = path[0]
571 opt = parser.optionxform(path[1])
572 v = parser.get(sect, opt, raw=True)
573 else:
574 raise InterpolationSyntaxError(
575 option, section, "More than one ':' found: %r" % (rest,)
576 )
577 except (KeyError, NoSectionError, NoOptionError):
578 raise InterpolationMissingOptionError(
579 option, section, rawval, ":".join(path)
580 ) from None
581 if v is None:
582 continue
583 if "$" in v:
584 self._interpolate_some(
585 parser,
586 opt,
587 accum,
588 v,
589 sect,
590 dict(parser.items(sect, raw=True)),
591 depth + 1,
592 )
593 else:
594 accum.append(v)
595 else:
596 raise InterpolationSyntaxError(
597 option,
598 section,
599 "'$' must be followed by '$' or '{', found: %r" % (rest,),
600 )
601
602
603class _ReadState:
604 elements_added: set[str]
605 cursect: dict[str, str] | None = None
606 sectname: str | None = None
607 optname: str | None = None
608 lineno: int = 0
609 indent_level: int = 0
610 errors: list[ParsingError]
611
612 def __init__(self):
613 self.elements_added = set()
614 self.errors = list()
615
616
617class _Line(str):
618 __slots__ = 'clean', 'has_comments'
619
620 def __new__(cls, val, *args, **kwargs):
621 return super().__new__(cls, val)
622
623 def __init__(self, val, comments):
624 trimmed = val.strip()
625 self.clean = comments.strip(trimmed)
626 self.has_comments = trimmed != self.clean
627
628
629class _CommentSpec:
630 def __init__(self, full_prefixes, inline_prefixes):
631 full_patterns = (
632 # prefix at the beginning of a line
633 rf'^({re.escape(prefix)}).*'
634 for prefix in full_prefixes
635 )
636 inline_patterns = (
637 # prefix at the beginning of the line or following a space
638 rf'(^|\s)({re.escape(prefix)}.*)'
639 for prefix in inline_prefixes
640 )
641 self.pattern = re.compile(
642 '|'.join(itertools.chain(full_patterns, inline_patterns))
643 )
644
645 def strip(self, text):
646 return self.pattern.sub('', text).rstrip()
647
648 def wrap(self, text):
649 return _Line(text, self)
650
651
652class RawConfigParser(MutableMapping):
653 """ConfigParser that does not do interpolation."""
654
655 # Regular expressions for parsing section headers and options
656 _SECT_TMPL = r"""
657 \[ # [
658 (?P<header>.+) # very permissive!
659 \] # ]
660 """
661 _OPT_TMPL = r"""
662 (?P<option> # very permissive!
663 (?:(?!{delim})\S)* # non-delimiter non-whitespace
664 (?:(?:(?!{delim})\s)+ # optionally more
665 (?:(?!{delim})\S)+)*) # space-separated words
666 \s*(?P<vi>{delim})\s* # any number of space/tab,
667 # followed by any of the
668 # allowed delimiters,
669 # followed by any space/tab
670 (?P<value>.*)$ # everything up to eol
671 """
672 _OPT_NV_TMPL = r"""
673 (?P<option> # very permissive!
674 (?:(?!{delim})\S)* # non-delimiter non-whitespace
675 (?:(?:(?!{delim})\s)+ # optionally more
676 (?:(?!{delim})\S)+)*) # space-separated words
677 \s*(?: # any number of space/tab,
678 (?P<vi>{delim})\s* # optionally followed by
679 # any of the allowed
680 # delimiters, followed by any
681 # space/tab
682 (?P<value>.*))?$ # everything up to eol
683 """
684 # Interpolation algorithm to be used if the user does not specify another
685 _DEFAULT_INTERPOLATION = Interpolation()
686 # Compiled regular expression for matching sections
687 SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)
688 # Compiled regular expression for matching options with typical separators
689 OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE)
690 # Compiled regular expression for matching options with optional values
691 # delimited using typical separators
692 OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE)
693 # Compiled regular expression for matching leading whitespace in a line
694 NONSPACECRE = re.compile(r"\S")
695 # Possible boolean values in the configuration.
696 BOOLEAN_STATES = {
697 '1': True,
698 'yes': True,
699 'true': True,
700 'on': True,
701 '0': False,
702 'no': False,
703 'false': False,
704 'off': False,
705 }
706
707 def __init__(
708 self,
709 defaults=None,
710 dict_type=_default_dict,
711 allow_no_value=False,
712 *,
713 delimiters=('=', ':'),
714 comment_prefixes=('#', ';'),
715 inline_comment_prefixes=None,
716 strict=True,
717 empty_lines_in_values=True,
718 default_section=DEFAULTSECT,
719 interpolation=_UNSET,
720 converters=_UNSET,
721 allow_unnamed_section=False,
722 ):
723 self._dict = dict_type
724 self._sections = self._dict()
725 self._defaults = self._dict()
726 self._converters = ConverterMapping(self)
727 self._proxies = self._dict()
728 self._proxies[default_section] = SectionProxy(self, default_section)
729 self._delimiters = tuple(delimiters)
730 if delimiters == ('=', ':'):
731 self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE
732 else:
733 d = "|".join(re.escape(d) for d in delimiters)
734 if allow_no_value:
735 self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), re.VERBOSE)
736 else:
737 self._optcre = re.compile(self._OPT_TMPL.format(delim=d), re.VERBOSE)
738 self._comments = _CommentSpec(
739 comment_prefixes or (), inline_comment_prefixes or ()
740 )
741 self._strict = strict
742 self._allow_no_value = allow_no_value
743 self._empty_lines_in_values = empty_lines_in_values
744 self.default_section = default_section
745 self._interpolation = interpolation
746 if self._interpolation is _UNSET:
747 self._interpolation = self._DEFAULT_INTERPOLATION
748 if self._interpolation is None:
749 self._interpolation = Interpolation()
750 if not isinstance(self._interpolation, Interpolation):
751 raise TypeError(
752 f"interpolation= must be None or an instance of Interpolation;"
753 f" got an object of type {type(self._interpolation)}"
754 )
755 if converters is not _UNSET:
756 self._converters.update(converters)
757 if defaults:
758 self._read_defaults(defaults)
759 self._allow_unnamed_section = allow_unnamed_section
760
761 def defaults(self):
762 return self._defaults
763
764 def sections(self):
765 """Return a list of section names, excluding [DEFAULT]"""
766 # self._sections will never have [DEFAULT] in it
767 return list(self._sections.keys())
768
769 def add_section(self, section):
770 """Create a new section in the configuration.
771
772 Raise DuplicateSectionError if a section by the specified name
773 already exists. Raise ValueError if name is DEFAULT.
774 """
775 if section == self.default_section:
776 raise ValueError('Invalid section name: %r' % section)
777
778 if section is UNNAMED_SECTION:
779 if not self._allow_unnamed_section:
780 raise UnnamedSectionDisabledError
781
782 if section in self._sections:
783 raise DuplicateSectionError(section)
784 self._sections[section] = self._dict()
785 self._proxies[section] = SectionProxy(self, section)
786
787 def has_section(self, section):
788 """Indicate whether the named section is present in the configuration.
789
790 The DEFAULT section is not acknowledged.
791 """
792 return section in self._sections
793
794 def options(self, section):
795 """Return a list of option names for the given section name."""
796 try:
797 opts = self._sections[section].copy()
798 except KeyError:
799 raise NoSectionError(section) from None
800 opts.update(self._defaults)
801 return list(opts.keys())
802
803 def read(self, filenames, encoding=None):
804 """Read and parse a filename or an iterable of filenames.
805
806 Files that cannot be opened are silently ignored; this is
807 designed so that you can specify an iterable of potential
808 configuration file locations (e.g. current directory, user's
809 home directory, systemwide directory), and all existing
810 configuration files in the iterable will be read. A single
811 filename may also be given.
812
813 Return list of successfully read files.
814 """
815 if isinstance(filenames, (str, bytes, os.PathLike)):
816 filenames = [filenames]
817 encoding = io.text_encoding(encoding)
818 read_ok = []
819 for filename in filenames:
820 try:
821 with open(filename, encoding=encoding) as fp:
822 self._read(fp, filename)
823 except OSError:
824 continue
825 if isinstance(filename, os.PathLike):
826 filename = os.fspath(filename)
827 read_ok.append(filename)
828 return read_ok
829
830 def read_file(self, f, source=None):
831 """Like read() but the argument must be a file-like object.
832
833 The `f` argument must be iterable, returning one line at a time.
834 Optional second argument is the `source` specifying the name of the
835 file being read. If not given, it is taken from f.name. If `f` has no
836 `name` attribute, `<???>` is used.
837 """
838 if source is None:
839 try:
840 source = f.name
841 except AttributeError:
842 source = '<???>'
843 self._read(f, source)
844
845 def read_string(self, string, source='<string>'):
846 """Read configuration from a given string."""
847 sfile = io.StringIO(string)
848 self.read_file(sfile, source)
849
850 def read_dict(self, dictionary, source='<dict>'):
851 """Read configuration from a dictionary.
852
853 Keys are section names, values are dictionaries with keys and values
854 that should be present in the section. If the used dictionary type
855 preserves order, sections and their keys will be added in order.
856
857 All types held in the dictionary are converted to strings during
858 reading, including section names, option names and keys.
859
860 Optional second argument is the `source` specifying the name of the
861 dictionary being read.
862 """
863 elements_added = set()
864 for section, keys in dictionary.items():
865 if section is not UNNAMED_SECTION:
866 section = str(section)
867 try:
868 self.add_section(section)
869 except (DuplicateSectionError, ValueError):
870 if self._strict and section in elements_added:
871 raise
872 elements_added.add(section)
873 for key, value in keys.items():
874 key = self.optionxform(str(key))
875 if value is not None:
876 value = str(value)
877 if self._strict and (section, key) in elements_added:
878 raise DuplicateOptionError(section, key, source)
879 elements_added.add((section, key))
880 self.set(section, key, value)
881
882 def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
883 """Get an option value for a given section.
884
885 If `vars` is provided, it must be a dictionary. The option is looked up
886 in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order.
887 If the key is not found and `fallback` is provided, it is used as
888 a fallback value. `None` can be provided as a `fallback` value.
889
890 If interpolation is enabled and the optional argument `raw` is False,
891 all interpolations are expanded in the return values.
892
893 Arguments `raw`, `vars`, and `fallback` are keyword only.
894
895 The section DEFAULT is special.
896 """
897 try:
898 d = self._unify_values(section, vars)
899 except NoSectionError:
900 if fallback is _UNSET:
901 raise
902 else:
903 return fallback
904 option = self.optionxform(option)
905 try:
906 value = d[option]
907 except KeyError:
908 if fallback is _UNSET:
909 raise NoOptionError(option, section)
910 else:
911 return fallback
912
913 if raw or value is None:
914 return value
915 else:
916 return self._interpolation.before_get(self, section, option, value, d)
917
918 def _get(self, section, conv, option, **kwargs):
919 return conv(self.get(section, option, **kwargs))
920
921 def _get_conv(
922 self, section, option, conv, *, raw=False, vars=None, fallback=_UNSET, **kwargs
923 ):
924 try:
925 return self._get(section, conv, option, raw=raw, vars=vars, **kwargs)
926 except (NoSectionError, NoOptionError):
927 if fallback is _UNSET:
928 raise
929 return fallback
930
931 # getint, getfloat and getboolean provided directly for backwards compat
932 def getint(
933 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs
934 ):
935 return self._get_conv(
936 section, option, int, raw=raw, vars=vars, fallback=fallback, **kwargs
937 )
938
939 def getfloat(
940 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs
941 ):
942 return self._get_conv(
943 section, option, float, raw=raw, vars=vars, fallback=fallback, **kwargs
944 )
945
946 def getboolean(
947 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs
948 ):
949 return self._get_conv(
950 section,
951 option,
952 self._convert_to_boolean,
953 raw=raw,
954 vars=vars,
955 fallback=fallback,
956 **kwargs,
957 )
958
959 def items(self, section=_UNSET, raw=False, vars=None):
960 """Return a list of (name, value) tuples for each option in a section.
961
962 All % interpolations are expanded in the return values, based on the
963 defaults passed into the constructor, unless the optional argument
964 `raw` is true. Additional substitutions may be provided using the
965 `vars` argument, which must be a dictionary whose contents overrides
966 any pre-existing defaults.
967
968 The section DEFAULT is special.
969 """
970 if section is _UNSET:
971 return super(RawConfigParser, self).items()
972 d = self._defaults.copy()
973 try:
974 d.update(self._sections[section])
975 except KeyError:
976 if section != self.default_section:
977 raise NoSectionError(section)
978 orig_keys = list(d.keys())
979 # Update with the entry specific variables
980 if vars:
981 for key, value in vars.items():
982 d[self.optionxform(key)] = value
983
984 def value_getter_interp(option):
985 return self._interpolation.before_get(self, section, option, d[option], d)
986
987 def value_getter_raw(option):
988 return d[option]
989
990 value_getter = value_getter_raw if raw else value_getter_interp
991
992 return [(option, value_getter(option)) for option in orig_keys]
993
994 def popitem(self):
995 """Remove a section from the parser and return it as
996 a (section_name, section_proxy) tuple. If no section is present, raise
997 KeyError.
998
999 The section DEFAULT is never returned because it cannot be removed.
1000 """
1001 for key in self.sections():
1002 value = self[key]
1003 del self[key]
1004 return key, value
1005 raise KeyError
1006
1007 def optionxform(self, optionstr):
1008 return optionstr.lower()
1009
1010 def has_option(self, section, option):
1011 """Check for the existence of a given option in a given section.
1012 If the specified `section` is None or an empty string, DEFAULT is
1013 assumed. If the specified `section` does not exist, returns False."""
1014 if not section or section == self.default_section:
1015 option = self.optionxform(option)
1016 return option in self._defaults
1017 elif section not in self._sections:
1018 return False
1019 else:
1020 option = self.optionxform(option)
1021 return option in self._sections[section] or option in self._defaults
1022
1023 def set(self, section, option, value=None):
1024 """Set an option."""
1025 if value:
1026 value = self._interpolation.before_set(self, section, option, value)
1027 if not section or section == self.default_section:
1028 sectdict = self._defaults
1029 else:
1030 try:
1031 sectdict = self._sections[section]
1032 except KeyError:
1033 raise NoSectionError(section) from None
1034 sectdict[self.optionxform(option)] = value
1035
1036 def write(self, fp, space_around_delimiters=True):
1037 """Write an .ini-format representation of the configuration state.
1038
1039 If `space_around_delimiters` is True (the default), delimiters
1040 between keys and values are surrounded by spaces.
1041
1042 Please note that comments in the original configuration file are not
1043 preserved when writing the configuration back.
1044 """
1045 if space_around_delimiters:
1046 d = " {} ".format(self._delimiters[0])
1047 else:
1048 d = self._delimiters[0]
1049 if self._defaults:
1050 self._write_section(fp, self.default_section, self._defaults.items(), d)
1051 if UNNAMED_SECTION in self._sections and self._sections[UNNAMED_SECTION]:
1052 self._write_section(
1053 fp,
1054 UNNAMED_SECTION,
1055 self._sections[UNNAMED_SECTION].items(),
1056 d,
1057 unnamed=True,
1058 )
1059
1060 for section in self._sections:
1061 if section is UNNAMED_SECTION:
1062 continue
1063 self._write_section(fp, section, self._sections[section].items(), d)
1064
1065 def _write_section(self, fp, section_name, section_items, delimiter, unnamed=False):
1066 """Write a single section to the specified 'fp'."""
1067 if not unnamed:
1068 fp.write("[{}]\n".format(section_name))
1069 for key, value in section_items:
1070 self._validate_key_contents(key)
1071 value = self._interpolation.before_write(self, section_name, key, value)
1072 if value is not None or not self._allow_no_value:
1073 # Convert all possible line-endings into '\n\t'
1074 value = delimiter + str(value).replace('\r\n', '\n').replace(
1075 '\r', '\n'
1076 ).replace('\n', '\n\t')
1077 else:
1078 value = ""
1079 fp.write("{}{}\n".format(key, value))
1080 fp.write("\n")
1081
1082 def remove_option(self, section, option):
1083 """Remove an option."""
1084 if not section or section == self.default_section:
1085 sectdict = self._defaults
1086 else:
1087 try:
1088 sectdict = self._sections[section]
1089 except KeyError:
1090 raise NoSectionError(section) from None
1091 option = self.optionxform(option)
1092 existed = option in sectdict
1093 if existed:
1094 del sectdict[option]
1095 return existed
1096
1097 def remove_section(self, section):
1098 """Remove a file section."""
1099 existed = section in self._sections
1100 if existed:
1101 del self._sections[section]
1102 del self._proxies[section]
1103 return existed
1104
1105 def __getitem__(self, key):
1106 if key != self.default_section and not self.has_section(key):
1107 raise KeyError(key)
1108 return self._proxies[key]
1109
1110 def __setitem__(self, key, value):
1111 # To conform with the mapping protocol, overwrites existing values in
1112 # the section.
1113 if key in self and self[key] is value:
1114 return
1115 # XXX this is not atomic if read_dict fails at any point. Then again,
1116 # no update method in configparser is atomic in this implementation.
1117 if key == self.default_section:
1118 self._defaults.clear()
1119 elif key in self._sections:
1120 self._sections[key].clear()
1121 self.read_dict({key: value})
1122
1123 def __delitem__(self, key):
1124 if key == self.default_section:
1125 raise ValueError("Cannot remove the default section.")
1126 if not self.has_section(key):
1127 raise KeyError(key)
1128 self.remove_section(key)
1129
1130 def __contains__(self, key):
1131 return key == self.default_section or self.has_section(key)
1132
1133 def __len__(self):
1134 return len(self._sections) + 1 # the default section
1135
1136 def __iter__(self):
1137 # XXX does it break when underlying container state changed?
1138 return itertools.chain((self.default_section,), self._sections.keys())
1139
1140 def _read(self, fp, fpname):
1141 """Parse a sectioned configuration file.
1142
1143 Each section in a configuration file contains a header, indicated by
1144 a name in square brackets (`[]`), plus key/value options, indicated by
1145 `name` and `value` delimited with a specific substring (`=` or `:` by
1146 default).
1147
1148 Values can span multiple lines, as long as they are indented deeper
1149 than the first line of the value. Depending on the parser's mode, blank
1150 lines may be treated as parts of multiline values or ignored.
1151
1152 Configuration files may include comments, prefixed by specific
1153 characters (`#` and `;` by default). Comments may appear on their own
1154 in an otherwise empty line or may be entered in lines holding values or
1155 section names. Please note that comments get stripped off when reading
1156 configuration files.
1157 """
1158 try:
1159 ParsingError._raise_all(self._read_inner(fp, fpname))
1160 finally:
1161 self._join_multiline_values()
1162
1163 def _read_inner(self, fp, fpname):
1164 st = _ReadState()
1165
1166 for st.lineno, line in enumerate(map(self._comments.wrap, fp), start=1):
1167 if not line.clean:
1168 if self._empty_lines_in_values:
1169 # add empty line to the value, but only if there was no
1170 # comment on the line
1171 if (
1172 not line.has_comments
1173 and st.cursect is not None
1174 and st.optname
1175 and st.cursect[st.optname] is not None
1176 ):
1177 st.cursect[st.optname].append('') # newlines added at join
1178 else:
1179 # empty line marks end of value
1180 st.indent_level = sys.maxsize
1181 continue
1182
1183 first_nonspace = self.NONSPACECRE.search(line)
1184 st.cur_indent_level = first_nonspace.start() if first_nonspace else 0
1185
1186 if self._handle_continuation_line(st, line, fpname):
1187 continue
1188
1189 self._handle_rest(st, line, fpname)
1190
1191 return st.errors
1192
1193 def _handle_continuation_line(self, st, line, fpname):
1194 # continuation line?
1195 is_continue = (
1196 st.cursect is not None
1197 and st.optname
1198 and st.cur_indent_level > st.indent_level
1199 )
1200 if is_continue:
1201 if st.cursect[st.optname] is None:
1202 raise MultilineContinuationError(fpname, st.lineno, line)
1203 st.cursect[st.optname].append(line.clean)
1204 return is_continue
1205
1206 def _handle_rest(self, st, line, fpname):
1207 # a section header or option header?
1208 if self._allow_unnamed_section and st.cursect is None:
1209 self._handle_header(st, UNNAMED_SECTION, fpname)
1210
1211 st.indent_level = st.cur_indent_level
1212 # is it a section header?
1213 mo = self.SECTCRE.match(line.clean)
1214
1215 if not mo and st.cursect is None:
1216 raise MissingSectionHeaderError(fpname, st.lineno, line)
1217
1218 self._handle_header(
1219 st, mo.group('header'), fpname
1220 ) if mo else self._handle_option(st, line, fpname)
1221
1222 def _handle_header(self, st, sectname, fpname):
1223 st.sectname = sectname
1224 if st.sectname in self._sections:
1225 if self._strict and st.sectname in st.elements_added:
1226 raise DuplicateSectionError(st.sectname, fpname, st.lineno)
1227 st.cursect = self._sections[st.sectname]
1228 st.elements_added.add(st.sectname)
1229 elif st.sectname == self.default_section:
1230 st.cursect = self._defaults
1231 else:
1232 st.cursect = self._dict()
1233 self._sections[st.sectname] = st.cursect
1234 self._proxies[st.sectname] = SectionProxy(self, st.sectname)
1235 st.elements_added.add(st.sectname)
1236 # So sections can't start with a continuation line
1237 st.optname = None
1238
1239 def _handle_option(self, st, line, fpname):
1240 # an option line?
1241 st.indent_level = st.cur_indent_level
1242
1243 mo = self._optcre.match(line.clean)
1244 if not mo:
1245 # a non-fatal parsing error occurred. set up the
1246 # exception but keep going. the exception will be
1247 # raised at the end of the file and will contain a
1248 # list of all bogus lines
1249 st.errors.append(ParsingError(fpname, st.lineno, line))
1250 return
1251
1252 st.optname, vi, optval = mo.group('option', 'vi', 'value')
1253 if not st.optname:
1254 st.errors.append(ParsingError(fpname, st.lineno, line))
1255 st.optname = self.optionxform(st.optname.rstrip())
1256 if self._strict and (st.sectname, st.optname) in st.elements_added:
1257 raise DuplicateOptionError(st.sectname, st.optname, fpname, st.lineno)
1258 st.elements_added.add((st.sectname, st.optname))
1259 # This check is fine because the OPTCRE cannot
1260 # match if it would set optval to None
1261 if optval is not None:
1262 optval = optval.strip()
1263 st.cursect[st.optname] = [optval]
1264 else:
1265 # valueless option handling
1266 st.cursect[st.optname] = None
1267
1268 def _join_multiline_values(self):
1269 defaults = self.default_section, self._defaults
1270 all_sections = itertools.chain((defaults,), self._sections.items())
1271 for section, options in all_sections:
1272 for name, val in options.items():
1273 if isinstance(val, list):
1274 val = '\n'.join(val).rstrip()
1275 options[name] = self._interpolation.before_read(
1276 self, section, name, val
1277 )
1278
1279 def _read_defaults(self, defaults):
1280 """Read the defaults passed in the initializer.
1281 Note: values can be non-string."""
1282 for key, value in defaults.items():
1283 self._defaults[self.optionxform(key)] = value
1284
1285 def _unify_values(self, section, vars):
1286 """Create a sequence of lookups with 'vars' taking priority over
1287 the 'section' which takes priority over the DEFAULTSECT.
1288
1289 """
1290 sectiondict = {}
1291 try:
1292 sectiondict = self._sections[section]
1293 except KeyError:
1294 if section != self.default_section:
1295 raise NoSectionError(section)
1296 # Update with the entry specific variables
1297 vardict = {}
1298 if vars:
1299 for key, value in vars.items():
1300 if value is not None:
1301 value = str(value)
1302 vardict[self.optionxform(key)] = value
1303 return _ChainMap(vardict, sectiondict, self._defaults)
1304
1305 def _convert_to_boolean(self, value):
1306 """Return a boolean value translating from other types if necessary."""
1307 if value.lower() not in self.BOOLEAN_STATES:
1308 raise ValueError('Not a boolean: %s' % value)
1309 return self.BOOLEAN_STATES[value.lower()]
1310
1311 def _validate_key_contents(self, key):
1312 """Raises an InvalidWriteError for any keys containing
1313 delimiters or that begins with the section header pattern"""
1314 if re.match(self.SECTCRE, key):
1315 raise InvalidWriteError(
1316 f"Cannot write key {key}; begins with section pattern"
1317 )
1318 for delim in self._delimiters:
1319 if delim in key:
1320 raise InvalidWriteError(
1321 f"Cannot write key {key}; contains delimiter {delim}"
1322 )
1323
1324 def _validate_value_types(self, *, section="", option="", value=""):
1325 """Raises a TypeError for illegal non-string values.
1326
1327 Legal non-string values are UNNAMED_SECTION and falsey values if
1328 they are allowed.
1329
1330 For compatibility reasons this method is not used in classic set()
1331 for RawConfigParsers. It is invoked in every case for mapping protocol
1332 access and in ConfigParser.set().
1333 """
1334 if section is UNNAMED_SECTION:
1335 if not self._allow_unnamed_section:
1336 raise UnnamedSectionDisabledError
1337 elif not isinstance(section, str):
1338 raise TypeError("section names must be strings or UNNAMED_SECTION")
1339 if not isinstance(option, str):
1340 raise TypeError("option keys must be strings")
1341 if not self._allow_no_value or value:
1342 if not isinstance(value, str):
1343 raise TypeError("option values must be strings")
1344
1345 @property
1346 def converters(self):
1347 return self._converters
1348
1349
1350class ConfigParser(RawConfigParser):
1351 """ConfigParser implementing interpolation."""
1352
1353 _DEFAULT_INTERPOLATION = BasicInterpolation()
1354
1355 def set(self, section, option, value=None):
1356 """Set an option. Extends RawConfigParser.set by validating type and
1357 interpolation syntax on the value."""
1358 self._validate_value_types(option=option, value=value)
1359 super().set(section, option, value)
1360
1361 def add_section(self, section):
1362 """Create a new section in the configuration. Extends
1363 RawConfigParser.add_section by validating if the section name is
1364 a string."""
1365 self._validate_value_types(section=section)
1366 super().add_section(section)
1367
1368 def _read_defaults(self, defaults):
1369 """Reads the defaults passed in the initializer, implicitly converting
1370 values to strings like the rest of the API.
1371
1372 Does not perform interpolation for backwards compatibility.
1373 """
1374 try:
1375 hold_interpolation = self._interpolation
1376 self._interpolation = Interpolation()
1377 self.read_dict({self.default_section: defaults})
1378 finally:
1379 self._interpolation = hold_interpolation
1380
1381
1382class SectionProxy(MutableMapping):
1383 """A proxy for a single section from a parser."""
1384
1385 def __init__(self, parser, name):
1386 """Creates a view on a section of the specified `name` in `parser`."""
1387 self._parser = parser
1388 self._name = name
1389 for conv in parser.converters:
1390 key = 'get' + conv
1391 getter = functools.partial(self.get, _impl=getattr(parser, key))
1392 setattr(self, key, getter)
1393
1394 def __repr__(self):
1395 return '<Section: {}>'.format(self._name)
1396
1397 def __getitem__(self, key):
1398 if not self._parser.has_option(self._name, key):
1399 raise KeyError(key)
1400 return self._parser.get(self._name, key)
1401
1402 def __setitem__(self, key, value):
1403 self._parser._validate_value_types(option=key, value=value)
1404 return self._parser.set(self._name, key, value)
1405
1406 def __delitem__(self, key):
1407 if not (
1408 self._parser.has_option(self._name, key)
1409 and self._parser.remove_option(self._name, key)
1410 ):
1411 raise KeyError(key)
1412
1413 def __contains__(self, key):
1414 return self._parser.has_option(self._name, key)
1415
1416 def __len__(self):
1417 return len(self._options())
1418
1419 def __iter__(self):
1420 return self._options().__iter__()
1421
1422 def _options(self):
1423 if self._name != self._parser.default_section:
1424 return self._parser.options(self._name)
1425 else:
1426 return self._parser.defaults()
1427
1428 @property
1429 def parser(self):
1430 # The parser object of the proxy is read-only.
1431 return self._parser
1432
1433 @property
1434 def name(self):
1435 # The name of the section on a proxy is read-only.
1436 return self._name
1437
1438 def get(self, option, fallback=None, *, raw=False, vars=None, _impl=None, **kwargs):
1439 """Get an option value.
1440
1441 Unless `fallback` is provided, `None` will be returned if the option
1442 is not found.
1443
1444 """
1445 # If `_impl` is provided, it should be a getter method on the parser
1446 # object that provides the desired type conversion.
1447 if not _impl:
1448 _impl = self._parser.get
1449 return _impl(
1450 self._name, option, raw=raw, vars=vars, fallback=fallback, **kwargs
1451 )
1452
1453
1454class ConverterMapping(MutableMapping):
1455 """Enables reuse of get*() methods between the parser and section proxies.
1456
1457 If a parser class implements a getter directly, the value for the given
1458 key will be ``None``. The presence of the converter name here enables
1459 section proxies to find and use the implementation on the parser class.
1460 """
1461
1462 GETTERCRE = re.compile(r"^get(?P<name>.+)$")
1463
1464 def __init__(self, parser):
1465 self._parser = parser
1466 self._data = {}
1467 for getter in dir(self._parser):
1468 m = self.GETTERCRE.match(getter)
1469 if not m or not callable(getattr(self._parser, getter)):
1470 continue
1471 self._data[m.group('name')] = None # See class docstring.
1472
1473 def __getitem__(self, key):
1474 return self._data[key]
1475
1476 def __setitem__(self, key, value):
1477 try:
1478 k = 'get' + key
1479 except TypeError:
1480 raise ValueError('Incompatible key: {} (type: {})'.format(key, type(key)))
1481 if k == 'get':
1482 raise ValueError('Incompatible key: cannot use "" as a name')
1483 self._data[key] = value
1484 func = functools.partial(self._parser._get_conv, conv=value)
1485 func.converter = value
1486 setattr(self._parser, k, func)
1487 for proxy in self._parser.values():
1488 getter = functools.partial(proxy.get, _impl=func)
1489 setattr(proxy, k, getter)
1490
1491 def __delitem__(self, key):
1492 try:
1493 k = 'get' + (key or None)
1494 except TypeError:
1495 raise KeyError(key)
1496 del self._data[key]
1497 for inst in itertools.chain((self._parser,), self._parser.values()):
1498 try:
1499 delattr(inst, k)
1500 except AttributeError:
1501 # don't raise since the entry was present in _data, silently
1502 # clean up
1503 continue
1504
1505 def __iter__(self):
1506 return iter(self._data)
1507
1508 def __len__(self):
1509 return len(self._data)