Skip to content

Commit 9317794

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 4d2db10 commit 9317794

File tree

5 files changed

+75
-52
lines changed

5 files changed

+75
-52
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ lint: ## Run linters and checkers.
5858
PYTEST_SMOKE_ARGS = -n auto -m "not expensive" --maxfail=3 $(ARGS)
5959

6060
smoke: ## Run tests quickly with the C tracer in the lowest supported Python versions.
61-
COVERAGE_NO_PYTRACER=1 tox -q -e py38 -- $(PYTEST_SMOKE_ARGS)
61+
COVERAGE_TEST_CORES=ctrace tox -q -e py38 -- $(PYTEST_SMOKE_ARGS)
6262

6363

6464
##@ Metacov: coverage measurement of coverage.py itself
@@ -73,7 +73,7 @@ metahtml: ## Produce meta-coverage HTML reports.
7373
python igor.py combine_html
7474

7575
metasmoke:
76-
COVERAGE_NO_PYTRACER=1 ARGS="-e py39" make metacov metahtml
76+
COVERAGE_TEST_CORES=ctrace ARGS="-e py39" make metacov metahtml
7777

7878

7979
##@ Requirements management

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("COVERAGE_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.getenv("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)

doc/contributing.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ can combine tox and pytest options::
173173
py310: OK (17.99 seconds)
174174
congratulations :) (19.09 seconds)
175175

176+
TODO: Update this for CORE instead of TRACER
177+
176178
You can also affect the test runs with environment variables. Define any of
177179
these as 1 to use them:
178180

@@ -191,7 +193,7 @@ these as 1 to use them:
191193
There are other environment variables that affect tests. I use `set_env.py`_
192194
as a simple terminal interface to see and set them.
193195

194-
Of course, run all the tests on every version of Python you have, before
196+
Of course, run all the tests on every version of Python you have before
195197
submitting a change.
196198

197199
.. _pytest test selectors: https://doc.pytest.org/en/stable/usage.html#specifying-which-tests-to-run

igor.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -103,38 +103,48 @@ 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

118+
def should_skip(core):
119+
"""Is there a reason to skip these tests?
115120
116-
def should_skip(tracer):
117-
"""Is there a reason to skip these tests?"""
121+
Return empty string to run tests, or a message about why we are skipping
122+
the tests.
123+
"""
118124
skipper = ""
119125

120-
# $set_env.py: COVERAGE_ONE_TRACER - Only run tests for one tracer.
121-
only_one = os.getenv("COVERAGE_ONE_TRACER")
122-
if only_one:
123-
if CPYTHON:
124-
if tracer == "py":
125-
skipper = "Only one tracer: no Python tracer for CPython"
126-
else:
127-
if tracer == "c":
128-
skipper = f"No C tracer for {platform.python_implementation()}"
129-
elif tracer == "py":
130-
# $set_env.py: COVERAGE_NO_PYTRACER - Don't run the tests under the Python tracer.
131-
skipper = os.getenv("COVERAGE_NO_PYTRACER")
126+
# $set_env.py: COVERAGE_TEST_CORES - List of cores to run
127+
test_cores = os.getenv("COVERAGE_TEST_CORES")
128+
if test_cores:
129+
if core not in test_cores:
130+
skipper = f"core {core} not in COVERAGE_TEST_CORES={test_cores}"
131+
132132
else:
133-
# $set_env.py: COVERAGE_NO_CTRACER - Don't run the tests under the C tracer.
134-
skipper = os.getenv("COVERAGE_NO_CTRACER")
133+
# $set_env.py: COVERAGE_ONE_CORE - Only run tests for one core.
134+
only_one = os.getenv("COVERAGE_ONE_CORE")
135+
if only_one:
136+
if CPYTHON:
137+
if sys.version_info >= (3, 12):
138+
if core != "sysmon":
139+
skipper = f"Only one core: not running {core}"
140+
elif core != "ctrace":
141+
skipper = f"Only one core: not running {core}"
142+
else:
143+
if core != "pytrace":
144+
skipper = f"No C core for {platform.python_implementation()}"
135145

