Skip to content

Commit 2ca705b

Browse files
committed
simplify, rely on env var everywhere. Stop using findspec
1 parent e506d7d commit 2ca705b

11 files changed

Lines changed: 55 additions & 84 deletions

File tree

‎Lib/test/libregrtest/main.py‎

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,7 @@ def _rerun_failed_tests(self, runtests: RunTests) -> RunTests:
285285
forever=False,
286286
fail_fast=False,
287287
match_tests_dict=match_tests_dict,
288-
output_on_failure=False,
289-
github_annotations=bool(os.environ.get("GITHUB_STEP_SUMMARY")))
288+
output_on_failure=False)
290289
self.logger.set_tests(runtests)
291290

292291
msg = f"Re-running {len(tests)} failed tests in verbose mode"
@@ -397,8 +396,7 @@ def run_test(
397396
result = run_single_test(test_name, runtests)
398397

399398
self.results.accumulate_result(result, runtests)
400-
if runtests.github_annotations:
401-
result.print_github_annotation(runtests)
399+
result.print_github_annotation(runtests)
402400

403401
return result
404402

@@ -540,13 +538,6 @@ def display_summary(self) -> None:
540538
print(f"Result: {state}")
541539

542540
def create_run_tests(self, tests: TestTuple) -> RunTests:
543-
# Annotate test failures in the GitHub Actions job log of the last
544-
# run (the re-run, if any), if it reports failures: -v, -W or --pgo
545-
will_rerun = self.want_rerun and not self.python_cmd
546-
github_annotations = (bool(os.environ.get("GITHUB_STEP_SUMMARY"))
547-
and not will_rerun
548-
and bool(self.verbose or self.output_on_failure
549-
or self.pgo))
550541
return RunTests(
551542
tests,
552543
fail_fast=self.fail_fast,
@@ -564,7 +555,6 @@ def create_run_tests(self, tests: TestTuple) -> RunTests:
564555
hunt_refleak=self.hunt_refleak,
565556
test_dir=self.test_dir,
566557
use_junit=(self.junit_filename is not None),
567-
github_annotations=github_annotations,
568558
coverage=self.coverage,
569559
memory_limit=self.memory_limit,
570560
gc_threshold=self.gc_threshold,
@@ -627,10 +617,16 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
627617
if use_load_tracker:
628618
self.logger.start_load_tracker()
629619
try:
630-
if self.num_workers:
631-
self._run_tests_mp(runtests, self.num_workers)
632-
else:
633-
self.run_tests_sequentially(runtests)
620+
with os_helper.EnvironmentVarGuard() as env:
621+
# In GitHub Actions, only annotate failures of the last run:
622+
# not the first run if failed tests will be re-run (with
623+
# --python, they are not: see rerun_failed_tests())
624+
if self.want_rerun and not self.python_cmd:
625+
env.unset("GITHUB_STEP_SUMMARY")
626+
if self.num_workers:
627+
self._run_tests_mp(runtests, self.num_workers)
628+
else:
629+
self.run_tests_sequentially(runtests)
634630

635631
coverage = self.results.get_coverage_results()
636632
self.display_result(runtests)

‎Lib/test/libregrtest/result.py‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import dataclasses
2-
import importlib.util
32
import json
3+
import os
44
from _colorize import get_colors # type: ignore[import-not-found]
55
from typing import Any
66

7+
from .findtests import findtestdir
78
from .runtests import RunTests
89
from .utils import (
9-
StrJSON, TestName, FilterTuple, abs_module_name,
10-
format_duration, normalize_test_name, print_warning, github_annotation)
10+
StrJSON, TestName, FilterTuple,
11+
format_duration, normalize_test_name, print_warning,
12+
print_github_annotation)
1113

1214

1315
@dataclasses.dataclass(slots=True)
@@ -190,11 +192,14 @@ def print_github_annotation(self, runtests: RunTests) -> None:
190192
if (self.is_failed(runtests.fail_env_changed)
191193
and not self.errors and not self.failures):
192194
message = "\n".join([str(self), *(self.env_changed_reasons or ())])
193-
spec = importlib.util.find_spec(
194-
abs_module_name(self.test_name, runtests.test_dir))
195-
filename = spec.origin if spec is not None else None
196-
print(github_annotation(self.test_name, message, filename),
197-
flush=True)
195+
# Test file: "test_x" is test_x.py or test_x/__init__.py,
196+
# "test.test_x.test_y" is test_x/test_y.py
197+
name = self.test_name.removeprefix("test.")
198+
path = os.path.join(findtestdir(runtests.test_dir), *name.split("."))
199+
filename = path + ".py"
200+
if not os.path.exists(filename):
201+
filename = os.path.join(path, "__init__.py")
202+
print_github_annotation(self.test_name, message, filename)
198203

199204
def set_env_changed(self, *reasons):
200205
if self.state is None or self.state == State.PASSED:

‎Lib/test/libregrtest/run_workers.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -625,8 +625,7 @@ def _process_result(self, item: QueueOutput) -> TestResult:
625625
if stdout:
626626
print(stdout, flush=True)
627627
# Annotate after the output: env changed warnings, crash traceback
628-
if self.runtests.github_annotations:
629-
result.print_github_annotation(self.runtests)
628+
result.print_github_annotation(self.runtests)
630629

631630
return result
632631

‎Lib/test/libregrtest/runtests.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ class RunTests:
9393
hunt_refleak: HuntRefleak | None
9494
test_dir: StrPath | None
9595
use_junit: bool
96-
# Annotate test failures in the GitHub Actions job log
97-
github_annotations: bool
9896
coverage: bool
9997
memory_limit: str | None
10098
gc_threshold: int | None

‎Lib/test/libregrtest/save_env.py‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -139,18 +139,6 @@ def restore_os_environ(self, saved_environ):
139139
os.environ = saved_environ[1]
140140
os.environ.clear()
141141
os.environ.update(saved_environ[2])
142-
@staticmethod
143-
def describe_os_environ(original, current):
144-
# Only list the keys: values can be secrets
145-
before, after = original[2], current[2]
146-
changes = (
147-
('added', after.keys() - before.keys()),
148-
('removed', before.keys() - after.keys()),
149-
('changed', {key for key in before.keys() & after.keys()
150-
if before[key] != after[key]}),
151-
)
152-
return '; '.join(f'{change} {", ".join(sorted(keys))}'
153-
for change, keys in changes if keys)
154142

155143
def get_sys_path(self):
156144
return id(sys.path), sys.path, sys.path[:]
@@ -359,12 +347,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
359347
current = get()
360348
# Check for changes to the resource's value
361349
if current != original:
362-
reason = f"{name} was modified"
363-
if name == 'os.environ':
364-
delta = self.describe_os_environ(original, current)
365-
if delta:
366-
reason = f"{reason}: {delta}"
367-
support.set_environment_altered(reason)
350+
support.set_environment_altered(f"{name} was modified")
368351
restore(original)
369352
if not self.quiet and not self.pgo:
370353
print_warning(

‎Lib/test/libregrtest/setup.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ def setup_tests(runtests: RunTests) -> None:
118118
RegressionTestResult.USE_XML = True
119119
else:
120120
support.junit_xml_list = None
121-
RegressionTestResult.GITHUB_ANNOTATIONS = runtests.github_annotations
122121

123122
if runtests.memory_limit is not None:
124123
support.set_memlimit(runtests.memory_limit)

‎Lib/test/libregrtest/testresult.py‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
import traceback
1111
import unittest
1212
from test import support
13-
from test.libregrtest.utils import github_annotation, sanitize_xml
13+
from test.libregrtest.utils import print_github_annotation, sanitize_xml
1414

1515
class RegressionTestResult(unittest.TextTestResult):
1616
USE_XML = False
17-
GITHUB_ANNOTATIONS = False
1817

1918
def __init__(self, stream, descriptions, verbosity):
2019
super().__init__(stream=stream, descriptions=descriptions,
@@ -135,11 +134,10 @@ def addUnexpectedSuccess(self, test):
135134
def printErrorList(self, flavour, errors):
136135
for test, err in errors:
137136
super().printErrorList(flavour, [(test, err)])
138-
if self.GITHUB_ANNOTATIONS:
139-
# Write the annotation just after the failure report, so
140-
# that it links to the report in the job log
141-
self.stream.writeln(github_annotation(str(test), err,
142-
self._test_file(test)))
137+
# Annotate just after the failure report, so that the annotation
138+
# links to the report in the job log
139+
print_github_annotation(str(test), err, self._test_file(test),
140+
file=self.stream)
143141

144142
@staticmethod
145143
def _test_file(test):

‎Lib/test/libregrtest/utils.py‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,25 +153,25 @@ def repository_path(filename: str) -> str:
153153
return "Lib/" + filename.removeprefix(stdlib_dir).replace(os.sep, "/")
154154

155155

156-
def github_annotation(title: str, message: str,
157-
filename: str | None) -> str:
158-
"""Format a GitHub Actions error annotation.
156+
# Escape the data and the properties of GitHub Actions workflow commands
157+
GITHUB_ESCAPE_DATA = str.maketrans({"%": "%25", "\r": "%0D", "\n": "%0A"})
158+
GITHUB_ESCAPE_PROPERTY = GITHUB_ESCAPE_DATA | str.maketrans({":": "%3A",
159+
",": "%2C"})
160+
161+
162+
def print_github_annotation(title: str, message: str, filename: str | None,
163+
file=None) -> None:
164+
"""Print a GitHub Actions error annotation, if run in GitHub Actions.
159165
160166
message is a traceback or a failure description. Only keep the exception
161167
which ends the traceback: the job log has the full traceback. Locate the
162168
annotation in filename, the test file, at the last frame of the traceback
163169
in this file, if any.
164170
"""
171+
if not os.environ.get("GITHUB_STEP_SUMMARY"):
172+
return
165173
if not filename:
166174
raise ValueError(f"missing test file of annotation {title!r}")
167-
168-
def escape(text: str) -> str:
169-
return (text.replace("%", "%25").replace("\r", "%0D")
170-
.replace("\n", "%0A"))
171-
172-
def escape_property(text: str) -> str:
173-
return escape(text).replace(":", "%3A").replace(",", "%2C")
174-
175175
message = decolor(message)
176176
props = {"file": repository_path(filename)}
177177
# Line of the last traceback frame (' File "filename", line 123') or
@@ -184,9 +184,10 @@ def escape_property(text: str) -> str:
184184
# ' File' line followed by its indented source lines
185185
message = re.split(r'^ File .*\n(?: .*\n)*', message,
186186
flags=re.MULTILINE)[-1].strip()
187-
props_text = ",".join(f"{key}={escape_property(value)}"
187+
props_text = ",".join(f"{key}={value.translate(GITHUB_ESCAPE_PROPERTY)}"
188188
for key, value in props.items())
189-
return f"::error {props_text}::{escape(message)}"
189+
print(f"::error {props_text}::{message.translate(GITHUB_ESCAPE_DATA)}",
190+
file=file, flush=True)
190191

191192

192193
def regrtest_unraisable_hook(unraisable) -> None:

‎Lib/test/libregrtest/worker.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ def create_worker_process(runtests: WorkerRunTests, output_fd: int,
2828
cmd.extend(['-m', 'test.libregrtest.worker', worker_json])
2929

3030
env = dict(os.environ)
31-
# Only the main process writes the GitHub Actions job summary
32-
env.pop('GITHUB_STEP_SUMMARY', None)
3331
if tmp_dir is not None:
3432
env['TMPDIR'] = tmp_dir
3533
env['TEMP'] = tmp_dir

‎Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import time
1212
import unittest
1313
from unittest import mock
14-
from test.support import requires, requires_remote_subprocess_debugging
14+
from test.support import (
15+
os_helper, requires, requires_remote_subprocess_debugging)
1516
from test.support.import_helper import import_module
1617

1718
# Only run these tests if curses is available
@@ -856,6 +857,9 @@ def mock_init_curses_side_effect(self, n_times, mock_self, stdscr):
856857

857858
def test_run_failed_module_live(self):
858859
"""Test that running a existing module that fails exits with clean error."""
860+
# Don't write to the GitHub Actions job summary of the test suite
861+
env = self.enterContext(os_helper.EnvironmentVarGuard())
862+
env.unset('GITHUB_STEP_SUMMARY')
859863

860864
args = [
861865
"profiling.sampling.cli", "run", "--live", "-m", "test",

0 commit comments

Comments
 (0)