Describe the bug
When dynamic contexts are set through the switch_context() API instead of being declared in config, the sysmon core silently keeps only the first context that hits a line and drops the rest. There is no warning and no fallback to the default core.
I know sysmon doesn't support dynamic contexts in general, that part is documented. The actual problem is the asymmetry: the no-sysmon guard only fires for contexts declared in config (dynamic_context = ...), where coverage warns and falls back to the default core. If you set contexts programmatically with switch_context() and config_file=False, that guard never triggers, so you silently get wrong per-context data instead of either a warning or a clean fallback. On Python 3.14, where sysmon is the default core, this happens by default.
The practical impact: a shared helper called by two tests gets attributed to only one of them. For anything that uses contexts to answer "which tests touch this line" (test selection, impact analysis), that is a silent false negative.
To Reproduce
Save the script in question 4 below as repro.py, then run it once under each core:
pip install coverage==7.14.1
COVERAGE_CORE=sysmon python repro.py
COVERAGE_CORE=ctrace python repro.py
helper() is called by both test_a and test_b, so its line must carry both contexts. Under ctrace it does. Under sysmon the second context is silently dropped, with no warning printed. Full details below.
Answer the questions below:
-
What version of Python are you using?
Python 3.14.4
-
What version of coverage.py shows the problem?
coverage 7.14.1. coverage debug sys:
coverage_version: 7.14.1
CTracer: available
python: 3.14.4 (tags/v3.14.4:23116f9, Apr 7 2026) [MSC v.1944 64 bit (AMD64)]
platform: Windows-11-10.0.26200-SP0
implementation: CPython
-
What versions of what packages do you have installed?
coverage==7.14.1
pytest==8.4.2
-
What code shows the problem?
No repo to clone, it is a single self-contained script. Save it as repro.py:
python
import os
import sys
import tempfile
import textwrap
import coverage
# a tiny module: helper() is shared by two callers, one per "test"
d = tempfile.mkdtemp()
with open(os.path.join(d, "shared.py"), "w") as f:
f.write(textwrap.dedent("""
def helper():
return 42
def used_by_a():
return helper()
def used_by_b():
return helper()
"""))
sys.path.insert(0, d)
import shared
# set contexts through the API, not via config (config_file=False)
cov = coverage.Coverage(source=["shared"], config_file=False)
cov.start()
cov.switch_context("test_a")
shared.used_by_a()
cov.switch_context("test_b")
shared.used_by_b()
cov.switch_context("")
cov.stop()
data = cov.get_data()
path = [x for x in data.measured_files() if x.endswith("shared.py")][0]
cbl = data.contexts_by_lineno(path)
print("COVERAGE_CORE =", os.environ.get("COVERAGE_CORE", "(default core)"))
src = open(path).read().splitlines()
for ln in sorted(cbl):
ctx = sorted(c for c in cbl[ln] if c)
if ctx:
print(f" line {ln} ({src[ln-1].strip()}): {ctx}")
-
What commands should we run to reproduce the problem?
pip install coverage==7.14.1
COVERAGE_CORE=sysmon python repro.py
COVERAGE_CORE=ctrace python repro.py
Output:
$ COVERAGE_CORE=sysmon python repro.py
COVERAGE_CORE = sysmon
line 3 (return 42): ['test_a']
line 6 (return helper()): ['test_a']
line 9 (return helper()): ['test_b']
$ COVERAGE_CORE=ctrace python repro.py
COVERAGE_CORE = ctrace
line 3 (return 42): ['test_a', 'test_b']
line 6 (return helper()): ['test_a']
line 9 (return helper()): ['test_b']
helper() is reached by both test_a and test_b. Under ctrace line 3 is correctly attributed to both. Under sysmon test_b is silently gone.
Expected behavior
For config-declared dynamic contexts coverage already warns and falls back ("Can't use core=sysmon: it doesn't yet support dynamic contexts, using default core"). I would expect the same when contexts are set through switch_context(): either warn and fall back to the default core, or at the very least document that programmatic contexts are silently unsupported under sysmon. Right now the API path has no guard at all, so you just get wrong data with no signal.
Additional context
On Windows, python repro.py with the inline COVERAGE_CORE=... works in bash. In PowerShell set it first with $env:COVERAGE_CORE = "sysmon" then run, or use cmd set COVERAGE_CORE=sysmon.
Describe the bug
When dynamic contexts are set through the
switch_context()API instead of being declared in config, the sysmon core silently keeps only the first context that hits a line and drops the rest. There is no warning and no fallback to the default core.I know sysmon doesn't support dynamic contexts in general, that part is documented. The actual problem is the asymmetry: the no-sysmon guard only fires for contexts declared in config (
dynamic_context = ...), where coverage warns and falls back to the default core. If you set contexts programmatically withswitch_context()andconfig_file=False, that guard never triggers, so you silently get wrong per-context data instead of either a warning or a clean fallback. On Python 3.14, where sysmon is the default core, this happens by default.The practical impact: a shared helper called by two tests gets attributed to only one of them. For anything that uses contexts to answer "which tests touch this line" (test selection, impact analysis), that is a silent false negative.
To Reproduce
Save the script in question 4 below as
repro.py, then run it once under each core:helper()is called by bothtest_aandtest_b, so its line must carry both contexts. Under ctrace it does. Under sysmon the second context is silently dropped, with no warning printed. Full details below.Answer the questions below:
What version of Python are you using?
Python 3.14.4
What version of coverage.py shows the problem?
coverage 7.14.1.
coverage debug sys:What versions of what packages do you have installed?
What code shows the problem?
No repo to clone, it is a single self-contained script. Save it as
repro.py:What commands should we run to reproduce the problem?
Output:
helper()is reached by bothtest_aandtest_b. Under ctrace line 3 is correctly attributed to both. Under sysmontest_bis silently gone.Expected behavior
For config-declared dynamic contexts coverage already warns and falls back ("Can't use core=sysmon: it doesn't yet support dynamic contexts, using default core"). I would expect the same when contexts are set through
switch_context(): either warn and fall back to the default core, or at the very least document that programmatic contexts are silently unsupported under sysmon. Right now the API path has no guard at all, so you just get wrong data with no signal.Additional context
On Windows,
python repro.pywith the inlineCOVERAGE_CORE=...works in bash. In PowerShell set it first with$env:COVERAGE_CORE = "sysmon"then run, or use cmdset COVERAGE_CORE=sysmon.