Skip to content

Commit bdedca8

Browse files
committed
feat: core instead of tracer
The core of coverage.py is how it learns about execution. Now that we have sys.monitoring, it might not be a trace function. So instead of specifying the tracer to use, we specify the "core" to use.
1 parent bbf7461 commit bdedca8

File tree

5 files changed

+59
-43
lines changed

5 files changed

+59
-43
lines changed

coverage/collector.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@
3737
HAS_CTRACER = True
3838
except ImportError:
3939
# Couldn't import the C extension, maybe it isn't built.
40-
if os.getenv('COVERAGE_TEST_TRACER') == 'c': # pragma: part covered
41-
# During testing, we use the COVERAGE_TEST_TRACER environment variable
40+
if os.getenv("COVERATE_CORE") == "ctrace": # pragma: part covered
41+
# During testing, we use the COVERAGE_CORE environment variable
4242
# to indicate that we've fiddled with the environment to test this
4343
# fallback code. If we thought we had a C tracer, but couldn't import
4444
# it, then exit quickly and clearly instead of dribbling confusing
4545
# errors. I'm using sys.exit here instead of an exception because an
4646
# exception here causes all sorts of other noise in unittest.
47-
sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n")
47+
sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n")
4848
sys.exit(1)
4949
HAS_CTRACER = False
5050

