summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2023-09-30 22:37:23 (GMT)
committerGitHub <noreply@github.com>2023-09-30 22:37:23 (GMT)
commit53eb9a676f8c59b206dfc536b7590f6563ad65e0 (patch)
tree818bf98fb768d9f230b6375b9fff7102ce6849dc
parentd3728ddc572fff7ffcc95301bf5265717dbaf476 (diff)
downloadcpython-53eb9a676f8c59b206dfc536b7590f6563ad65e0.zip
cpython-53eb9a676f8c59b206dfc536b7590f6563ad65e0.tar.gz
cpython-53eb9a676f8c59b206dfc536b7590f6563ad65e0.tar.bz2
gh-110152: regrtest handles cross compilation and HOSTRUNNER (#110156)
* _add_python_opts() now handles cross compilation and HOSTRUNNER. * display_header() now tells if Python is cross-compiled, display HOSTRUNNER, and get the host platform. * Remove Tools/scripts/run_tests.py script. * Remove "make hostrunnertest": use "make buildbottest" or "make test" instead.
-rw-r--r--Lib/test/libregrtest/main.py98
-rw-r--r--Lib/test/libregrtest/utils.py39
-rw-r--r--Lib/test/test_regrtest.py8
-rw-r--r--Makefile.pre.in7
-rw-r--r--Misc/NEWS.d/next/Tests/2023-09-30-20-18-38.gh-issue-110152.4Kxve1.rst5
-rw-r--r--Tools/scripts/run_tests.py78
6 files changed, 125 insertions, 110 deletions
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py
index dcb2c58..19bf235 100644
--- a/Lib/test/libregrtest/main.py
+++ b/Lib/test/libregrtest/main.py
@@ -3,6 +3,7 @@ import random
import re
import shlex
import sys
+import sysconfig
import time
from test import support
@@ -22,6 +23,7 @@ from .utils import (
strip_py_suffix, count, format_duration,
printlist, get_temp_dir, get_work_dir, exit_timeout,
display_header, cleanup_temp_dir, print_warning,
+ is_cross_compiled, get_host_runner,
MS_WINDOWS, EXIT_TIMEOUT)
@@ -71,10 +73,9 @@ class Regrtest:
self.want_rerun: bool = ns.rerun
self.want_run_leaks: bool = ns.runleaks
- ci_mode = (ns.fast_ci or ns.slow_ci)
+ self.ci_mode: bool = (ns.fast_ci or ns.slow_ci)
self.want_add_python_opts: bool = (_add_python_opts
- and ns._add_python_opts
- and ci_mode)
+ and ns._add_python_opts)
# Select tests
if ns.match_tests:
@@ -431,7 +432,7 @@ class Regrtest:
if (self.want_header
or not(self.pgo or self.quiet or self.single_test_run
or tests or self.cmdline_args)):
- display_header(self.use_resources)
+ display_header(self.use_resources, self.python_cmd)
if self.randomize:
print("Using random seed", self.random_seed)
@@ -489,8 +490,56 @@ class Regrtest:
# processes.
return self._run_tests(selected, tests)
- def _add_python_opts(self):
- python_opts = []
+ def _add_cross_compile_opts(self, regrtest_opts):
+ # WASM/WASI buildbot builders pass multiple PYTHON environment
+ # variables such as PYTHONPATH and _PYTHON_HOSTRUNNER.
+ keep_environ = bool(self.python_cmd)
+ environ = None
+
+ # Are we using cross-compilation?
+ cross_compile = is_cross_compiled()
+
+ # Get HOSTRUNNER
+ hostrunner = get_host_runner()
+
+ if cross_compile:
+ # emulate -E, but keep PYTHONPATH + cross compile env vars,
+ # so test executable can load correct sysconfigdata file.
+ keep = {
+ '_PYTHON_PROJECT_BASE',
+ '_PYTHON_HOST_PLATFORM',
+ '_PYTHON_SYSCONFIGDATA_NAME',
+ 'PYTHONPATH'
+ }
+ old_environ = os.environ
+ new_environ = {
+ name: value for name, value in os.environ.items()
+ if not name.startswith(('PYTHON', '_PYTHON')) or name in keep
+ }
+ # Only set environ if at least one variable was removed
+ if new_environ != old_environ:
+ environ = new_environ
+ keep_environ = True
+
+ if cross_compile and hostrunner:
+ if self.num_workers == 0:
+ # For now use only two cores for cross-compiled builds;
+ # hostrunner can be expensive.
+ regrtest_opts.extend(['-j', '2'])
+
+ # If HOSTRUNNER is set and -p/--python option is not given, then
+ # use hostrunner to execute python binary for tests.
+ if not self.python_cmd:
+ buildpython = sysconfig.get_config_var("BUILDPYTHON")
+ python_cmd = f"{hostrunner} {buildpython}"
+ regrtest_opts.extend(["--python", python_cmd])
+ keep_environ = True
+
+ return (environ, keep_environ)
+
+ def _add_ci_python_opts(self, python_opts, keep_environ):
+ # --fast-ci and --slow-ci add options to Python:
+ # "-u -W default -bb -E"
# Unbuffered stdout and stderr
if not sys.stdout.write_through:
@@ -504,32 +553,27 @@ class Regrtest:
if sys.flags.bytes_warning < 2:
python_opts.append('-bb')
- # WASM/WASI buildbot builders pass multiple PYTHON environment
- # variables such as PYTHONPATH and _PYTHON_HOSTRUNNER.
- if not self.python_cmd:
+ if not keep_environ:
# Ignore PYTHON* environment variables
if not sys.flags.ignore_environment:
python_opts.append('-E')
- if not python_opts:
- return
-
- cmd = [*sys.orig_argv, "--dont-add-python-opts"]
- cmd[1:1] = python_opts
-
+ def _execute_python(self, cmd, environ):
# Make sure that messages before execv() are logged
sys.stdout.flush()
sys.stderr.flush()
cmd_text = shlex.join(cmd)
try:
+ print(f"+ {cmd_text}", flush=True)
+
if hasattr(os, 'execv') and not MS_WINDOWS:
os.execv(cmd[0], cmd)
# On success, execv() do no return.
# On error, it raises an OSError.
else:
import subprocess
- with subprocess.Popen(cmd) as proc:
+ with subprocess.Popen(cmd, env=environ) as proc:
try:
proc.wait()
except KeyboardInterrupt:
@@ -548,6 +592,28 @@ class Regrtest:
f"Command: {cmd_text}")
# continue executing main()
+ def _add_python_opts(self):
+ python_opts = []
+ regrtest_opts = []
+
+ environ, keep_environ = self._add_cross_compile_opts(regrtest_opts)
+ if self.ci_mode:
+ self._add_ci_python_opts(python_opts, keep_environ)
+
+ if (not python_opts) and (not regrtest_opts) and (environ is None):
+ # Nothing changed: nothing to do
+ return
+
+ # Create new command line
+ cmd = list(sys.orig_argv)
+ if python_opts:
+ cmd[1:1] = python_opts
+ if regrtest_opts:
+ cmd.extend(regrtest_opts)
+ cmd.append("--dont-add-python-opts")
+
+ self._execute_python(cmd, environ)
+
def _init(self):
# Set sys.stdout encoder error handler to backslashreplace,
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index dc1fa51..d2c274d 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -5,7 +5,9 @@ import math
import os.path
import platform
import random
+import shlex
import signal
+import subprocess
import sys
import sysconfig
import tempfile
@@ -523,7 +525,18 @@ def adjust_rlimit_nofile():
f"{new_fd_limit}: {err}.")
-def display_header(use_resources: tuple[str, ...]):
+def get_host_runner():
+ if (hostrunner := os.environ.get("_PYTHON_HOSTRUNNER")) is None:
+ hostrunner = sysconfig.get_config_var("HOSTRUNNER")
+ return hostrunner
+
+
+def is_cross_compiled():
+ return ('_PYTHON_HOST_PLATFORM' in os.environ)
+
+
+def display_header(use_resources: tuple[str, ...],
+ python_cmd: tuple[str, ...] | None):
# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
print("==", platform.platform(aliased=True),
@@ -537,13 +550,35 @@ def display_header(use_resources: tuple[str, ...]):
print("== encodings: locale=%s, FS=%s"
% (locale.getencoding(), sys.getfilesystemencoding()))
-
if use_resources:
print(f"== resources ({len(use_resources)}): "
f"{', '.join(sorted(use_resources))}")
else:
print("== resources: (all disabled, use -u option)")
+ cross_compile = is_cross_compiled()
+ if cross_compile:
+ print("== cross compiled: Yes")
+ if python_cmd:
+ cmd = shlex.join(python_cmd)
+ print(f"== host python: {cmd}")
+
+ get_cmd = [*python_cmd, '-m', 'platform']
+ proc = subprocess.run(
+ get_cmd,
+ stdout=subprocess.PIPE,
+ text=True,
+ cwd=os_helper.SAVEDCWD)
+ stdout = proc.stdout.replace('\n', ' ').strip()
+ if stdout:
+ print(f"== host platform: {stdout}")
+ elif proc.returncode:
+ print(f"== host platform: <command failed with exit code {proc.returncode}>")
+ else:
+ hostrunner = get_host_runner()
+ if hostrunner:
+ print(f"== host runner: {hostrunner}")
+
# This makes it easier to remember what to set in your local
# environment when trying to reproduce a sanitizer failure.
asan = support.check_sanitizer(address=True)
diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py
index e940cf0..0e052e2 100644
--- a/Lib/test/test_regrtest.py
+++ b/Lib/test/test_regrtest.py
@@ -788,14 +788,6 @@ class ProgramsTestCase(BaseTestCase):
args = [*self.python_args, script, *self.regrtest_args, *self.tests]
self.run_tests(args)
- @unittest.skipUnless(sysconfig.is_python_build(),
- 'run_tests.py script is not installed')
- def test_tools_script_run_tests(self):
- # Tools/scripts/run_tests.py
- script = os.path.join(ROOT_DIR, 'Tools', 'scripts', 'run_tests.py')
- args = [script, *self.regrtest_args, *self.tests]
- self.run_tests(args)
-
def run_batch(self, *args):
proc = self.run_command(args)
self.check_output(proc.stdout)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index fa5b9e6..cf03c86 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1837,7 +1837,7 @@ $(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS)
TESTOPTS= $(EXTRATESTOPTS)
TESTPYTHON= $(RUNSHARED) $(PYTHON_FOR_BUILD) $(TESTPYTHONOPTS)
-TESTRUNNER= $(TESTPYTHON) $(srcdir)/Tools/scripts/run_tests.py
+TESTRUNNER= $(TESTPYTHON) -m test
TESTTIMEOUT=
# Remove "test_python_*" directories of previous failed test jobs.
@@ -1875,11 +1875,6 @@ buildbottest: all
fi
$(TESTRUNNER) --slow-ci --timeout=$(TESTTIMEOUT) $(TESTOPTS)
-# Like buildbottest, but run Python tests with HOSTRUNNER directly.
-.PHONY: hostrunnertest
-hostrunnertest: all
- $(RUNSHARED) $(HOSTRUNNER) ./$(BUILDPYTHON) -m test --slow-ci --timeout=$(TESTTIMEOUT) $(TESTOPTS)
-
.PHONY: pythoninfo
pythoninfo: all
$(RUNSHARED) $(HOSTRUNNER) ./$(BUILDPYTHON) -m test.pythoninfo
diff --git a/Misc/NEWS.d/next/Tests/2023-09-30-20-18-38.gh-issue-110152.4Kxve1.rst b/Misc/NEWS.d/next/Tests/2023-09-30-20-18-38.gh-issue-110152.4Kxve1.rst
new file mode 100644
index 0000000..2fb6cbb
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2023-09-30-20-18-38.gh-issue-110152.4Kxve1.rst
@@ -0,0 +1,5 @@
+Remove ``Tools/scripts/run_tests.py`` and ``make hostrunnertest``. Just run
+``./python -m test --slow-ci``, ``make buildbottest`` or ``make test`` instead.
+Python test runner (regrtest) now handles cross-compilation and HOSTRUNNER. It
+also adds options to Python such fast ``-u -E -W default -bb`` when
+``--fast-ci`` or ``--slow-ci`` option is used. Patch by Victor Stinner.
diff --git a/Tools/scripts/run_tests.py b/Tools/scripts/run_tests.py
deleted file mode 100644
index 3e3d15d..0000000
--- a/Tools/scripts/run_tests.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""Run Python's test suite in a fast, rigorous way.
-
-The defaults are meant to be reasonably thorough, while skipping certain
-tests that can be time-consuming or resource-intensive (e.g. largefile),
-or distracting (e.g. audio and gui). These defaults can be overridden by
-simply passing a -u option to this script.
-
-"""
-
-import os
-import shlex
-import sys
-import sysconfig
-import test.support
-
-
-def is_multiprocess_flag(arg):
- return arg.startswith('-j') or arg.startswith('--multiprocess')
-
-
-def is_python_flag(arg):
- return arg.startswith('-p') or arg.startswith('--python')
-
-
-def main(regrtest_args):
- args = [sys.executable]
-
- cross_compile = '_PYTHON_HOST_PLATFORM' in os.environ
- if (hostrunner := os.environ.get("_PYTHON_HOSTRUNNER")) is None:
- hostrunner = sysconfig.get_config_var("HOSTRUNNER")
- if cross_compile:
- # emulate -E, but keep PYTHONPATH + cross compile env vars, so
- # test executable can load correct sysconfigdata file.
- keep = {
- '_PYTHON_PROJECT_BASE',
- '_PYTHON_HOST_PLATFORM',
- '_PYTHON_SYSCONFIGDATA_NAME',
- 'PYTHONPATH'
- }
- environ = {
- name: value for name, value in os.environ.items()
- if not name.startswith(('PYTHON', '_PYTHON')) or name in keep
- }
- else:
- environ = os.environ.copy()
-
- # Allow user-specified interpreter options to override our defaults.
- args.extend(test.support.args_from_interpreter_flags())
-
- args.extend(['-m', 'test', # Run the test suite
- '--fast-ci', # Fast Continuous Integration mode
- ])
- if not any(is_multiprocess_flag(arg) for arg in regrtest_args):
- if cross_compile and hostrunner:
- # For now use only two cores for cross-compiled builds;
- # hostrunner can be expensive.
- args.extend(['-j', '2'])
-
- if cross_compile and hostrunner:
- # If HOSTRUNNER is set and -p/--python option is not given, then
- # use hostrunner to execute python binary for tests.
- if not any(is_python_flag(arg) for arg in regrtest_args):
- buildpython = sysconfig.get_config_var("BUILDPYTHON")
- args.extend(["--python", f"{hostrunner} {buildpython}"])
-
- args.extend(regrtest_args)
-
- print(shlex.join(args), flush=True)
-
- if sys.platform == 'win32':
- from subprocess import call
- sys.exit(call(args))
- else:
- os.execve(sys.executable, args, environ)
-
-
-if __name__ == '__main__':
- main(sys.argv[1:])