Skip to content

🐛 fix(config): ignore a config file that fails to parse instead of crashing - #3230

Open
darrenhuai wants to merge 2 commits into
pypa:mainfrom
darrenhuai:fix/ini-parse-failure
Open

🐛 fix(config): ignore a config file that fails to parse instead of crashing#3230
darrenhuai wants to merge 2 commits into
pypa:mainfrom
darrenhuai:fix/ini-parse-failure

Conversation

@darrenhuai

@darrenhuai darrenhuai commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thanks for contributing, make sure you address all the checklists (for details on how see development documentation)

  • ran the linter to address style issues (tox -e fix)
  • wrote descriptive pull request text
  • ensured there are test(s) validating the fix
  • added news fragment in docs/changelog folder
  • updated/extended the documentation

IniConfig.__init__ is written to survive a config file it cannot parse: it catches the exception, logs failed to read config file ... because ..., and there is a "failed to parse" entry in STATE for the help epilog. But has_virtualenv_section is only assigned after a successful _load(), and __bool__ reads it unconditionally:

def __bool__(self) -> bool:
    return bool(self.has_config_file) and bool(self.has_virtualenv_section)

VirtualEnvConfigParser._fix_defaults evaluates if outcome is None and self.file_config: for every option, so the first one without an env var override blows up. Any virtualenv.ini that configparser rejects takes every invocation down, right after the log line saying the file would be ignored:

$ printf '\xef\xbb\xbf[virtualenv]\nclear = True\n' > bom.ini
$ VIRTUALENV_CONFIG_FILE=bom.ini virtualenv --help
failed to read config file bom.ini because MissingSectionHeaderError('File contains no section headers.\nfile: ...bom.ini, line: 1\n\'\ufeff[virtualenv]\n\'')
Traceback (most recent call last):
  ...
  File "src/virtualenv/config/cli/parser.py", line 110, in _fix_defaults
    if outcome is None and self.file_config:
  File "src/virtualenv/config/ini.py", line 76, in __bool__
    return bool(self.has_config_file) and bool(self.has_virtualenv_section)
AttributeError: 'IniConfig' object has no attribute 'has_virtualenv_section'

The "failed to parse" epilog state was unreachable for the same reason; a file that failed to parse still reported itself as active.

The BOM above is the way I expect people actually hit this. On Windows, Out-File -Encoding utf8 in PowerShell 5 and older versions of Notepad both write a UTF-8 BOM, and configparser then sees \ufeff[virtualenv] as a line outside any section. A stray line inside the section or a missing header fail the same way.

Three changes in IniConfig:

  • initialise has_virtualenv_section = False before trying to load, so __bool__ is always answerable
  • on a parse exception set has_config_file = None, which is the state STATE already reserves for "failed to parse"; the config is then skipped and --help says why instead of active
  • open the file with utf-8-sig so a BOM is stripped rather than fatal; it is a no-op for files without one

With the fix the three files above all produce a working --help. The BOM one is parsed normally (clear = True takes effect), and the other two are ignored with the error logged and the epilog reading config file ... failed to parse.

Three tests: a bad file is ignored and reported (bool(config) is False, the epilog says failed to parse, the error is logged), a bad file does not break session_via_cli, and a BOM-prefixed file is read. All three fail on main, and I checked that reverting each of the three source lines individually makes exactly one of them fail. tests/unit is 314 passed / 40 skipped on Windows 3.14; ruff and ty check src/virtualenv are clean.

…ashing

IniConfig only sets has_virtualenv_section after a successful parse, but
__bool__ reads it unconditionally, so a virtualenv.ini that configparser
rejects took every invocation down with

    AttributeError: 'IniConfig' object has no attribute 'has_virtualenv_section'

right after logging that the file would be ignored. The "failed to parse"
state in the help epilog was unreachable for the same reason.

The easiest way to hit this on Windows is a UTF-8 BOM: PowerShell 5's
Out-File and older Notepad both write one, and configparser then sees
"\ufeff[virtualenv]" as a line outside any section. Read the file with
utf-8-sig so a BOM is tolerated, and when parsing does fail, mark the
config as failed to parse so it is skipped and the epilog says why.
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.

1 participant