@@ -141,28 +141,37 @@ def __init__(
141141
self._trace_class: Type[TTracer]
142142
self.file_disposition_class: Type[TFileDisposition]
143143

144-
use_ctracer = False
145-
if HAS_CTRACER and not timid:
146-
use_ctracer = True
147-
148-
if env.PYBEHAVIOR.pep669 and self.should_start_context is None:
144+
core: Optional[str]
145+
if timid:
146+
core = "pytrace"
147+
else:
148+
core = os.environ.get("COVERAGE_CORE")
149+
if not core:
150+
if env.PYBEHAVIOR.pep669 and self.should_start_context is None:
151+
core = "sysmon"
152+
elif HAS_CTRACER:
153+
core = "ctrace"
154+
155+
if core == "sysmon":
149156
self._trace_class = Pep669Tracer
150157
self.file_disposition_class = FileDisposition
151158
self.supports_plugins = False
152159
self.packed_arcs = False
153160
self.systrace = False
154-
elif use_ctracer:
161+
elif core == "ctrace":
155162
self._trace_class = CTracer
156163
self.file_disposition_class = CFileDisposition
157164
self.supports_plugins = True
158165
self.packed_arcs = True
159166
self.systrace = True
160-
else:
167+
elif core == "pytrace":
161168
self._trace_class = PyTracer
162169
self.file_disposition_class = FileDisposition
163170
self.supports_plugins = False
164171
self.packed_arcs = False
165172
self.systrace = True
173+
else:
174+
raise ConfigError(f"Unknown core value: {core!r}")
166175

167176
# We can handle a few concurrency options here, but only one at a time.
168177
concurrencies = set(self.concurrency)

coverage/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class PYBEHAVIOR:
120120
# Coverage.py specifics.
121121

122122
# Are we using the C-implemented trace function?
123-
C_TRACER = os.getenv("COVERAGE_TEST_TRACER", "c") == "c"
123+
C_TRACER = os.getenv("COVERAGE_CORE", "ctrace") == "ctrace"
124124

125125
# Are we coverage-measuring ourselves?
126126
METACOV = os.getenv("COVERAGE_COVERAGE", "") != ""

igor.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -103,38 +103,43 @@ def do_remove_extension(*args):
103103
print(f"Couldn't remove {filename}: {exc}")
104104

105105

106-
def label_for_tracer(tracer):
106+
def label_for_core(core):
107107
"""Get the label for these tests."""
108-
if tracer == "py":
109-
label = "with Python tracer"
108+
if core == "pytrace":
109+
return "with Python tracer"
110+
elif core == "ctrace":
111+
return "with C tracer"
112+
elif core == "sysmon":
113+
return "with sys.monitoring"
110114
else:
111-
label = "with C tracer"
115+
raise ValueError(f"Bad core: {core!r}")
112116

113-
return label
114117

115-
116-
def should_skip(tracer):
118+
def should_skip(core):
117119
"""Is there a reason to skip these tests?"""
118120
skipper = ""
119121

120-
# $set_env.py: COVERAGE_ONE_TRACER - Only run tests for one tracer.
121-
only_one = os.environ.get("COVERAGE_ONE_TRACER")
122+
# $set_env.py: COVERAGE_ONE_CORE - Only run tests for one core.
123+
only_one = os.environ.get("COVERAGE_ONE_CORE")
122124
if only_one:
123125
if CPYTHON:
124-
if tracer == "py":
125-
skipper = "Only one tracer: no Python tracer for CPython"
126+
if sys.version_info >= (3, 12):
127+
if core != "sysmon":
128+
skipper = f"Only one core: not running {core}"
129+
elif core != "ctrace":
130+
skipper = f"Only one core: not running {core}"
126131
else:
127-
if tracer == "c":
128-
skipper = f"No C tracer for {platform.python_implementation()}"
129-
elif tracer == "py":
132+
if core != "pytrace":
133+
skipper = f"No C core for {platform.python_implementation()}"
134+
elif core == "pytrace":
130135
# $set_env.py: COVERAGE_NO_PYTRACER - Don't run the tests under the Python tracer.
131136
skipper = os.environ.get("COVERAGE_NO_PYTRACER")
132137
else:
133138
# $set_env.py: COVERAGE_NO_CTRACER - Don't run the tests under the C tracer.
134139
skipper = os.environ.get("COVERAGE_NO_CTRACER")
135140

136141
if skipper:
137-
msg = "Skipping tests " + label_for_tracer(tracer)
142+
msg = "Skipping tests " + label_for_core(core)
138143
if len(skipper) > 1:
139144
msg += ": " + skipper
140145
else:
@@ -143,26 +148,26 @@ def should_skip(tracer):
143148
return msg
144149

145150

146-
def make_env_id(tracer):
151+
def make_env_id(core):
147152
"""An environment id that will keep all the test runs distinct."""
148153
impl = platform.python_implementation().lower()
149154
version = "%s%s" % sys.version_info[:2]
150155
if PYPY:
151156
version += "_%s%s" % sys.pypy_version_info[:2]
152-
env_id = f"{impl}{version}_{tracer}"
157+
env_id = f"{impl}{version}_{core}"
153158
return env_id
154159

155160

156-
def run_tests(tracer, *runner_args):
161+
def run_tests(core, *runner_args):
157162
"""The actual running of tests."""
158163
if "COVERAGE_TESTING" not in os.environ:
159164
os.environ["COVERAGE_TESTING"] = "True"
160-
print_banner(label_for_tracer(tracer))
165+
print_banner(label_for_core(core))
161166

162167
return pytest.main(list(runner_args))
163168

164169

165-
def run_tests_with_coverage(tracer, *runner_args):
170+
def run_tests_with_coverage(core, *runner_args):
166171
"""Run tests, but with coverage."""
167172
# Need to define this early enough that the first import of env.py sees it.
168173
os.environ["COVERAGE_TESTING"] = "True"
@@ -172,7 +177,7 @@ def run_tests_with_coverage(tracer, *runner_args):
172177
if context:
173178
if context[0] == "$":
174179
context = os.environ[context[1:]]
175-
os.environ["COVERAGE_CONTEXT"] = context + "." + tracer
180+
os.environ["COVERAGE_CONTEXT"] = context + "." + core
176181

177182
# Create the .pth file that will let us measure coverage in sub-processes.
178183
# The .pth file seems to have to be alphabetically after easy-install.pth
@@ -183,7 +188,7 @@ def run_tests_with_coverage(tracer, *runner_args):
183188
with open(pth_path, "w") as pth_file:
184189
pth_file.write("import coverage; coverage.process_startup()\n")
185190

186-
suffix = f"{make_env_id(tracer)}_{platform.platform()}"
191+
suffix = f"{make_env_id(core)}_{platform.platform()}"
187192
os.environ["COVERAGE_METAFILE"] = os.path.abspath(".metacov." + suffix)
188193

189194
import coverage
@@ -211,7 +216,7 @@ def run_tests_with_coverage(tracer, *runner_args):
211216
sys.modules.update(covmods)
212217

213218
# Run tests, with the arguments from our command line.
214-
status = run_tests(tracer, *runner_args)
219+
status = run_tests(core, *runner_args)
215220

216221
finally:
217222
cov.stop()
@@ -240,19 +245,19 @@ def do_combine_html():
240245
cov.html_report(show_contexts=show_contexts)
241246

242247

243-
def do_test_with_tracer(tracer, *runner_args):
244-
"""Run tests with a particular tracer."""
248+
def do_test_with_core(core, *runner_args):
249+
"""Run tests with a particular core."""
245250
# If we should skip these tests, skip them.
246-
skip_msg = should_skip(tracer)
251+
skip_msg = should_skip(core)
247252
if skip_msg:
248253
print(skip_msg)
249254
return None
250255

251-
os.environ["COVERAGE_TEST_TRACER"] = tracer
256+
os.environ["COVERAGE_CORE"] = core
252257
if os.environ.get("COVERAGE_COVERAGE", "no") == "yes":
253-
return run_tests_with_coverage(tracer, *runner_args)
258+
return run_tests_with_coverage(core, *runner_args)
254259
else:
255-
return run_tests(tracer, *runner_args)
260+
return run_tests(core, *runner_args)
256261

257262

258263
def do_zip_mods():

tests/test_process.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ def test_timid(self) -> None:
516516
assert py_out == "None\n"
517517

518518
cov_out = self.run_command("coverage run showtrace.py")
519-
if os.environ.get('COVERAGE_TEST_TRACER', 'c') == 'c':
519+
if env.C_TRACER:
520520
# If the C trace function is being tested, then regular running should have
521521
# the C function, which registers itself as f_trace.
522522
assert cov_out == "CTracer\n"

tox.ini

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ commands =
4141
# Build the C extension and test with the CTracer
4242
python setup.py --quiet build_ext --inplace
4343
python -m pip install {env:COVERAGE_PIP_ARGS} -q -e .
44-
python igor.py test_with_tracer c {posargs}
44+
python igor.py test_with_core ctrace {posargs}
45+
46+
py3{12}: python igor.py test_with_core sysmon {posargs}
4547

4648
# Remove the C extension so that we can test the PyTracer
4749
python igor.py remove_extension
4850

4951
# Test with the PyTracer
50-
python igor.py test_with_tracer py {posargs}
52+
python igor.py test_with_core pytrace {posargs}
5153

5254
[testenv:anypy]
5355
# $set_env.py: COVERAGE_ANYPY - The custom Python for "tox -e anypy"

0 commit comments

Comments
 (0)