136146
if skipper:
137-
msg = "Skipping tests " + label_for_tracer(tracer)
147+
msg = "Skipping tests " + label_for_core(core)
138148
if len(skipper) > 1:
139149
msg += ": " + skipper
140150
else:
@@ -143,26 +153,26 @@ def should_skip(tracer):
143153
return msg
144154

145155

146-
def make_env_id(tracer):
156+
def make_env_id(core):
147157
"""An environment id that will keep all the test runs distinct."""
148158
impl = platform.python_implementation().lower()
149159
version = "%s%s" % sys.version_info[:2]
150160
if PYPY:
151161
version += "_%s%s" % sys.pypy_version_info[:2]
152-
env_id = f"{impl}{version}_{tracer}"
162+
env_id = f"{impl}{version}_{core}"
153163
return env_id
154164

155165

156-
def run_tests(tracer, *runner_args):
166+
def run_tests(core, *runner_args):
157167
"""The actual running of tests."""
158168
if "COVERAGE_TESTING" not in os.environ:
159169
os.environ["COVERAGE_TESTING"] = "True"
160-
print_banner(label_for_tracer(tracer))
170+
print_banner(label_for_core(core))
161171

162172
return pytest.main(list(runner_args))
163173

164174

165-
def run_tests_with_coverage(tracer, *runner_args):
175+
def run_tests_with_coverage(core, *runner_args):
166176
"""Run tests, but with coverage."""
167177
# Need to define this early enough that the first import of env.py sees it.
168178
os.environ["COVERAGE_TESTING"] = "True"
@@ -172,7 +182,7 @@ def run_tests_with_coverage(tracer, *runner_args):
172182
if context:
173183
if context[0] == "$":
174184
context = os.environ[context[1:]]
175-
os.environ["COVERAGE_CONTEXT"] = context + "." + tracer
185+
os.environ["COVERAGE_CONTEXT"] = context + "." + core
176186

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

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

189199
import coverage
@@ -211,7 +221,7 @@ def run_tests_with_coverage(tracer, *runner_args):
211221
sys.modules.update(covmods)
212222

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

216226
finally:
217227
cov.stop()
@@ -240,19 +250,19 @@ def do_combine_html():
240250
cov.html_report(show_contexts=show_contexts)
241251

242252

243-
def do_test_with_tracer(tracer, *runner_args):
244-
"""Run tests with a particular tracer."""
253+
def do_test_with_core(core, *runner_args):
254+
"""Run tests with a particular core."""
245255
# If we should skip these tests, skip them.
246-
skip_msg = should_skip(tracer)
256+
skip_msg = should_skip(core)
247257
if skip_msg:
248258
print(skip_msg)
249259
return None
250260

251-
os.environ["COVERAGE_TEST_TRACER"] = tracer
261+
os.environ["COVERAGE_CORE"] = core
252262
if os.getenv("COVERAGE_COVERAGE", "no") == "yes":
253-
return run_tests_with_coverage(tracer, *runner_args)
263+
return run_tests_with_coverage(core, *runner_args)
254264
else:
255-
return run_tests(tracer, *runner_args)
265+
return run_tests(core, *runner_args)
256266

257267

258268
def do_zip_mods():

tox.ini

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ install_command = python -m pip install -U {opts} {packages}
2626

2727
passenv = *
2828
setenv =
29-
pypy3{,8,9,10}: COVERAGE_NO_CTRACER=no C extension under PyPy
29+
pypy3{,8,9,10}: COVERAGE_TEST_CORES=pytrace
3030
# For some tests, we need .pyc files written in the current directory,
3131
# so override any local setting.
3232
PYTHONPYCACHEPREFIX=
@@ -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)