Skip to content

Define a configparser-compatibility policy, and fix two silent misparses (malformed section headers, inline comments in parse())聽#83

Description

@RonnyPfannschmidt

馃 Written by Claude Opus 5.5 via Claude Code for the iniconfig maintainers; I prompted it, it did the work, I read it.

Why this matters

pytest reads pytest.ini, tox.ini and setup.cfg through iniconfig.IniConfig(path) (_pytest/config/findpaths.py). It turns ParseError into a UsageError and requires iniconfig>=2 with no upper bound. Two consequences follow:

  • Every behaviour change to the constructor reaches all pytest users on upgrade.
  • tox.ini and setup.cfg are also read by tox, setuptools and flake8 through configparser. Wherever iniconfig and configparser disagree, one file means two different things.

To find where they disagree, iniconfig 2.3.1 was run against 44 edge-case files and compared with configparser (ConfigParser and RawConfigParser), configobj, Node ini, PHP parse_ini_string, Go gopkg.in/ini.v1 and rust-ini.

Bug 1: malformed section headers are silently swallowed (constructor and parse())

_parseline treats any line that starts with [ but does not end with ] as continuation data, even when the line is not indented:

[s]
k = v
[t] extra
j = w
  • iniconfig: {"s": {"k": "v\n[t] extra", "j": "w"}}. Section t disappears and j moves into s.
  • configparser: {"s": {"k": "v"}, "t": {"j": "w"}}

With [t (no closing bracket), iniconfig swallows the line the same way, while configparser raises ParsingError. As the first line of a file, [s produces the misleading error "unexpected value continuation".

Here is a pytest-shaped reproduction of the typo:

# tox.ini
[testenv]
commands = pytest
[pytest
addopts = -x
markers =
    slow: slow tests

pytest finds no [pytest] section and silently runs without this configuration, so --strict-markers then fails on slow. tox rejects the same file with ParsingError.

Proposal: raise ParseError for a line that starts with [ and is not a valid section header. The messages would be "unclosed section header" and "unexpected text after section header". For pytest this turns silently ignored configuration into a clear UsageError. The only files it breaks are ones whose meaning is already corrupted.

Bug 2: parse() cuts values at # or ; anywhere

IniConfig.parse() strips inline comments by default, but it does not require whitespace before the comment character:

value parse() configparser(inline_comment_prefixes=("#", ";"))
a#b a a#b
http://x/#frag http://x/ http://x/#frag
d;e (continuation line) d d;e
-k 'a or b' # c -k 'a or b' -k 'a or b'

pytest is not affected today, because it uses the constructor, which does not strip inline comments. This has to be fixed before pytest could ever move to parse(), though. Values such as filterwarnings messages, URLs and marker descriptions with ; would otherwise be cut short.

Proposal: only treat # or ; as a comment marker when whitespace comes before it, matching configparser.

Proposed policy

iniconfig reads like RawConfigParser(strict=True) unless a difference is documented. An undocumented difference is a bug.

Under that policy, the current behaviour lines up as follows:

Topic iniconfig configparser Note for pytest
Whitespace in section names ([ s ], NO-BREAK SPACE) kept, opt-in strip via parse(strip_section_whitespace=True) kept Same. Optionally warn, which was the original ask in #4.
Unicode whitespace around keys and values stripped stripped Same.
Duplicate section or key error error Same.
Key case case-sensitive lowercased Document it. pytest ini keys are case-sensitive.
Indented first key ( k = v right after a header) error accepted Stricter but loud. Keep it, maybe with a clearer message.
Blank line inside a continuation dropped kept Document it. pytest's line-list values are unaffected.
UTF-8 BOM stripped error Keep it (#76).
Indented [t] continuation continuation Same. A warning for a line that looks like a header would be cheap.
Interpolation, quotes, [DEFAULT] none, kept, ordinary section % interpolation (ConfigParser), kept, inherited Document it. pytest behaves like RawConfigParser.

Separately, the strip_section_whitespace docstrings in __init__.py and _parse.py say "section and key names". The flag only affects section names, and keys are always stripped.

Questions

  1. Should bug 1 raise an error in the constructor right away? It reaches pytest users without a pin. Alternatively, it could warn for one release first.
  2. Should bug 2 be fixed in a 2.3.x patch release, given that parse() shipped in 2.3.0?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions