https://github.com/python/cpython/commit/58bde1adc289ef478d10d0aac732873d4e1bd469 commit: 58bde1adc289ef478d10d0aac732873d4e1bd469 branch: main author: Jason R. Coombs <[email protected]> committer: jaraco <[email protected]> date: 2026-10-22T17:20:59Z summary:
gh-160139: Format `configparser` and its tests with `ruff format` (#160265) * gh-160139: Format `configparser` and its tests with `ruff format` `Lib/configparser.py` and `Lib/test/test_configparser.py` are synced periodically with the `configparser` backport on PyPI, which runs `ruff format`. Nearly all of the divergence between the two copies was line wrapping and call-argument layout, reconciled by hand on every sync, and an upstream change landing inside a block the backport had rewrapped surfaced as a conflict even when the change itself was trivial. Run `ruff format` with the settings the backport uses -- preview style, `quote-style = "preserve"` and ruff's default line length -- so that both copies agree on layout and the sync carries only the genuine compatibility deltas. No behavior changes. Co-Authored-By: Claude Opus 5 <[email protected]> * gh-160139: Wrap the two lines `ruff format` leaves long The formatter does not rewrap comments or docstring text, so a docstring line in `_read` and a commented-out assertion in `test_no_first_section` stayed over the line length. Wrap both by hand; no text changes. Co-Authored-By: Claude Opus 5 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]> files: M Lib/configparser.py M Lib/test/test_configparser.py diff --git a/Lib/configparser.py b/Lib/configparser.py index 00e072d98eef2c..f28aba7c3db7b6 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -194,7 +194,6 @@ MAX_INTERPOLATION_DEPTH = 10 - # exception classes class Error(Exception): """Base class for ConfigParser exceptions.""" @@ -215,7 +214,7 @@ class NoSectionError(Error): def __init__(self, section): Error.__init__(self, f'No section: {section!r}') self.section = section - self.args = (section, ) + self.args = (section,) class DuplicateSectionError(Error): @@ -252,8 +251,7 @@ class DuplicateOptionError(Error): """ def __init__(self, section, option, source=None, lineno=None): - msg = [repr(option), " in section ", repr(section), - " already exists"] + msg = [repr(option), " in section ", repr(section), " already exists"] if source is not None: message = ["While reading from ", repr(source)] if lineno is not None: @@ -295,9 +293,11 @@ class InterpolationMissingOptionError(InterpolationError): """A string substitution required a setting which was not available.""" def __init__(self, option, section, rawval, reference): - msg = (f"Bad value substitution: option {option!r} in section " - f"{section!r} contains an interpolation key {reference!r} " - f"which is not a valid option name. Raw value: {rawval!r}") + msg = ( + f"Bad value substitution: option {option!r} in section " + f"{section!r} contains an interpolation key {reference!r} " + f"which is not a valid option name. Raw value: {rawval!r}" + ) InterpolationError.__init__(self, option, section, msg) self.reference = reference self.args = (option, section, rawval, reference) @@ -315,10 +315,12 @@ class InterpolationDepthError(InterpolationError): """Raised when substitutions are nested too deeply.""" def __init__(self, option, section, rawval): - msg = (f"Recursion limit exceeded in value substitution: option " - f"{option!r} in section {section!r} contains an interpolation " - f"key which cannot be substituted in " - f"{MAX_INTERPOLATION_DEPTH} steps. Raw value: {rawval!r}") + msg = ( + f"Recursion limit exceeded in value substitution: option " + f"{option!r} in section {section!r} contains an interpolation " + f"key which cannot be substituted in " + f"{MAX_INTERPOLATION_DEPTH} steps. Raw value: {rawval!r}" + ) InterpolationError.__init__(self, option, section, msg) self.args = (option, section, rawval) @@ -330,7 +332,7 @@ def __init__(self, source, *args): super().__init__(f'Source contains parsing errors: {source!r}') self.source = source self.errors = [] - self.args = (source, ) + self.args = (source,) if args: self.append(*args) @@ -357,7 +359,6 @@ def _raise_all(exceptions: Iterable[ParsingError]): raise next(exceptions).combine(exceptions) - class MissingSectionHeaderError(ParsingError): """Raised when a key-value pair is found before any section header.""" @@ -365,7 +366,8 @@ def __init__(self, filename, lineno, line): Error.__init__( self, 'File contains no section headers.\n' - f'file: {filename!r}, line: {lineno:d}\n{line!r}') + f'file: {filename!r}, line: {lineno:d}\n{line!r}', + ) self.source = filename self.lineno = lineno self.line = line @@ -374,11 +376,13 @@ def __init__(self, filename, lineno, line): class MultilineContinuationError(ParsingError): """Raised when a key without value is followed by continuation line""" + def __init__(self, filename, lineno, line): Error.__init__( self, "Key without value continued with an indented line.\n" - f"file: {filename!r}, line: {lineno:d}\n{line!r}") + f"file: {filename!r}, line: {lineno:d}\n{line!r}", + ) self.source = filename self.lineno = lineno self.line = line @@ -388,19 +392,20 @@ def __init__(self, filename, lineno, line): class UnnamedSectionDisabledError(Error): """Raised when an attempt to use UNNAMED_SECTION is made with the feature disabled.""" + def __init__(self): Error.__init__(self, "Support for UNNAMED_SECTION is disabled.") class _UnnamedSection: - def __repr__(self): return "<UNNAMED_SECTION>" + class InvalidWriteError(Error): """Raised when attempting to write data that the parser would read back differently. ex: writing a key which begins with the section header pattern would read back as a - new section """ + new section""" def __init__(self, msg=''): Error.__init__(self, msg) @@ -454,15 +459,16 @@ def before_get(self, parser, section, option, value, defaults): return ''.join(L) def before_set(self, parser, section, option, value): - tmp_value = value.replace('%%', '') # escaped percent signs - tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax + tmp_value = value.replace('%%', '') # escaped percent signs + tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax if '%' in tmp_value: - raise ValueError(f"invalid interpolation syntax in {value!r} " - f"at position {tmp_value.find('%'):d}") + raise ValueError( + f"invalid interpolation syntax in {value!r} " + f"at position {tmp_value.find('%'):d}" + ) return value - def _interpolate_some(self, parser, option, accum, rest, section, map, - depth): + def _interpolate_some(self, parser, option, accum, rest, section, map, depth): rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) @@ -482,25 +488,31 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, elif c == "(": m = self._KEYCRE.match(rest) if m is None: - raise InterpolationSyntaxError(option, section, - f"bad interpolation variable reference {rest!r}") + raise InterpolationSyntaxError( + option, + section, + f"bad interpolation variable reference {rest!r}", + ) var = parser.optionxform(m.group(1)) - rest = rest[m.end():] + rest = rest[m.end() :] try: v = map[var] except KeyError: raise InterpolationMissingOptionError( - option, section, rawval, var) from None + option, section, rawval, var + ) from None if "%" in v: - self._interpolate_some(parser, option, accum, v, - section, map, depth + 1) + self._interpolate_some( + parser, option, accum, v, section, map, depth + 1 + ) else: accum.append(v) else: raise InterpolationSyntaxError( - option, section, - "'%' must be followed by '%' or '(', " - f"found: {rest!r}") + option, + section, + f"'%' must be followed by '%' or '(', found: {rest!r}", + ) class ExtendedInterpolation(Interpolation): @@ -515,15 +527,25 @@ def before_get(self, parser, section, option, value, defaults): return ''.join(L) def before_set(self, parser, section, option, value): - tmp_value = value.replace('$$', '') # escaped dollar signs - tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax + tmp_value = value.replace('$$', '') # escaped dollar signs + tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax if '$' in tmp_value: - raise ValueError(f"invalid interpolation syntax in {value!r} " - f"at position {tmp_value.find('$'):d}") + raise ValueError( + f"invalid interpolation syntax in {value!r} " + f"at position {tmp_value.find('$'):d}" + ) return value - def _interpolate_some(self, parser, option, # noqa: C901 # gh-159686 - accum, rest, section, map, depth): + def _interpolate_some( + self, + parser, + option, # noqa: C901 # gh-159686 + accum, + rest, + section, + map, + depth, + ): rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) @@ -543,10 +565,13 @@ def _interpolate_some(self, parser, option, # noqa: C901 # gh-159686 elif c == "{": m = self._KEYCRE.match(rest) if m is None: - raise InterpolationSyntaxError(option, section, - f"bad interpolation variable reference {rest!r}") + raise InterpolationSyntaxError( + option, + section, + f"bad interpolation variable reference {rest!r}", + ) path = m.group(1).split(':') - rest = rest[m.end():] + rest = rest[m.end() :] sect = section opt = option try: @@ -559,34 +584,42 @@ def _interpolate_some(self, parser, option, # noqa: C901 # gh-159686 v = parser.get(sect, opt, raw=True) else: raise InterpolationSyntaxError( - option, section, - f"More than one ':' found: {rest!r}") + option, section, f"More than one ':' found: {rest!r}" + ) except (KeyError, NoSectionError, NoOptionError): raise InterpolationMissingOptionError( - option, section, rawval, ":".join(path)) from None + option, section, rawval, ":".join(path) + ) from None if v is None: continue if "$" in v: - self._interpolate_some(parser, opt, accum, v, sect, - dict(parser.items(sect, raw=True)), - depth + 1) + self._interpolate_some( + parser, + opt, + accum, + v, + sect, + dict(parser.items(sect, raw=True)), + depth + 1, + ) else: accum.append(v) else: raise InterpolationSyntaxError( - option, section, - "'$' must be followed by '$' or '{', " - f"found: {rest!r}") + option, + section, + f"'$' must be followed by '$' or '{{', found: {rest!r}", + ) class _ReadState: - elements_added : set[str] - cursect : dict[str, str] | None = None - sectname : str | None = None - optname : str | None = None - lineno : int = 0 - indent_level : int = 0 - errors : list[ParsingError] + elements_added: set[str] + cursect: dict[str, str] | None = None + sectname: str | None = None + optname: str | None = None + lineno: int = 0 + indent_level: int = 0 + errors: list[ParsingError] def __init__(self): self.elements_added = set() @@ -609,15 +642,17 @@ class _CommentSpec: def __init__(self, full_prefixes, inline_prefixes): full_patterns = ( # prefix at the beginning of a line - fr'^({re.escape(prefix)}).*' + rf'^({re.escape(prefix)}).*' for prefix in full_prefixes ) inline_patterns = ( # prefix at the beginning of the line or following a space - fr'(^|\s)({re.escape(prefix)}.*)' + rf'(^|\s)({re.escape(prefix)}.*)' for prefix in inline_prefixes ) - self.pattern = re.compile('|'.join(itertools.chain(full_patterns, inline_patterns))) + self.pattern = re.compile( + '|'.join(itertools.chain(full_patterns, inline_patterns)) + ) def strip(self, text): return self.pattern.sub('', text).rstrip() @@ -671,17 +706,32 @@ class RawConfigParser(MutableMapping): NONSPACECRE = re.compile(r"\S") # Possible boolean values in the configuration. BOOLEAN_STATES: ClassVar[dict[str, bool]] = { - '1': True, 'yes': True, 'true': True, 'on': True, - '0': False, 'no': False, 'false': False, 'off': False, + '1': True, + 'yes': True, + 'true': True, + 'on': True, + '0': False, + 'no': False, + 'false': False, + 'off': False, } - def __init__(self, defaults=None, dict_type=_default_dict, - allow_no_value=False, *, delimiters=('=', ':'), - comment_prefixes=('#', ';'), inline_comment_prefixes=None, - strict=True, empty_lines_in_values=True, - default_section=DEFAULTSECT, - interpolation=_UNSET, converters=_UNSET, - allow_unnamed_section=False,): + def __init__( + self, + defaults=None, + dict_type=_default_dict, + allow_no_value=False, + *, + delimiters=('=', ':'), + comment_prefixes=('#', ';'), + inline_comment_prefixes=None, + strict=True, + empty_lines_in_values=True, + default_section=DEFAULTSECT, + interpolation=_UNSET, + converters=_UNSET, + allow_unnamed_section=False, + ): self._dict = dict_type self._sections = self._dict() @@ -695,16 +745,16 @@ def __init__(self, defaults=None, dict_type=_default_dict, else: d = "|".join(re.escape(d) for d in delimiters) if allow_no_value: - self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), - re.VERBOSE) + self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), re.VERBOSE) else: - self._optcre = re.compile(self._OPT_TMPL.format(delim=d), - re.VERBOSE) - self._comments = _CommentSpec(comment_prefixes or (), inline_comment_prefixes or ()) + self._optcre = re.compile(self._OPT_TMPL.format(delim=d), re.VERBOSE) + self._comments = _CommentSpec( + comment_prefixes or (), inline_comment_prefixes or () + ) self._strict = strict self._allow_no_value = allow_no_value self._empty_lines_in_values = empty_lines_in_values - self.default_section=default_section + self.default_section = default_section self._interpolation = interpolation if self._interpolation is _UNSET: self._interpolation = self._DEFAULT_INTERPOLATION @@ -875,37 +925,48 @@ def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET): if raw or value is None: return value else: - return self._interpolation.before_get(self, section, option, value, - d) + return self._interpolation.before_get(self, section, option, value, d) def _get(self, section, conv, option, **kwargs): return conv(self.get(section, option, **kwargs)) - def _get_conv(self, section, option, conv, *, raw=False, vars=None, - fallback=_UNSET, **kwargs): + def _get_conv( + self, section, option, conv, *, raw=False, vars=None, fallback=_UNSET, **kwargs + ): try: - return self._get(section, conv, option, raw=raw, vars=vars, - **kwargs) + return self._get(section, conv, option, raw=raw, vars=vars, **kwargs) except (NoSectionError, NoOptionError): if fallback is _UNSET: raise return fallback # getint, getfloat and getboolean provided directly for backwards compat - def getint(self, section, option, *, raw=False, vars=None, - fallback=_UNSET, **kwargs): - return self._get_conv(section, option, int, raw=raw, vars=vars, - fallback=fallback, **kwargs) + def getint( + self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs + ): + return self._get_conv( + section, option, int, raw=raw, vars=vars, fallback=fallback, **kwargs + ) - def getfloat(self, section, option, *, raw=False, vars=None, - fallback=_UNSET, **kwargs): - return self._get_conv(section, option, float, raw=raw, vars=vars, - fallback=fallback, **kwargs) + def getfloat( + self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs + ): + return self._get_conv( + section, option, float, raw=raw, vars=vars, fallback=fallback, **kwargs + ) - def getboolean(self, section, option, *, raw=False, vars=None, - fallback=_UNSET, **kwargs): - return self._get_conv(section, option, self._convert_to_boolean, - raw=raw, vars=vars, fallback=fallback, **kwargs) + def getboolean( + self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs + ): + return self._get_conv( + section, + option, + self._convert_to_boolean, + raw=raw, + vars=vars, + fallback=fallback, + **kwargs, + ) def items(self, section=_UNSET, raw=False, vars=None): """Return a list of (name, value) tuples for each option in a section. @@ -931,8 +992,9 @@ def items(self, section=_UNSET, raw=False, vars=None): if vars: for key, value in vars.items(): d[self.optionxform(key)] = value - value_getter = lambda option: self._interpolation.before_get(self, - section, option, d[option], d) + value_getter = lambda option: self._interpolation.before_get( + self, section, option, d[option], d + ) if raw: value_getter = lambda option: d[option] return [(option, value_getter(option)) for option in orig_keys] @@ -964,14 +1026,12 @@ def has_option(self, section, option): return False else: option = self.optionxform(option) - return (option in self._sections[section] - or option in self._defaults) + return option in self._sections[section] or option in self._defaults def set(self, section, option, value=None): """Set an option.""" if value: - value = self._interpolation.before_set(self, section, option, - value) + value = self._interpolation.before_set(self, section, option, value) if not section or section == self.default_section: sectdict = self._defaults else: @@ -995,16 +1055,20 @@ def write(self, fp, space_around_delimiters=True): else: d = self._delimiters[0] if self._defaults: - self._write_section(fp, self.default_section, - self._defaults.items(), d) + self._write_section(fp, self.default_section, self._defaults.items(), d) if self._sections.get(UNNAMED_SECTION): - self._write_section(fp, UNNAMED_SECTION, self._sections[UNNAMED_SECTION].items(), d, unnamed=True) + self._write_section( + fp, + UNNAMED_SECTION, + self._sections[UNNAMED_SECTION].items(), + d, + unnamed=True, + ) for section in self._sections: if section is UNNAMED_SECTION: continue - self._write_section(fp, section, - self._sections[section].items(), d) + self._write_section(fp, section, self._sections[section].items(), d) def _write_section(self, fp, section_name, section_items, delimiter, unnamed=False): """Write a single section to the specified 'fp'.""" @@ -1012,12 +1076,12 @@ def _write_section(self, fp, section_name, section_items, delimiter, unnamed=Fal fp.write(f"[{section_name}]\n") for key, value in section_items: self._validate_key_contents(key) - value = self._interpolation.before_write(self, section_name, key, - value) + value = self._interpolation.before_write(self, section_name, key, value) if value is not None or not self._allow_no_value: # Convert all possible line-endings into '\n\t' - value = (delimiter + str(value).replace('\r\n', '\n') - .replace('\r', '\n').replace('\n', '\n\t')) + value = delimiter + str(value).replace('\r\n', '\n').replace( + '\r', '\n' + ).replace('\n', '\n\t') else: value = "" fp.write(f"{key}{value}\n") @@ -1075,7 +1139,7 @@ def __contains__(self, key): return key == self.default_section or self.has_section(key) def __len__(self): - return len(self._sections) + 1 # the default section + return len(self._sections) + 1 # the default section def __iter__(self): # XXX does it break when underlying container state changed? @@ -1096,7 +1160,8 @@ def _read(self, fp, fpname): Configuration files may include comments, prefixed by specific characters (`#` and `;` by default). Comments may appear on their own in an otherwise empty line or may be entered in lines holding values or - section names. Please note that comments get stripped off when reading configuration files. + section names. Please note that comments get stripped off when reading + configuration files. """ try: ParsingError._raise_all(self._read_inner(fp, fpname)) @@ -1111,11 +1176,13 @@ def _read_inner(self, fp, fpname): if self._empty_lines_in_values: # add empty line to the value, but only if there was no # comment on the line - if (not line.has_comments and - st.cursect is not None and - st.optname and - st.cursect[st.optname] is not None): - st.cursect[st.optname].append('') # newlines added at join + if ( + not line.has_comments + and st.cursect is not None + and st.optname + and st.cursect[st.optname] is not None + ): + st.cursect[st.optname].append('') # newlines added at join else: # empty line marks end of value st.indent_level = sys.maxsize @@ -1133,8 +1200,11 @@ def _read_inner(self, fp, fpname): def _handle_continuation_line(self, st, line, fpname): # continuation line? - is_continue = (st.cursect is not None and st.optname and - st.cur_indent_level > st.indent_level) + is_continue = ( + st.cursect is not None + and st.optname + and st.cur_indent_level > st.indent_level + ) if is_continue: if st.cursect[st.optname] is None: raise MultilineContinuationError(fpname, st.lineno, line) @@ -1153,14 +1223,15 @@ def _handle_rest(self, st, line, fpname): if not mo and st.cursect is None: raise MissingSectionHeaderError(fpname, st.lineno, line) - self._handle_header(st, mo.group('header'), fpname) if mo else self._handle_option(st, line, fpname) + self._handle_header( + st, mo.group('header'), fpname + ) if mo else self._handle_option(st, line, fpname) def _handle_header(self, st, sectname, fpname): st.sectname = sectname if st.sectname in self._sections: if self._strict and st.sectname in st.elements_added: - raise DuplicateSectionError(st.sectname, fpname, - st.lineno) + raise DuplicateSectionError(st.sectname, fpname, st.lineno) st.cursect = self._sections[st.sectname] st.elements_added.add(st.sectname) elif st.sectname == self.default_section: @@ -1190,10 +1261,8 @@ def _handle_option(self, st, line, fpname): if not st.optname: st.errors.append(ParsingError(fpname, st.lineno, line)) st.optname = self.optionxform(st.optname.rstrip()) - if (self._strict and - (st.sectname, st.optname) in st.elements_added): - raise DuplicateOptionError(st.sectname, st.optname, - fpname, st.lineno) + if self._strict and (st.sectname, st.optname) in st.elements_added: + raise DuplicateOptionError(st.sectname, st.optname, fpname, st.lineno) st.elements_added.add((st.sectname, st.optname)) # This check is fine because the OPTCRE cannot # match if it would set optval to None @@ -1206,15 +1275,14 @@ def _handle_option(self, st, line, fpname): def _join_multiline_values(self): defaults = self.default_section, self._defaults - all_sections = itertools.chain((defaults,), - self._sections.items()) + all_sections = itertools.chain((defaults,), self._sections.items()) for section, options in all_sections: for name, val in options.items(): if isinstance(val, list): val = '\n'.join(val).rstrip() - options[name] = self._interpolation.before_read(self, - section, - name, val) + options[name] = self._interpolation.before_read( + self, section, name, val + ) def _read_defaults(self, defaults): """Read the defaults passed in the initializer. @@ -1243,8 +1311,7 @@ def _unify_values(self, section, vars): return _ChainMap(vardict, sectiondict, self._defaults) def _convert_to_boolean(self, value): - """Return a boolean value translating from other types if necessary. - """ + """Return a boolean value translating from other types if necessary.""" if value.lower() not in self.BOOLEAN_STATES: raise ValueError(f'Not a boolean: {value}') return self.BOOLEAN_STATES[value.lower()] @@ -1254,11 +1321,13 @@ def _validate_key_contents(self, key): delimiters or that begins with the section header pattern""" if re.match(self.SECTCRE, key): raise InvalidWriteError( - f"Cannot write key {key}; begins with section pattern") + f"Cannot write key {key}; begins with section pattern" + ) for delim in self._delimiters: if delim in key: raise InvalidWriteError( - f"Cannot write key {key}; contains delimiter {delim}") + f"Cannot write key {key}; contains delimiter {delim}" + ) def _validate_value_types(self, *, section="", option="", value=""): """Raises a TypeError for illegal non-string values. @@ -1342,8 +1411,10 @@ def __setitem__(self, key, value): return self._parser.set(self._name, key, value) def __delitem__(self, key): - if not (self._parser.has_option(self._name, key) and - self._parser.remove_option(self._name, key)): + if not ( + self._parser.has_option(self._name, key) + and self._parser.remove_option(self._name, key) + ): raise KeyError(key) def __contains__(self, key): @@ -1371,8 +1442,7 @@ def name(self): # The name of the section on a proxy is read-only. return self._name - def get(self, option, fallback=None, *, raw=False, vars=None, - _impl=None, **kwargs): + def get(self, option, fallback=None, *, raw=False, vars=None, _impl=None, **kwargs): """Get an option value. Unless `fallback` is provided, `None` will be returned if the option @@ -1383,8 +1453,9 @@ def get(self, option, fallback=None, *, raw=False, vars=None, # object that provides the desired type conversion. if not _impl: _impl = self._parser.get - return _impl(self._name, option, raw=raw, vars=vars, - fallback=fallback, **kwargs) + return _impl( + self._name, option, raw=raw, vars=vars, fallback=fallback, **kwargs + ) class ConverterMapping(MutableMapping): @@ -1404,7 +1475,7 @@ def __init__(self, parser): m = self.GETTERCRE.match(getter) if not m or not callable(getattr(self._parser, getter)): continue - self._data[m.group('name')] = None # See class docstring. + self._data[m.group('name')] = None # See class docstring. def __getitem__(self, key): return self._data[key] diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 82a2272be1ed6a..f24d7b869991c2 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -11,7 +11,6 @@ class SortedDict(collections.UserDict): - def items(self): return sorted(self.data.items()) @@ -68,19 +67,19 @@ def fromstring(self, string, defaults=None): class BasicTestCase(CfgParserTestCaseClass): - def basic_test(self, cf): - E = ['Commented Bar', - 'Foo Bar', - 'Internationalized Stuff', - 'Long Line', - 'Section\\with$weird%characters[\t', - 'Spaces', - 'Spacey Bar', - 'Spacey Bar From The Beginning', - 'Types', - 'This One Has A ] In It', - ] + E = [ + 'Commented Bar', + 'Foo Bar', + 'Internationalized Stuff', + 'Long Line', + 'Section\\with$weird%characters[\t', + 'Spaces', + 'Spacey Bar', + 'Spacey Bar From The Beginning', + 'Types', + 'This One Has A ] In It', + ] if self.allow_no_value: E.append('NoValue') @@ -148,27 +147,23 @@ def basic_test(self, cf): eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None) eq(cf.getint('Types', 'int', fallback=18), 42) eq(cf.getint('Types', 'no-such-int', fallback=18), 18) - eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic! + eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic! with self.assertRaises(configparser.NoOptionError): cf.getint('Types', 'no-such-int') - self.assertAlmostEqual(cf.getfloat('Types', 'float', - fallback=0.0), 0.44) - self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float', - fallback=0.0), 0.0) - eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic! + self.assertAlmostEqual(cf.getfloat('Types', 'float', fallback=0.0), 0.44) + self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float', fallback=0.0), 0.0) + eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic! with self.assertRaises(configparser.NoOptionError): cf.getfloat('Types', 'no-such-float') eq(cf.getboolean('Types', 'boolean', fallback=True), False) - eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"), - "yes") # sic! + eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"), "yes") # sic! eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True) with self.assertRaises(configparser.NoOptionError): cf.getboolean('Types', 'no-such-boolean') eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True) if self.allow_no_value: eq(cf.get('NoValue', 'option-without-value', fallback=False), None) - eq(cf.get('NoValue', 'no-such-option-without-value', - fallback=False), False) + eq(cf.get('NoValue', 'no-such-option-without-value', fallback=False), False) # mapping access eq(cf['Foo Bar']['foo'], 'bar1') @@ -186,8 +181,10 @@ def basic_test(self, cf): eq(cf['Commented Bar']['baz'], 'qwe') eq(cf['Spaces']['key with spaces'], 'value') eq(cf['Spaces']['another with spaces'], 'splat!') - eq(cf['Long Line']['foo'], - 'this line is much, much longer than my editor\nlikes it.') + eq( + cf['Long Line']['foo'], + 'this line is much, much longer than my editor\nlikes it.', + ) if self.allow_no_value: eq(cf['NoValue']['option-without-value'], None) # test vars= and fallback= @@ -211,23 +208,20 @@ def basic_test(self, cf): eq(cf['Types'].getint('int', fallback=18), 42) eq(cf['Types'].getint('no-such-int', 18), 18) eq(cf['Types'].getint('no-such-int', fallback=18), 18) - eq(cf['Types'].getint('no-such-int', "18"), "18") # sic! - eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic! + eq(cf['Types'].getint('no-such-int', "18"), "18") # sic! + eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic! eq(cf['Types'].getint('no-such-int'), None) self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44) - self.assertAlmostEqual(cf['Types'].getfloat('float', - fallback=0.0), 0.44) + self.assertAlmostEqual(cf['Types'].getfloat('float', fallback=0.0), 0.44) self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0) - self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', - fallback=0.0), 0.0) - eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic! - eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic! + self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', fallback=0.0), 0.0) + eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic! + eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic! eq(cf['Types'].getfloat('no-such-float'), None) eq(cf['Types'].getboolean('boolean', True), False) eq(cf['Types'].getboolean('boolean', fallback=True), False) - eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic! - eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"), - "yes") # sic! + eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic! + eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"), "yes") # sic! eq(cf['Types'].getboolean('no-such-boolean', True), True) eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True) eq(cf['Types'].getboolean('no-such-boolean'), None) @@ -235,8 +229,7 @@ def basic_test(self, cf): eq(cf['NoValue'].get('option-without-value', False), None) eq(cf['NoValue'].get('option-without-value', fallback=False), None) eq(cf['NoValue'].get('no-such-option-without-value', False), False) - eq(cf['NoValue'].get('no-such-option-without-value', - fallback=False), False) + eq(cf['NoValue'].get('no-such-option-without-value', fallback=False), False) # Make sure the right things happen for remove_section() and # remove_option(); added to include check for SourceForge bug #123324. @@ -249,13 +242,17 @@ def basic_test(self, cf): self.assertFalse(cf.has_option('Spaces', 'key with spaces')) self.assertFalse(cf.remove_section('Spaces')) self.assertFalse(cf.remove_section(self.default_section)) - self.assertTrue(cf.remove_option('Foo Bar', 'foo'), - "remove_option() failed to report existence of option") - self.assertFalse(cf.has_option('Foo Bar', 'foo'), - "remove_option() failed to remove option") - self.assertFalse(cf.remove_option('Foo Bar', 'foo'), - "remove_option() failed to report non-existence of option" - " that was removed") + self.assertTrue( + cf.remove_option('Foo Bar', 'foo'), + "remove_option() failed to report existence of option", + ) + self.assertFalse( + cf.has_option('Foo Bar', 'foo'), "remove_option() failed to remove option" + ) + self.assertFalse( + cf.remove_option('Foo Bar', 'foo'), + "remove_option() failed to report non-existence of option that was removed", + ) self.assertTrue(cf.has_option('Foo Bar', 'this_value')) self.assertFalse(cf.remove_option('Foo Bar', 'this_value')) self.assertTrue(cf.remove_option(self.default_section, 'this_value')) @@ -266,8 +263,10 @@ def basic_test(self, cf): cf.remove_option('No Such Section', 'foo') self.assertEqual(cm.exception.args, ('No Such Section',)) - eq(cf.get('Long Line', 'foo'), - 'this line is much, much longer than my editor\nlikes it.') + eq( + cf.get('Long Line', 'foo'), + 'this line is much, much longer than my editor\nlikes it.', + ) # mapping access del cf['Types'] @@ -326,47 +325,55 @@ def test_basic(self): forks {self.delimiters[0]} spoons """ if self.allow_no_value: - config_string += ( - "[NoValue]\n" - "option-without-value\n" - ) + config_string += "[NoValue]\noption-without-value\n" cf = self.fromstring(config_string) self.basic_test(cf) if self.strict: with self.assertRaises(configparser.DuplicateOptionError): - cf.read_string(textwrap.dedent(f"""\ + cf.read_string( + textwrap.dedent(f"""\ [Duplicate Options Here] option {self.delimiters[0]} with a value option {self.delimiters[1]} with another value - """)) + """) + ) with self.assertRaises(configparser.DuplicateSectionError): - cf.read_string(textwrap.dedent(f"""\ + cf.read_string( + textwrap.dedent(f"""\ [And Now For Something] completely different {self.delimiters[0]} True [And Now For Something] the larch {self.delimiters[1]} 1 - """)) + """) + ) else: - cf.read_string(textwrap.dedent(f"""\ + cf.read_string( + textwrap.dedent(f"""\ [Duplicate Options Here] option {self.delimiters[0]} with a value option {self.delimiters[1]} with another value - """)) + """) + ) - cf.read_string(textwrap.dedent(f"""\ + cf.read_string( + textwrap.dedent(f"""\ [And Now For Something] completely different {self.delimiters[0]} True [And Now For Something] the larch {self.delimiters[1]} 1 - """)) - - @support.subTests('data', [ - 'foo bar=baz', - 'foo bar=baz', - 'foo=bar=baz', - 'foo = bar=baz', - 'foo\t \t=\t \tbar=baz', - ]) + """) + ) + + @support.subTests( + 'data', + [ + 'foo bar=baz', + 'foo bar=baz', + 'foo=bar=baz', + 'foo = bar=baz', + 'foo\t \t=\t \tbar=baz', + ], + ) def test_space_delimiter(self, data): # gh-156353: Space should be accepted as a delimiter cf = self.newconfig(delimiters=(' ', '=')) @@ -403,11 +410,9 @@ def test_basic_from_dict(self): "baz": "qwe", }, "Long Line": { - "foo": "this line is much, much longer than my editor\nlikes " - "it.", - }, - "Section\\with$weird%characters[\t": { + "foo": "this line is much, much longer than my editor\nlikes it.", }, + "Section\\with$weird%characters[\t": {}, "Internationalized Stuff": { "foo[bg]": "Bulgarian", "foo": "Default", @@ -424,9 +429,7 @@ def test_basic_from_dict(self): "boolean": False, 123: "strange but acceptable", }, - "This One Has A ] In It": { - "forks": "spoons" - }, + "This One Has A ] In It": {"forks": "spoons"}, } if self.allow_no_value: config.update({ @@ -473,8 +476,11 @@ def test_case_sensitivity(self): eq(L, ["A", "B", "a"]) cf.set("a", "B", "value") eq(cf.options("a"), ["b"]) - eq(cf.get("a", "b"), "value", - "could not locate option, expecting case-insensitive option names") + eq( + cf.get("a", "b"), + "value", + "could not locate option, expecting case-insensitive option names", + ) with self.assertRaises(configparser.NoSectionError): # section names are case-sensitive cf.set("b", "A", "value") @@ -484,7 +490,8 @@ def test_case_sensitivity(self): for opt in ("a-b", "A-b", "a-B", "A-B"): self.assertTrue( cf.has_option("A", opt), - "has_option() returned false for option which should exist") + "has_option() returned false for option which should exist", + ) eq(cf.options("A"), ["a-b"]) eq(cf.options("a"), ["b"]) cf.remove_option("a", "B") @@ -492,18 +499,17 @@ def test_case_sensitivity(self): # SF bug #432369: cf = self.fromstring( - f"[MySection]\nOption{self.delimiters[0]} first line \n" - "\tsecond line \n") + f"[MySection]\nOption{self.delimiters[0]} first line \n\tsecond line \n" + ) eq(cf.options("MySection"), ["option"]) eq(cf.get("MySection", "Option"), "first line\nsecond line") # SF bug #561822: - cf = self.fromstring("[section]\n" - f"nekey{self.delimiters[0]}nevalue\n", - defaults={"key":"value"}) + cf = self.fromstring( + f"[section]\nnekey{self.delimiters[0]}nevalue\n", defaults={"key": "value"} + ) self.assertTrue(cf.has_option("section", "Key")) - def test_case_sensitivity_mapping_access(self): cf = self.newconfig() cf["A"] = {} @@ -515,8 +521,11 @@ def test_case_sensitivity_mapping_access(self): elem_eq = self.assertCountEqual eq(L, sorted(["A", "B", self.default_section, "a"])) eq(cf["a"].keys(), {"b"}) - eq(cf["a"]["b"], "value", - "could not locate option, expecting case-insensitive option names") + eq( + cf["a"]["b"], + "value", + "could not locate option, expecting case-insensitive option names", + ) with self.assertRaises(KeyError): # section names are case-sensitive cf["b"]["A"] = "value" @@ -525,7 +534,8 @@ def test_case_sensitivity_mapping_access(self): for opt in ("a-b", "A-b", "a-B", "A-B"): self.assertTrue( opt in cf["A"], - "has_option() returned false for option which should exist") + "has_option() returned false for option which should exist", + ) eq(cf["A"].keys(), {"a-b"}) eq(cf["a"].keys(), {"b"}) del cf["a"]["B"] @@ -533,29 +543,38 @@ def test_case_sensitivity_mapping_access(self): # SF bug #432369: cf = self.fromstring( - f"[MySection]\nOption{self.delimiters[0]} first line \n" - "\tsecond line \n") + f"[MySection]\nOption{self.delimiters[0]} first line \n\tsecond line \n" + ) eq(cf["MySection"].keys(), {"option"}) eq(cf["MySection"]["Option"], "first line\nsecond line") # SF bug #561822: - cf = self.fromstring("[section]\n" - f"nekey{self.delimiters[0]}nevalue\n", - defaults={"key":"value"}) + cf = self.fromstring( + f"[section]\nnekey{self.delimiters[0]}nevalue\n", defaults={"key": "value"} + ) self.assertTrue("Key" in cf["section"]) def test_default_case_sensitivity(self): cf = self.newconfig({"foo": "Bar"}) self.assertEqual( - cf.get(self.default_section, "Foo"), "Bar", - "could not locate option, expecting case-insensitive option names") + cf.get(self.default_section, "Foo"), + "Bar", + "could not locate option, expecting case-insensitive option names", + ) cf = self.newconfig({"Foo": "Bar"}) self.assertEqual( - cf.get(self.default_section, "Foo"), "Bar", - "could not locate option, expecting case-insensitive defaults") + cf.get(self.default_section, "Foo"), + "Bar", + "could not locate option, expecting case-insensitive defaults", + ) def test_crlf_normalization(self): - cf = self.newconfig({"key1": "a\nb","key2": "a\rb", "key3": "a\r\nb", "key4": "a\r\nb"}) + cf = self.newconfig({ + "key1": "a\nb", + "key2": "a\rb", + "key3": "a\r\nb", + "key4": "a\r\nb", + }) buf = io.StringIO() cf.write(buf) cf_str = buf.getvalue() @@ -567,18 +586,24 @@ def test_crlf_normalization(self): def test_parse_errors(self): cf = self.newconfig() - self.parse_error(cf, configparser.ParsingError, - "[Foo]\n" - f"{self.delimiters[0]}val-without-opt-name\n") - self.parse_error(cf, configparser.ParsingError, - "[Foo]\n" - f"{self.delimiters[1]}val-without-opt-name\n") - e = self.parse_error(cf, configparser.MissingSectionHeaderError, - "No Section!\n") + self.parse_error( + cf, + configparser.ParsingError, + f"[Foo]\n{self.delimiters[0]}val-without-opt-name\n", + ) + self.parse_error( + cf, + configparser.ParsingError, + f"[Foo]\n{self.delimiters[1]}val-without-opt-name\n", + ) + e = self.parse_error( + cf, configparser.MissingSectionHeaderError, "No Section!\n" + ) self.assertEqual(e.args, ('<???>', 1, "No Section!\n")) if not self.allow_no_value: - e = self.parse_error(cf, configparser.ParsingError, - "[Foo]\n wrong-indent\n") + e = self.parse_error( + cf, configparser.ParsingError, "[Foo]\n wrong-indent\n" + ) self.assertEqual(e.args, ('<???>',)) # read_file on a real file tricky = support.findfile("cfgparser.3", subdir="configdata") @@ -587,8 +612,11 @@ def test_parse_errors(self): expected = (tricky,) else: error = configparser.MissingSectionHeaderError - expected = (tricky, 1, - ' # INI with as many tricky parts as possible\n') + expected = ( + tricky, + 1, + ' # INI with as many tricky parts as possible\n', + ) with open(tricky, encoding='utf-8') as f: e = self.parse_error(cf, error, f) self.assertEqual(e.args, expected) @@ -604,11 +632,13 @@ def parse_error(self, cf, exc, src): def test_query_errors(self): cf = self.newconfig() - self.assertEqual(cf.sections(), [], - "new ConfigParser should have no defined sections") - self.assertFalse(cf.has_section("Foo"), - "new ConfigParser should have no acknowledged " - "sections") + self.assertEqual( + cf.sections(), [], "new ConfigParser should have no defined sections" + ) + self.assertFalse( + cf.has_section("Foo"), + "new ConfigParser should have no acknowledged sections", + ) with self.assertRaises(configparser.NoSectionError): cf.options("Foo") with self.assertRaises(configparser.NoSectionError): @@ -625,8 +655,7 @@ def get_error(self, cf, exc, section, option): except exc as e: return e else: - self.fail( - f"expected exception type {exc.__module__}.{exc.__qualname__}") + self.fail(f"expected exception type {exc.__module__}.{exc.__qualname__}") def test_boolean(self): cf = self.fromstring( @@ -646,12 +675,11 @@ def test_boolean(self): f"E3{self.delimiters[0]}-1\n" f"E4{self.delimiters[0]}0.1\n" f"E5{self.delimiters[0]}FALSE AND MORE" - ) + ) for x in range(1, 5): self.assertTrue(cf.getboolean('BOOLTEST', f't{x}')) self.assertFalse(cf.getboolean('BOOLTEST', f'f{x}')) - self.assertRaises(ValueError, - cf.getboolean, 'BOOLTEST', f'e{x}') + self.assertRaises(ValueError, cf.getboolean, 'BOOLTEST', f'e{x}') def test_weird_errors(self): cf = self.newconfig() @@ -664,34 +692,47 @@ def test_weird_errors(self): if self.strict: with self.assertRaises(configparser.DuplicateSectionError) as cm: - cf.read_string(textwrap.dedent("""\ + cf.read_string( + textwrap.dedent( + """\ [Foo] will this be added{equals}True [Bar] what about this{equals}True [Foo] oops{equals}this won't - """.format(equals=self.delimiters[0])), source='<foo-bar>') + """.format(equals=self.delimiters[0]) + ), + source='<foo-bar>', + ) e = cm.exception - self.assertEqual(str(e), "While reading from '<foo-bar>' " - "[line 5]: section 'Foo' already exists") + self.assertEqual( + str(e), + "While reading from '<foo-bar>' " + "[line 5]: section 'Foo' already exists", + ) self.assertEqual(e.args, ("Foo", '<foo-bar>', 5)) with self.assertRaises(configparser.DuplicateOptionError) as cm: cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}}) e = cm.exception - self.assertEqual(str(e), "While reading from '<dict>': option " - "'opt' in section 'Bar' already exists") + self.assertEqual( + str(e), + "While reading from '<dict>': option " + "'opt' in section 'Bar' already exists", + ) self.assertEqual(e.args, ("Bar", "opt", "<dict>", None)) def test_get_after_duplicate_option_error(self): cf = self.newconfig() - ini = textwrap.dedent("""\ + ini = textwrap.dedent( + """\ [Foo] x{equals}1 y{equals}2 y{equals}3 - """.format(equals=self.delimiters[0])) + """.format(equals=self.delimiters[0]) + ) if self.strict: with self.assertRaises(configparser.DuplicateOptionError): cf.read_string(ini) @@ -715,12 +756,9 @@ def test_write(self): f" comments {self.comment_prefixes[0]} in\n" f" multiline {self.comment_prefixes[0]} values" "\n" - ) + ) if self.allow_no_value: - config_string += ( - "[Valueless]\n" - "option-without-value\n" - ) + config_string += "[Valueless]\noption-without-value\n" cf = self.fromstring(config_string) for space_around_delimiters in (True, False): @@ -745,22 +783,19 @@ def test_write(self): "\tcomments\n" "\tmultiline\n" "\n" - ) + ) if self.allow_no_value: - expect_string += ( - "[Valueless]\n" - "option-without-value\n" - "\n" - ) + expect_string += "[Valueless]\noption-without-value\n\n" self.assertEqual(output.getvalue(), expect_string) def test_set_string_types(self): - cf = self.fromstring("[sect]\n" - f"option1{self.delimiters[0]}foo\n") + cf = self.fromstring(f"[sect]\noption1{self.delimiters[0]}foo\n") + # Check that we don't get an exception when setting values in # an existing section using strings: class mystr(str): pass + cf.set("sect", "option1", "splat") cf.set("sect", "option1", mystr("splat")) cf.set("sect", "option2", "splat") @@ -815,7 +850,9 @@ def test_read_returns_file_list_with_bytestring_path(self): self.assertEqual(parsed_files, []) # check when passing both an existing and non-existing bytestring path cf = self.newconfig() - parsed_files = cf.read([file1_bytestring, b'nonexistent-file'], encoding="utf-8") + parsed_files = cf.read( + [file1_bytestring, b'nonexistent-file'], encoding="utf-8" + ) self.assertEqual(parsed_files, [file1_bytestring]) # shared by subclasses @@ -848,15 +885,19 @@ def get_interpolation_config(self): "\n" "[Interpolation Error]\n" # no definition for 'reference' - f"name{self.delimiters[0]}%(reference)s\n") + f"name{self.delimiters[0]}%(reference)s\n" + ) def check_items_config(self, expected): - cf = self.fromstring(f""" + cf = self.fromstring( + f""" [section] name {self.delimiters[0]} %(value)s key{self.delimiters[1]} |%(name)s| getdefault{self.delimiters[1]} |%(default)s| - """, defaults={"default": "<default>"}) + """, + defaults={"default": "<default>"}, + ) L = list(cf.items("section", vars={'value': 'value'})) L.sort() self.assertEqual(L, expected) @@ -864,14 +905,17 @@ def check_items_config(self, expected): cf.items("no such section") def test_popitem(self): - cf = self.fromstring(f""" + cf = self.fromstring( + f""" [section1] name1 {self.delimiters[0]} value1 [section2] name2 {self.delimiters[0]} value2 [section3] name3 {self.delimiters[0]} value3 - """, defaults={"default": "<default>"}) + """, + defaults={"default": "<default>"}, + ) self.assertEqual(cf.popitem()[0], 'section1') self.assertEqual(cf.popitem()[0], 'section2') self.assertEqual(cf.popitem()[0], 'section3') @@ -881,8 +925,10 @@ def test_popitem(self): def test_clear(self): cf = self.newconfig({"foo": "Bar"}) self.assertEqual( - cf.get(self.default_section, "Foo"), "Bar", - "could not locate option, expecting case-insensitive option names") + cf.get(self.default_section, "Foo"), + "Bar", + "could not locate option, expecting case-insensitive option names", + ) cf['zing'] = {'option1': 'value1', 'option2': 'value2'} self.assertEqual(cf.sections(), ['zing']) self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'}) @@ -891,14 +937,17 @@ def test_clear(self): self.assertEqual(set(cf[self.default_section].keys()), {'foo'}) def test_setitem(self): - cf = self.fromstring(f""" + cf = self.fromstring( + f""" [section1] name1 {self.delimiters[0]} value1 [section2] name2 {self.delimiters[0]} value2 [section3] name3 {self.delimiters[0]} value3 - """, defaults={"nameD": "valueD"}) + """, + defaults={"nameD": "valueD"}, + ) self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'}) self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'}) self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'}) @@ -935,8 +984,7 @@ def test_invalid_multiline_value(self): invalid = textwrap.dedent(f"""\ [DEFAULT] test {self.delimiters[0]} test - invalid""" - ) + invalid""") cf = self.newconfig() with self.assertRaises(configparser.ParsingError): cf.read_string(invalid) @@ -956,46 +1004,58 @@ def test_interpolation(self): cf = self.get_interpolation_config() eq = self.assertEqual eq(cf.get("Foo", "bar"), "something with interpolation (1 step)") - eq(cf.get("Foo", "bar9"), - "something with lots of interpolation (9 steps)") - eq(cf.get("Foo", "bar10"), - "something with lots of interpolation (10 steps)") + eq(cf.get("Foo", "bar9"), "something with lots of interpolation (9 steps)") + eq(cf.get("Foo", "bar10"), "something with lots of interpolation (10 steps)") e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11") if self.interpolation == configparser._UNSET: - self.assertEqual(e.args, ("bar11", "Foo", - "something %(with11)s lots of interpolation (11 steps)")) + self.assertEqual( + e.args, + ( + "bar11", + "Foo", + "something %(with11)s lots of interpolation (11 steps)", + ), + ) def test_interpolation_missing_value(self): cf = self.get_interpolation_config() - e = self.get_error(cf, configparser.InterpolationMissingOptionError, - "Interpolation Error", "name") + e = self.get_error( + cf, + configparser.InterpolationMissingOptionError, + "Interpolation Error", + "name", + ) self.assertEqual(e.reference, "reference") self.assertEqual(e.section, "Interpolation Error") self.assertEqual(e.option, "name") if self.interpolation == configparser._UNSET: - self.assertEqual(e.args, ('name', 'Interpolation Error', - '%(reference)s', 'reference')) + self.assertEqual( + e.args, ('name', 'Interpolation Error', '%(reference)s', 'reference') + ) def test_items(self): - self.check_items_config([('default', '<default>'), - ('getdefault', '|<default>|'), - ('key', '|value|'), - ('name', 'value')]) + self.check_items_config([ + ('default', '<default>'), + ('getdefault', '|<default>|'), + ('key', '|value|'), + ('name', 'value'), + ]) def test_safe_interpolation(self): # See https://bugs.python.org/issue511737 - cf = self.fromstring("[section]\n" - f"option1{self.delimiters[0]}xxx\n" - f"option2{self.delimiters[0]}%(option1)s/xxx\n" - f"ok{self.delimiters[0]}%(option1)s/%%s\n" - f"not_ok{self.delimiters[0]}%(option2)s/%%s") + cf = self.fromstring( + "[section]\n" + f"option1{self.delimiters[0]}xxx\n" + f"option2{self.delimiters[0]}%(option1)s/xxx\n" + f"ok{self.delimiters[0]}%(option1)s/%%s\n" + f"not_ok{self.delimiters[0]}%(option2)s/%%s" + ) self.assertEqual(cf.get("section", "ok"), "xxx/%s") if self.interpolation == configparser._UNSET: self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s") def test_set_malformatted_interpolation(self): - cf = self.fromstring("[sect]\n" - f"option1{self.delimiters[0]}foo\n") + cf = self.fromstring(f"[sect]\noption1{self.delimiters[0]}foo\n") self.assertEqual(cf.get('sect', "option1"), "foo") @@ -1010,8 +1070,7 @@ def test_set_malformatted_interpolation(self): self.assertEqual(cf.get("sect", "option2"), "foo%bar") def test_set_nonstring_types(self): - cf = self.fromstring("[sect]\n" - f"option1{self.delimiters[0]}foo\n") + cf = self.fromstring(f"[sect]\noption1{self.delimiters[0]}foo\n") # Check that we get a TypeError when setting non-string values # in an existing section: self.assertRaises(TypeError, cf.set, "sect", "option1", 1) @@ -1072,9 +1131,10 @@ class CustomConfigParser(configparser.ConfigParser): cf.read_string(self.ini) self.assertMatchesIni(cf) + class ConfigParserTestCaseInvalidInterpolationType(unittest.TestCase): def test_error_on_wrong_type_for_interpolation(self): - for value in [configparser.ExtendedInterpolation, 42, "a string"]: + for value in [configparser.ExtendedInterpolation, 42, "a string"]: with self.subTest(value=value), self.assertRaises(TypeError): configparser.ConfigParser(interpolation=value) @@ -1091,9 +1151,11 @@ class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase): class MultilineValuesTestCase(BasicTestCase, unittest.TestCase): config_class = configparser.ConfigParser - wonderful_spam = ("I'm having spam spam spam spam " - "spam spam spam beaked beans spam " - "spam spam and spam!").replace(' ', '\t\n') + wonderful_spam = ( + "I'm having spam spam spam spam " + "spam spam spam beaked beans spam " + "spam spam and spam!" + ).replace(' ', '\t\n') def setUp(self): cf = self.newconfig() @@ -1114,8 +1176,10 @@ def test_dominating_multiline_values(self): cf_from_file = self.newconfig() with open(os_helper.TESTFN, encoding="utf-8") as f: cf_from_file.read_file(f) - self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'), - self.wonderful_spam.replace('\t\n', '\n')) + self.assertEqual( + cf_from_file.get('section8', 'lovely_spam4'), + self.wonderful_spam.replace('\t\n', '\n'), + ) class RawConfigParserTestCase(BasicTestCase, unittest.TestCase): @@ -1124,20 +1188,24 @@ class RawConfigParserTestCase(BasicTestCase, unittest.TestCase): def test_interpolation(self): cf = self.get_interpolation_config() eq = self.assertEqual - eq(cf.get("Foo", "bar"), - "something %(with1)s interpolation (1 step)") - eq(cf.get("Foo", "bar9"), - "something %(with9)s lots of interpolation (9 steps)") - eq(cf.get("Foo", "bar10"), - "something %(with10)s lots of interpolation (10 steps)") - eq(cf.get("Foo", "bar11"), - "something %(with11)s lots of interpolation (11 steps)") + eq(cf.get("Foo", "bar"), "something %(with1)s interpolation (1 step)") + eq(cf.get("Foo", "bar9"), "something %(with9)s lots of interpolation (9 steps)") + eq( + cf.get("Foo", "bar10"), + "something %(with10)s lots of interpolation (10 steps)", + ) + eq( + cf.get("Foo", "bar11"), + "something %(with11)s lots of interpolation (11 steps)", + ) def test_items(self): - self.check_items_config([('default', '<default>'), - ('getdefault', '|%(default)s|'), - ('key', '|%(name)s|'), - ('name', '%(value)s')]) + self.check_items_config([ + ('default', '<default>'), + ('getdefault', '|%(default)s|'), + ('key', '|%(name)s|'), + ('name', '%(value)s'), + ]) def test_set_nonstring_types(self): cf = self.newconfig() @@ -1146,8 +1214,7 @@ def test_set_nonstring_types(self): cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13]) cf.set('non-string', 'dict', {'pi': 3.14159}) self.assertEqual(cf.get('non-string', 'int'), 1) - self.assertEqual(cf.get('non-string', 'list'), - [0, 1, 1, 2, 3, 5, 8, 13]) + self.assertEqual(cf.get('non-string', 'list'), [0, 1, 1, 2, 3, 5, 8, 13]) self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159}) cf.add_section(123) cf.set(123, 'this is sick', True) @@ -1187,14 +1254,22 @@ def test_reading(self): cf = self.newconfig() parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8') self.assertEqual(parsed_files, [smbconf]) - sections = ['global', 'homes', 'printers', - 'print$', 'pdf-generator', 'tmp', 'Agustin'] + sections = [ + 'global', + 'homes', + 'printers', + 'print$', + 'pdf-generator', + 'tmp', + 'Agustin', + ] self.assertEqual(cf.sections(), sections) self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP") self.assertEqual(cf.getint("global", "max log size"), 50) self.assertEqual(cf.get("global", "hosts allow"), "127.") self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s") + class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase): config_class = configparser.ConfigParser interpolation = configparser.ExtendedInterpolation() @@ -1209,7 +1284,8 @@ def fromstring(self, string, defaults=None, optionxform=None): return cf def test_extended_interpolation(self): - cf = self.fromstring(textwrap.dedent(""" + cf = self.fromstring( + textwrap.dedent(""" [common] favourite Beatle = Paul favourite color = green @@ -1231,7 +1307,8 @@ def test_extended_interpolation(self): favourite state of mind = paranoid favourite movie = soylent ${common:favourite color} favourite song = ${favourite color} sabbath - ${favourite state of mind} - """).strip()) + """).strip() + ) eq = self.assertEqual eq(cf['common']['favourite Beatle'], 'Paul') @@ -1250,11 +1327,11 @@ def test_extended_interpolation(self): eq(cf['stanley']['favourite state of mind'], 'paranoid') eq(cf['stanley']['favourite movie'], 'soylent green') eq(cf['stanley']['favourite pope'], 'John Paul II') - eq(cf['stanley']['favourite song'], - 'black sabbath - paranoid') + eq(cf['stanley']['favourite song'], 'black sabbath - paranoid') def test_endless_loop(self): - cf = self.fromstring(textwrap.dedent(""" + cf = self.fromstring( + textwrap.dedent(""" [one for you] ping = ${one for me:pong} @@ -1263,7 +1340,8 @@ def test_endless_loop(self): [selfish] me = ${me} - """).strip()) + """).strip() + ) with self.assertRaises(configparser.InterpolationDepthError): cf['one for you']['ping'] @@ -1289,7 +1367,7 @@ def test_strange_options(self): with self.assertRaises(exception_class) as cm: cf['interpolated']['$trying'] self.assertEqual(cm.exception.reference, 'dollars:${sick') - self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval + self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') # rawval def test_case_sensitivity_basic(self): ini = textwrap.dedent(""" @@ -1421,32 +1499,37 @@ def test_cfgparser_dot_3(self): tricky = support.findfile("cfgparser.3", subdir="configdata") cf = self.newconfig() self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1) - self.assertEqual(cf.sections(), ['strange', - 'corruption', - ('yeah, sections can be ' - 'indented as well'), - 'another one!', - 'no values here', - 'tricky interpolation', - 'more interpolation']) - self.assertEqual(cf.getint(self.default_section, 'go', - vars={'interpolate': '-1'}), -1) + self.assertEqual( + cf.sections(), + [ + 'strange', + 'corruption', + ('yeah, sections can be indented as well'), + 'another one!', + 'no values here', + 'tricky interpolation', + 'more interpolation', + ], + ) + self.assertEqual( + cf.getint(self.default_section, 'go', vars={'interpolate': '-1'}), -1 + ) with self.assertRaises(ValueError): # no interpolation will happen - cf.getint(self.default_section, 'go', raw=True, - vars={'interpolate': '-1'}) + cf.getint(self.default_section, 'go', raw=True, vars={'interpolate': '-1'}) self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4) self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10) longname = 'yeah, sections can be indented as well' self.assertFalse(cf.getboolean(longname, 'are they subsections')) self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名') - self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and - # `go` from DEFAULT + self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and + # `go` from DEFAULT with self.assertRaises(configparser.InterpolationMissingOptionError): cf.items('no values here') self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this') - self.assertEqual(cf.get('tricky interpolation', 'lets'), - cf.get('tricky interpolation', 'go')) + self.assertEqual( + cf.get('tricky interpolation', 'lets'), cf.get('tricky interpolation', 'go') + ) self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping') def test_unicode_failure(self): @@ -1489,23 +1572,12 @@ class SortedTestCase(RawConfigParserTestCase): dict_type = SortedDict def test_sorted(self): - cf = self.fromstring("[b]\n" - "o4=1\n" - "o3=2\n" - "o2=3\n" - "o1=4\n" - "[a]\n" - "k=v\n") + cf = self.fromstring("[b]\no4=1\no3=2\no2=3\no1=4\n[a]\nk=v\n") output = io.StringIO() cf.write(output) - self.assertEqual(output.getvalue(), - "[a]\n" - "k = v\n\n" - "[b]\n" - "o1 = 4\n" - "o2 = 3\n" - "o3 = 2\n" - "o4 = 1\n\n") + self.assertEqual( + output.getvalue(), "[a]\nk = v\n\n[b]\no1 = 4\no2 = 3\no3 = 2\no4 = 1\n\n" + ) class CompatibleTestCase(CfgParserTestCaseClass, unittest.TestCase): @@ -1524,11 +1596,10 @@ def test_comment_handling(self): ; a space must precede an inline comment """) cf = self.fromstring(config_string) - self.assertEqual(cf.get('Commented Bar', 'foo'), - 'bar # not a comment!') + self.assertEqual(cf.get('Commented Bar', 'foo'), 'bar # not a comment!') self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe') - self.assertEqual(cf.get('Commented Bar', 'quirk'), - 'this;is not a comment') + self.assertEqual(cf.get('Commented Bar', 'quirk'), 'this;is not a comment') + class CopyTestCase(BasicTestCase, unittest.TestCase): config_class = configparser.ConfigParser @@ -1576,7 +1647,7 @@ def test_file(self): try: file_paths.append(file_paths[0].encode('utf8')) except UnicodeEncodeError: - pass # unfortunately we can't test bytes on this path + pass # unfortunately we can't test bytes on this path for file_path in file_paths: parser = configparser.ConfigParser() with open(file_path, encoding="utf-8") as f: @@ -1586,9 +1657,14 @@ def test_file(self): self.assertEqual(parser["Foo Bar"]["foo"], "newbar") def test_iterable(self): - lines = textwrap.dedent(""" + lines = ( + textwrap + .dedent(""" [Foo Bar] - foo=newbar""").strip().split('\n') + foo=newbar""") + .strip() + .split('\n') + ) parser = configparser.ConfigParser() parser.read_file(lines) self.assertIn("Foo Bar", parser) @@ -1607,56 +1683,74 @@ def test_readline_generator(self): def test_source_as_bytes(self): """Issue #18260.""" - lines = textwrap.dedent(""" + lines = ( + textwrap + .dedent(""" [badbad] - [badbad]""").strip().split('\n') + [badbad]""") + .strip() + .split('\n') + ) parser = configparser.ConfigParser() with self.assertRaises(configparser.DuplicateSectionError) as dse: parser.read_file(lines, source=b"badbad") self.assertEqual( str(dse.exception), - "While reading from b'badbad' [line 2]: section 'badbad' " - "already exists" + "While reading from b'badbad' [line 2]: section 'badbad' already exists", ) - lines = textwrap.dedent(""" + lines = ( + textwrap + .dedent(""" [badbad] bad = bad - bad = bad""").strip().split('\n') + bad = bad""") + .strip() + .split('\n') + ) parser = configparser.ConfigParser() with self.assertRaises(configparser.DuplicateOptionError) as dse: parser.read_file(lines, source=b"badbad") self.assertEqual( str(dse.exception), "While reading from b'badbad' [line 3]: option 'bad' in section " - "'badbad' already exists" + "'badbad' already exists", ) - lines = textwrap.dedent(""" + lines = ( + textwrap + .dedent(""" [badbad] - = bad""").strip().split('\n') + = bad""") + .strip() + .split('\n') + ) parser = configparser.ConfigParser() with self.assertRaises(configparser.ParsingError) as dse: parser.read_file(lines, source=b"badbad") self.assertEqual( str(dse.exception), - "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'" + "Source contains parsing errors: b'badbad'\n\t[line 2]: '= bad'", ) - lines = textwrap.dedent(""" + lines = ( + textwrap + .dedent(""" [badbad - bad = bad""").strip().split('\n') + bad = bad""") + .strip() + .split('\n') + ) parser = configparser.ConfigParser() with self.assertRaises(configparser.MissingSectionHeaderError) as dse: parser.read_file(lines, source=b"badbad") self.assertEqual( str(dse.exception), - "File contains no section headers.\nfile: b'badbad', line: 1\n" - "'[badbad'" + "File contains no section headers.\nfile: b'badbad', line: 1\n'[badbad'", ) def test_keys_without_value_with_extra_whitespace(self): lines = [ '[SECT]\n', 'KEY1\n', - ' KEY2 = VAL2\n', # note the Space before the key! + ' KEY2 = VAL2\n', # note the Space before the key! ] parser = configparser.ConfigParser( comment_prefixes="", @@ -1671,12 +1765,10 @@ def test_keys_without_value_with_extra_whitespace(self): str(dse.exception), "Key without value continued with an indented line.\n" "file: '<???>', line: 3\n" - "' KEY2 = VAL2\\n'" + "' KEY2 = VAL2\\n'", ) - - class CoverageOneHundredTestCase(unittest.TestCase): """Covers edge cases in the codebase.""" @@ -1687,12 +1779,12 @@ def test_duplicate_option_error(self): self.assertEqual(error.source, None) self.assertEqual(error.lineno, None) self.assertEqual(error.args, ('section', 'option', None, None)) - self.assertEqual(str(error), "Option 'option' in section 'section' " - "already exists") + self.assertEqual( + str(error), "Option 'option' in section 'section' already exists" + ) def test_interpolation_depth_error(self): - error = configparser.InterpolationDepthError('option', 'section', - 'rawval') + error = configparser.InterpolationDepthError('option', 'section', 'rawval') self.assertEqual(error.args, ('option', 'section', 'rawval')) self.assertEqual(error.option, 'option') self.assertEqual(error.section, 'section') @@ -1715,12 +1807,14 @@ def test_interpolation_validation(self): """) with self.assertRaises(configparser.InterpolationSyntaxError) as cm: parser['section']['invalid_percent'] - self.assertEqual(str(cm.exception), "'%' must be followed by '%' or " - "'(', found: '%'") + self.assertEqual( + str(cm.exception), "'%' must be followed by '%' or '(', found: '%'" + ) with self.assertRaises(configparser.InterpolationSyntaxError) as cm: parser['section']['invalid_reference'] - self.assertEqual(str(cm.exception), "bad interpolation variable " - "reference '%(()'") + self.assertEqual( + str(cm.exception), "bad interpolation variable reference '%(()'" + ) def test_sectionproxy_repr(self): parser = configparser.ConfigParser() @@ -1733,6 +1827,7 @@ def test_sectionproxy_repr(self): def test_inconsistent_converters_state(self): parser = configparser.ConfigParser() import decimal + parser.converters['decimal'] = decimal.Decimal parser.read_string(""" [s1] @@ -1764,6 +1859,7 @@ class ExceptionPicklingTestCase(unittest.TestCase): def test_error(self): import pickle + e1 = configparser.Error('value') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1786,6 +1882,7 @@ def test_combine_error_linear_complexity(self): def test_nosectionerror(self): import pickle + e1 = configparser.NoSectionError('section') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1797,6 +1894,7 @@ def test_nosectionerror(self): def test_nooptionerror(self): import pickle + e1 = configparser.NoOptionError('option', 'section') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1809,6 +1907,7 @@ def test_nooptionerror(self): def test_duplicatesectionerror(self): import pickle + e1 = configparser.DuplicateSectionError('section', 'source', 123) for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1822,8 +1921,8 @@ def test_duplicatesectionerror(self): def test_duplicateoptionerror(self): import pickle - e1 = configparser.DuplicateOptionError('section', 'option', 'source', - 123) + + e1 = configparser.DuplicateOptionError('section', 'option', 'source', 123) for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) e2 = pickle.loads(pickled) @@ -1837,6 +1936,7 @@ def test_duplicateoptionerror(self): def test_interpolationerror(self): import pickle + e1 = configparser.InterpolationError('option', 'section', 'msg') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1849,8 +1949,10 @@ def test_interpolationerror(self): def test_interpolationmissingoptionerror(self): import pickle - e1 = configparser.InterpolationMissingOptionError('option', 'section', - 'rawval', 'reference') + + e1 = configparser.InterpolationMissingOptionError( + 'option', 'section', 'rawval', 'reference' + ) for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) e2 = pickle.loads(pickled) @@ -1863,6 +1965,7 @@ def test_interpolationmissingoptionerror(self): def test_interpolationsyntaxerror(self): import pickle + e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1875,8 +1978,8 @@ def test_interpolationsyntaxerror(self): def test_interpolationdeptherror(self): import pickle - e1 = configparser.InterpolationDepthError('option', 'section', - 'rawval') + + e1 = configparser.InterpolationDepthError('option', 'section', 'rawval') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) e2 = pickle.loads(pickled) @@ -1888,6 +1991,7 @@ def test_interpolationdeptherror(self): def test_parsingerror(self): import pickle + e1 = configparser.ParsingError('source') e1.append(1, 'line1') e1.append(2, 'line2') @@ -1915,6 +2019,7 @@ def test_parsingerror(self): def test_missingsectionheadererror(self): import pickle + e1 = configparser.MissingSectionHeaderError('filename', 123, 'line') for proto in range(pickle.HIGHEST_PROTOCOL + 1): pickled = pickle.dumps(e1, proto) @@ -1932,8 +2037,7 @@ class InlineCommentStrippingTestCase(unittest.TestCase): delimiter occurs earlier without preceding space..""" def test_stripping(self): - cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#', - '//')) + cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#', '//')) cfg.read_string(""" [section] k1 = v1;still v1 @@ -1964,8 +2068,8 @@ def test_stripping(self): class ExceptionContextTestCase(unittest.TestCase): - """ Test that implementation details doesn't leak - through raising exceptions. """ + """Test that implementation details doesn't leak + through raising exceptions.""" def test_get_basic_interpolation(self): parser = configparser.ConfigParser() @@ -1982,7 +2086,8 @@ def test_get_basic_interpolation(self): def test_get_extended_interpolation(self): parser = configparser.ConfigParser( - interpolation=configparser.ExtendedInterpolation()) + interpolation=configparser.ExtendedInterpolation() + ) parser.read_string(""" [Paths] home_dir: /Users @@ -2024,8 +2129,9 @@ class ConvertersTestCase(BasicTestCase, unittest.TestCase): def newconfig(self, defaults=None, **kwargs): instance = super().newconfig(defaults=defaults, **kwargs) - instance.converters['list'] = lambda v: [e.strip() for e in v.split() - if e.strip()] + instance.converters['list'] = lambda v: [ + e.strip() for e in v.split() if e.strip() + ] return instance def test_converters(self): @@ -2063,20 +2169,19 @@ def test_converters(self): self.assertEqual(cfg.get('s', 'str'), 'string') self.assertEqual(cfg.getint('s', 'int'), 1) self.assertEqual(cfg.getfloat('s', 'float'), 0.5) - self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd', - 'e', 'f', 'g']) + self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd', 'e', 'f', 'g']) self.assertEqual(cfg.getboolean('s', 'bool'), True) self.assertEqual(s.get('str'), 'string') self.assertEqual(s.getint('int'), 1) self.assertEqual(s.getfloat('float'), 0.5) - self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd', - 'e', 'f', 'g']) + self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd', 'e', 'f', 'g']) self.assertEqual(s.getboolean('bool'), True) with self.assertRaises(AttributeError): cfg.getdecimal('s', 'float') with self.assertRaises(AttributeError): s.getdecimal('float') import decimal + cfg.converters['decimal'] = decimal.Decimal self.assertIn('decimal', cfg.converters) self.assertIsNotNone(cfg.converters['decimal']) @@ -2125,16 +2230,33 @@ def test_inheritance(self): class StrangeConfigParser(configparser.ConfigParser): gettysburg = 'a historic borough in south central Pennsylvania' - def getboolean(self, section, option, *, raw=False, vars=None, - fallback=configparser._UNSET): + def getboolean( + self, + section, + option, + *, + raw=False, + vars=None, + fallback=configparser._UNSET, + ): if section == option: return True - return super().getboolean(section, option, raw=raw, vars=vars, - fallback=fallback) - def getlen(self, section, option, *, raw=False, vars=None, - fallback=configparser._UNSET): - return self._get_conv(section, option, len, raw=raw, vars=vars, - fallback=fallback) + return super().getboolean( + section, option, raw=raw, vars=vars, fallback=fallback + ) + + def getlen( + self, + section, + option, + *, + raw=False, + vars=None, + fallback=configparser._UNSET, + ): + return self._get_conv( + section, option, len, raw=raw, vars=vars, fallback=fallback + ) cfg = StrangeConfigParser() cfg.read_string(self.config) @@ -2211,7 +2333,6 @@ def test_instance_assignment(self): class SectionlessTestCase(unittest.TestCase): - def fromstring(self, string): cfg = configparser.ConfigParser(allow_unnamed_section=True) cfg.read_string(string) @@ -2225,8 +2346,7 @@ def test_no_first_section(self): c = 3 """) - self.assertEqual({configparser.UNNAMED_SECTION, 'sect1'}, - set(cfg1.sections())) + self.assertEqual({configparser.UNNAMED_SECTION, 'sect1'}, set(cfg1.sections())) self.assertEqual('1', cfg1[configparser.UNNAMED_SECTION]['a']) self.assertEqual('2', cfg1[configparser.UNNAMED_SECTION]['b']) self.assertEqual('3', cfg1['sect1']['c']) @@ -2235,7 +2355,9 @@ def test_no_first_section(self): cfg1.write(output) cfg2 = self.fromstring(output.getvalue()) - #self.assertEqual(set([configparser.UNNAMED_SECTION, 'sect1']), set(cfg2.sections())) + # self.assertEqual( + # set([configparser.UNNAMED_SECTION, 'sect1']), set(cfg2.sections()) + # ) self.assertEqual('1', cfg2[configparser.UNNAMED_SECTION]['a']) self.assertEqual('2', cfg2[configparser.UNNAMED_SECTION]['b']) self.assertEqual('3', cfg2['sect1']['c']) @@ -2348,8 +2470,9 @@ def test_option_regex_no_value_does_not_backtrack(self): class MiscTestCase(unittest.TestCase): def test__all__(self): - support.check__all__(self, configparser, - not_exported={"Error", "TYPE_CHECKING"}) + support.check__all__( + self, configparser, not_exported={"Error", "TYPE_CHECKING"} + ) if __name__ == '__main__': _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
