Sitelet https://github.com/coveragepy/coveragepy/pull/2262
Skip to content

fix: validate the --contexts regexes up front - #2262

Merged
nedbat merged 1 commit into
coveragepy:mainfrom
VXNCXNX:fix/invalid-contexts-regex
Aug 18, 2026
Merged

nedbat merged 1 commit into
coveragepy:mainfrom
VXNCXNX:fix/invalid-contexts-regex

Conversation

@VXNCXNX

@VXNCXNX VXNCXNX commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

An invalid regex in --contexts reports a confusing error that blames the data file.

$ coverage report --contexts='foo('
Couldn't use data file '/tmp/covtest/.coverage': user-defined function raised exception

After:

Invalid context regex 'foo(': missing ), unterminated subpattern at position 3

The data file is fine. The regex goes straight to the SQLite REGEXP user function, where re.search raises inside sqlite, and sqlite reports only that some user function failed. Same for coverage json --contexts=, coverage html --contexts=, the [report] contexts config setting, and Coverage.report(contexts=[...]).

The fix

process_regexlist in config.py already does exactly this for every other regex-valued setting:

try:
    re.compile(value)
except re.error as e:
    raise ConfigError(f"Invalid [{name}].{option} value {value!r}: {e}") from e

contexts is the one that never got it, because it is not compiled at config time, it is handed to sqlite later. So the same check runs in set_query_contexts, which is the point every entry path funnels through.

Testing

test_set_query_contexts_bad_regex in tests/test_data.py.

Removing only the validation loop fails it, and the failure is the original symptom rather than a message mismatch:

        with pytest.raises(ConfigError, match=msg):
>           raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc
E           coverage.exceptions.DataError: Couldn't use data file '...': user-defined function raised exception

pytest tests/test_data.py is 4 passed for the selected tests.

Pre-existing failures

The full suite is 143 failed, 1450 passed, 23 skipped, 14 errors on a clean checkout here, and 143 failed, 1451 passed, 23 skipped, 14 errors with this change. A diff of the sorted FAILED lists is empty, so every one of those is pre-existing in this environment, which has no C extension built and lacks the venv/subprocess test dependencies.

Disclosure: written with AI assistance (Claude Code). I reproduced the confusing error at the CLI, checked all four entry paths, confirmed the pre-existing failures by diffing against a stashed tree, and ran the mutation check myself.

An invalid regex went straight to the SQLite REGEXP user function, so the
failure surfaced as a DataError blaming the data file. Every other
regex-valued setting is already validated via process_regexlist.
@nedbat

nedbat commented Aug 16, 2026

Copy link
Copy Markdown
Member

Can you tell me what you mean by "the mutation check"?

@VXNCXNX

VXNCXNX commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Sorry, that was jargon on my part. I just mean I checked that the new test actually fails when the fix is removed, instead of assuming it does.

Concretely, I deleted only these five lines from set_query_contexts:

for context in contexts:
    try:
        re.compile(context)
    except re.error as e:
        raise ConfigError(f"Invalid context regex {context!r}: {e}") from e

and re-ran the test. It fails, and it fails with the original symptom rather than a message mismatch:

E   coverage.exceptions.DataError: Couldn't use data file '/tmp/.../.coverage': user-defined function raised exception
coverage/sqlitedb.py:132: DataError

Then I put the lines back and it passes again. I re-ran both just now to make sure I was quoting you the real output.

The reason I bother: I have written tests before that passed no matter what the source did, so the test proved nothing. Reverting the source and confirming the specific test goes red is the cheapest way to catch that. Reverting the whole change is weaker, since it can fail for an unrelated reason like an unused import.

@nedbat

nedbat commented Aug 18, 2026

Copy link
Copy Markdown
Member

Thanks, I do the same thing to confirm that my tests are correct.

@nedbat
nedbat merged commit 93fc29d into coveragepy:main Aug 18, 2026
72 checks passed
@nedbat

nedbat commented Aug 28, 2026

Copy link
Copy Markdown
Member

This is now released as part of coverage 7.16.0.

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.

2 participants