Skip to content

configparser: allow default_section=None - #16262

Open
ekanshul wants to merge 2 commits into
python:mainfrom
ekanshul:configparser-default-section-none
Open

configparser: allow default_section=None#16262
ekanshul wants to merge 2 commits into
python:mainfrom
ekanshul:configparser-default-section-none

Conversation

@ekanshul

Copy link
Copy Markdown
Contributor

Closes #12700

Widen RawConfigParser.default_section and the default_section parameter of RawConfigParser.__init__ to str | None, since passing None works at runtime to disable the default section and is relied on in the wild (e.g. setuptools).

Agent used: Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

setuptools (https://github.com/pypa/setuptools)
+ setuptools/config/expand.py:371: error: Unused "type: ignore" comment  [unused-ignore]
+ setuptools/config/expand.py:372: error: Cannot assign to a method  [method-assign]
+ setuptools/config/expand.py:372: error: Incompatible types in assignment (expression has type "type[str]", variable has type "Callable[[str], str]")  [assignment]
+ setuptools/config/expand.py:375: error: Argument 1 to "pop" of "dict" has incompatible type "str | None"; expected "str"  [arg-type]

Comment thread stdlib/configparser.pyi
def __getitem__(self, key: _SectionName) -> SectionProxy: ...
def __setitem__(self, key: _SectionName, value: _Section) -> None: ...
def __delitem__(self, key: _SectionName) -> None: ...
def __iter__(self) -> Iterator[str]: ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A problem with this is that if default_section is given as None, you will get None in __iter__.

@ekanshul

ekanshul commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

You're right, and it goes further than __iter__. On 3.12 with default_section=None:

>>> p = ConfigParser(default_section=None)
>>> p.read_string("[DEFAULT]\na = 1\n[sec]\nb = 2\n")
>>> list(p)
[None, 'DEFAULT', 'sec']
>>> len(p), None in p, p[None]
(3, True, <Section: None>)

__iter__ chains self.default_section in front of the sections, so __iter__, __len__, __contains__ and the keys()/items()/values() mixins all surface None as a key. setuptools actually relies on that: it iterates parser.items() and then does groups.pop(parser.default_section, None) to drop the None entry, which is the pop() error in the mypy_primer output. (The two new errors on the parser.optionxform = str line are pre-existing problems that were hidden while the constructor call failed to match and parser was Any.)

Modelling this properly would mean Iterator[str | None] and friends, or making the parser generic in the section-name type, and both are a heavy price for undocumented behaviour with one known user. Even this narrower version has a cost for everybody: with default_section: str | None, parser[parser.default_section] and parser.get(parser.default_section, ...) no longer type check.

So I don't think this is worth doing. I'd suggest closing this PR and #12700 as "not modelled"; setuptools already carries a # type: ignore with a comment pointing at #12700, which seems like the right place for it. If you'd rather accept the imprecision in __iter__ with a comment, I can do that instead, but I lean towards closing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should None be an acceptable default_section parameter of configparser.ConfigParser

3 participants