Skip to content

Modernize string formatting in configparser and its test module #159684

Open

Description

@jaraco

Feature or enhancement

Proposal:

Lib/configparser.py and Lib/test/test_configparser.py still carry a fair
amount of pre-f-string formatting (% formatting and str.format() calls) plus
a handful of constructs that have since acquired simpler spellings. Linting the
two modules with Ruff's current default rule set reports 72 violations:

35  UP032   f-string
17  UP031   printf-string-formatting
 3  UP030   format-literals
 3  RUF012  mutable-class-default
 2  SIM102  collapsible-if
 2  C414    unnecessary-double-cast-or-process
 1  C408    unnecessary-collection-call
 1  C405    unnecessary-literal-set
 1  C901    complex-structure
 1  F841    unused-variable
 1  I001    unsorted-imports
 1  ISC004  implicit-string-concatenation-in-collection-literal
 1  RUF019  unnecessary-key-check
 1  RUF022  unsorted-dunder-all
 1  RUF059  unused-unpacked-variable
 1  SIM117  multiple-with-statements

None of these are bugs, and CPython's own Ruff configuration only selects F401
and F811 for Lib/, so nothing here is failing today. The proposal is simply
to modernize the two modules: convert the %/.format() call sites to
f-strings, and apply the small set of simplifications above.

Two of the reports need more than a mechanical fix:

  • C901 on ExtendedInterpolation._interpolate_some is a complexity metric,
    not a defect. Splitting that parser loop up would be a much larger and
    riskier change, so I propose an explicit # noqa: C901 # gh-159686. The
    issue reference rides along in the directive rather than in a comment above
    it, so the suppression carries its own trail without adding prose to the
    method; configparser: ExtendedInterpolation._interpolate_some exceeds common complexity limits #159686 records why the complexity is inherent to the
    interpolation grammar and what a refactoring might look like.

  • RUF012 wants the mutable class attributes annotated ClassVar, and the
    obvious spelling of that would import typing at runtime, which this module
    deliberately avoids (cf. the gh-117703 note above the imports). Adding
    from __future__ import annotations and importing ClassVar only under
    TYPE_CHECKING keeps typing out of the import graph, as tomllib and
    importlib.metadata already do. -X importtime confirms typing is still
    not imported. A side benefit is that Iterable['ParsingError'] can lose its
    quotes (UP037).

    This does make TYPE_CHECKING a module-level name, so it needs adding to
    not_exported in the check__all__ test.

Motivation

Two reasons:

  1. These are the usual readability wins. The error paths in particular read
    better as f-strings, e.g.

    -        Error.__init__(self, 'No section: %r' % (section,))
    +        Error.__init__(self, f'No section: {section!r}')
  2. configparser is backported to PyPI as
    backports.configparser, whose
    module is merged from CPython periodically. That project lints with the
    default rule set and currently has to carry a per-file exemption for the
    CPython-derived files (jaraco/configparser#73). Landing the changes here
    lets the backport drop the exemption and keeps the two copies converging
    rather than diverging.

I have this working locally as four reviewable commits (the f-string
conversion, the simplifications, the handful of % sites Ruff will not
autofix, and the import/__all__/ClassVar changes above), and I am happy to
put up the PR. With all of it applied, both CPython's own Ruff configuration
and the default rule set report no findings for either file, and
test_configparser, test_logging, test_collections, test___all__ and
test_pydoc all pass.

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

jaraco/configparser#73

Linked PRs

Activity

  1. added a commit that references this issue on Oct 18, 2026
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    type-refactorCode refactoring (with no changes in behavior)
    on Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-refactorCode refactoring (with no changes in behavior)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions