2023-10-12 23:45:36 +02:00
|
|
|
import contextlib
|
|
|
|
|
import faulthandler
|
|
|
|
|
import locale
|
2018-06-14 14:58:13 +02:00
|
|
|
import math
|
2019-04-26 04:08:53 +02:00
|
|
|
import os.path
|
2023-10-12 23:45:36 +02:00
|
|
|
import platform
|
|
|
|
|
import random
|
|
|
|
|
import shlex
|
|
|
|
|
import signal
|
|
|
|
|
import subprocess
|
2019-04-26 04:08:53 +02:00
|
|
|
import sys
|
[3.11] gh-108822: Backport libregrtest changes from the main branch (#108820)
* Revert "[3.11] gh-101634: regrtest reports decoding error as failed test (#106169) (#106175)"
This reverts commit d5418e97fc524420011a370ba3c2c3cf6a89a74f.
* Revert "[3.11] bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (GH-30895) (GH-103342)"
This reverts commit ecb09a849689764193e0115d27e220f82b5f6d9f.
* Revert "gh-95027: Fix regrtest stdout encoding on Windows (GH-98492)"
This reverts commit b2aa28eec56d07b9c6777b02b7247cf21839de9f.
* Revert "[3.11] gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253) (GH-94408)"
This reverts commit 0122ab235b5acb52dd99fd05d8802a00f438b828.
* Revert "Run Tools/scripts/reindent.py (GH-94225)"
This reverts commit f0f3a424afb00a15ce8c0140dd218f5b33929be6.
* Revert "gh-94052: Don't re-run failed tests with --python option (GH-94054)"
This reverts commit 1347607db12012f6458ffcba48d8ad797083812e.
* Revert "[3.11] gh-84461: Fix Emscripten umask and permission issues (GH-94002) (GH-94006)"
This reverts commit 10731849184a3101ed18683b0128d689f1671c3f.
* gh-93353: regrtest checks for leaked temporary files (#93776)
When running tests with -jN, create a temporary directory per process
and mark a test as "environment changed" if a test leaks a temporary
file or directory.
(cherry picked from commit e566ce5496f1bad81c431aaee65e36d5e44771c5)
* gh-93353: Fix regrtest for -jN with N >= 2 (GH-93813)
(cherry picked from commit 36934a16e86f34d69ba2d41630fb5b4d06d59cff)
* gh-93353: regrtest supports checking tmp files with -j2 (#93909)
regrtest now also implements checking for leaked temporary files and
directories when using -jN for N >= 2. Use tempfile.mkdtemp() to
create the temporary directory. Skip this check on WASI.
(cherry picked from commit 4f85cec9e2077681b3dacc3108e646d509b720bf)
* gh-84461: Fix Emscripten umask and permission issues (GH-94002)
- Emscripten's default umask is too strict, see
https://github.com/emscripten-core/emscripten/issues/17269
- getuid/getgid and geteuid/getegid are stubs that always return 0
(root). Disable effective uid/gid syscalls and fix tests that use
chmod() current user.
- Cannot drop X bit from directory.
(cherry picked from commit 2702e408fd0e0dd7aec396b4cf8c7ce9caae81d8)
* gh-94052: Don't re-run failed tests with --python option (#94054)
(cherry picked from commit 0ff7b996f5d836e63cdaf652c7aa734285261096)
* Run Tools/scripts/reindent.py (#94225)
Reindent files which were not properly formatted (PEP 8: 4 spaces).
Remove also some trailing spaces.
(cherry picked from commit e87ada48a9e5d9d03f9759138869216df0d7383a)
* gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253)
Co-authored-by: Victor Stinner <vstinner@python.org>
(cherry picked from commit 199ba233248ab279f445e0809c2077976f0711bc)
* gh-96465: Clear fractions hash lru_cache under refleak testing (GH-96689)
Automerge-Triggered-By: GH:zware
(cherry picked from commit 9c8f3794337457b1d905a9fa0f38c2978fe32abd)
* gh-95027: Fix regrtest stdout encoding on Windows (#98492)
On Windows, when the Python test suite is run with the -jN option,
the ANSI code page is now used as the encoding for the stdout
temporary file, rather than using UTF-8 which can lead to decoding
errors.
(cherry picked from commit ec1f6f5f139868dc2c1116a7c7c878c38c668d53)
* gh-98903: Test suite fails with exit code 4 if no tests ran (#98904)
The Python test suite now fails wit exit code 4 if no tests ran. It
should help detecting typos in test names and test methods.
* Add "EXITCODE_" constants to Lib/test/libregrtest/main.py.
* Fix a typo: "NO TEST RUN" becomes "NO TESTS RAN"
(cherry picked from commit c76db37c0d23174cbffd6fa978d39693890ef020)
* gh-100086: Add build info to test.libregrtest (#100093)
The Python test runner (libregrtest) now logs Python build information like
"debug" vs "release" build, or LTO and PGO optimizations.
(cherry picked from commit 3c892022472eb975360fb3f0caa6f6fcc6fbf220)
* bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (#30895)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit 995386071f96e4cfebfa027a71ca9134e4651d2a)
* gh-82054: allow test runner to split test_asyncio to execute in parallel by sharding. (#103927)
This runs test_asyncio sub-tests in parallel using sharding from Cinder. This suite is typically the longest-pole in runs because it is a test package with a lot of further sub-tests otherwise run serially. By breaking out the sub-tests as independent modules we can run a lot more in parallel.
After porting we can see the direct impact on a multicore system.
Without this change:
Running make test is 5 min 26 seconds
With this change:
Running make test takes 3 min 39 seconds
That'll vary based on system and parallelism. On a `-j 4` run similar to what CI and buildbot systems often do, it reduced the overall test suite completion latency by 10%.
The drawbacks are that this implementation is hacky and due to the sorting of the tests it obscures when the asyncio tests occur and involves changing CPython test infrastructure but, the wall time saved it is worth it, especially in low-core count CI runs as it pulls a long tail. The win for productivity and reserved CI resource usage is significant.
Future tests that deserve to be refactored into split up suites to benefit from are test_concurrent_futures and the way the _test_multiprocessing suite gets run for all start methods. As exposed by passing the -o flag to python -m test to get a list of the 10 longest running tests.
---------
Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google, LLC]
(cherry picked from commit 9e011e7c77dad7d0bbb944c44891531606caeb21)
* Display the sanitizer config in the regrtest header. (#105301)
Display the sanitizers present in libregrtest.
Having this in the CI output for tests with the relevant environment
variable displayed will help make it easier to do what we need to
create an equivalent local test run.
(cherry picked from commit 852348ab65783601e0844b6647ea033668b45c11)
* gh-101634: regrtest reports decoding error as failed test (#106169)
When running the Python test suite with -jN option, if a worker stdout
cannot be decoded from the locale encoding report a failed testn so the
exitcode is non-zero.
(cherry picked from commit 2ac3eec103cf450aaaebeb932e51155d2e7fb37b)
* gh-108223: test.pythoninfo and libregrtest log Py_NOGIL (#108238)
Enable with --disable-gil --without-pydebug:
$ make pythoninfo|grep NOGIL
sysconfig[Py_NOGIL]: 1
$ ./python -m test
...
== Python build: nogil debug
...
(cherry picked from commit 5afe0c17ca14df430736e549542a4b85e7e7c7ac)
* gh-90791: test.pythoninfo logs ASAN_OPTIONS env var (#108289)
* Cleanup libregrtest code logging ASAN_OPTIONS.
* Fix a typo on "ASAN_OPTIONS" vs "MSAN_OPTIONS".
(cherry picked from commit 3a1ac87f8f89d3206b46a0df4908afae629d669d)
* gh-108388: regrtest splits test_asyncio package (#108393)
Currently, test_asyncio package is only splitted into sub-tests when
using command "./python -m test". With this change, it's also
splitted when passing it on the command line:
"./python -m test test_asyncio".
Remove the concept of "STDTESTS". Python is now mature enough to not
have to bother with that anymore. Removing STDTESTS simplify the
code.
(cherry picked from commit 174e9da0836844a2138cc8915dd305cb2cd7a583)
* regrtest computes statistics (#108793)
test_netrc, test_pep646_syntax and test_xml_etree now return results
in the test_main() function.
Changes:
* Rewrite TestResult as a dataclass with a new State class.
* Add test.support.TestStats class and Regrtest.stats_dict attribute.
* libregrtest.runtest functions now modify a TestResult instance
in-place.
* libregrtest summary lists the number of run tests and skipped
tests, and denied resources.
* Add TestResult.has_meaningful_duration() method.
* Compute TestResult duration in the upper function.
* Use time.perf_counter() instead of time.monotonic().
* Regrtest: rename 'resource_denieds' attribute to 'resource_denied'.
* Rename CHILD_ERROR to MULTIPROCESSING_ERROR.
* Use match/case syntadx to have different code depending on the
test state.
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
(cherry picked from commit d4e534cbb35678c82b3a1276826af55d7bfc23b6)
* gh-108822: Add Changelog entry for regrtest statistics (#108821)
---------
Co-authored-by: Christian Heimes <christian@python.org>
Co-authored-by: Zachary Ware <zach@python.org>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Co-authored-by: Joshua Herman <zitterbewegung@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
2023-09-03 19:21:53 +02:00
|
|
|
import sysconfig
|
2023-10-12 23:45:36 +02:00
|
|
|
import tempfile
|
2018-06-01 11:04:45 +02:00
|
|
|
import textwrap
|
2023-12-01 20:09:00 +01:00
|
|
|
from collections.abc import Callable, Iterable
|
2023-10-12 23:45:36 +02:00
|
|
|
|
2019-06-13 01:09:04 +02:00
|
|
|
from test import support
|
2023-10-12 23:45:36 +02:00
|
|
|
from test.support import os_helper
|
|
|
|
|
from test.support import threading_helper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# All temporary files and temporary directories created by libregrtest should
|
|
|
|
|
# use TMP_PREFIX so cleanup_temp_dir() can remove them all.
|
|
|
|
|
TMP_PREFIX = 'test_python_'
|
|
|
|
|
WORK_DIR_PREFIX = TMP_PREFIX
|
|
|
|
|
WORKER_WORK_DIR_PREFIX = WORK_DIR_PREFIX + 'worker_'
|
|
|
|
|
|
|
|
|
|
# bpo-38203: Maximum delay in seconds to exit Python (call Py_Finalize()).
|
|
|
|
|
# Used to protect against threading._shutdown() hang.
|
|
|
|
|
# Must be smaller than buildbot "1200 seconds without output" limit.
|
|
|
|
|
EXIT_TIMEOUT = 120.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ALL_RESOURCES = ('audio', 'curses', 'largefile', 'network',
|
|
|
|
|
'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'walltime')
|
|
|
|
|
|
|
|
|
|
# Other resources excluded from --use=all:
|
|
|
|
|
#
|
|
|
|
|
# - extralagefile (ex: test_zipfile64): really too slow to be enabled
|
|
|
|
|
# "by default"
|
|
|
|
|
# - tzdata: while needed to validate fully test_datetime, it makes
|
|
|
|
|
# test_datetime too slow (15-20 min on some buildbots) and so is disabled by
|
|
|
|
|
# default (see bpo-30822).
|
|
|
|
|
RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Types for types hints
|
|
|
|
|
StrPath = str
|
|
|
|
|
TestName = str
|
|
|
|
|
StrJSON = str
|
|
|
|
|
TestTuple = tuple[TestName, ...]
|
|
|
|
|
TestList = list[TestName]
|
|
|
|
|
# --match and --ignore options: list of patterns
|
|
|
|
|
# ('*' joker character can be used)
|
2023-10-21 20:30:19 +03:00
|
|
|
TestFilter = list[tuple[TestName, bool]]
|
2023-10-12 23:45:36 +02:00
|
|
|
FilterTuple = tuple[TestName, ...]
|
|
|
|
|
FilterDict = dict[TestName, FilterTuple]
|
2018-06-01 11:04:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_duration(seconds):
|
2018-06-14 14:58:13 +02:00
|
|
|
ms = math.ceil(seconds * 1e3)
|
|
|
|
|
seconds, ms = divmod(ms, 1000)
|
|
|
|
|
minutes, seconds = divmod(seconds, 60)
|
|
|
|
|
hours, minutes = divmod(minutes, 60)
|
2018-06-01 11:04:45 +02:00
|
|
|
|
2018-06-14 14:58:13 +02:00
|
|
|
parts = []
|
2018-06-01 11:04:45 +02:00
|
|
|
if hours:
|
2018-06-14 14:58:13 +02:00
|
|
|
parts.append('%s hour' % hours)
|
|
|
|
|
if minutes:
|
|
|
|
|
parts.append('%s min' % minutes)
|
|
|
|
|
if seconds:
|
2019-10-03 16:15:16 +02:00
|
|
|
if parts:
|
|
|
|
|
# 2 min 1 sec
|
|
|
|
|
parts.append('%s sec' % seconds)
|
|
|
|
|
else:
|
|
|
|
|
# 1.0 sec
|
|
|
|
|
parts.append('%.1f sec' % (seconds + ms / 1000))
|
2018-06-14 14:58:13 +02:00
|
|
|
if not parts:
|
2019-10-03 16:15:16 +02:00
|
|
|
return '%s ms' % ms
|
2018-06-14 14:58:13 +02:00
|
|
|
|
|
|
|
|
parts = parts[:2]
|
|
|
|
|
return ' '.join(parts)
|
2018-06-01 11:04:45 +02:00
|
|
|
|
|
|
|
|
|
2023-10-12 23:45:36 +02:00
|
|
|
def strip_py_suffix(names: list[str] | None) -> None:
|
2018-06-01 11:04:45 +02:00
|
|
|
if not names:
|
|
|
|
|
return
|
|
|
|
|
for idx, name in enumerate(names):
|
|
|
|
|
basename, ext = os.path.splitext(name)
|
|
|
|
|
if ext == '.py':
|
|
|
|
|
names[idx] = basename
|
|
|
|
|
|
|
|
|
|
|
2023-10-12 23:45:36 +02:00
|
|
|
def plural(n, singular, plural=None):
|
|
|
|
|
if n == 1:
|
|
|
|
|
return singular
|
|
|
|
|
elif plural is not None:
|
|
|
|
|
return plural
|
|
|
|
|
else:
|
|
|
|
|
return singular + 's'
|
|
|
|
|
|
|
|
|
|
|
2018-06-01 11:04:45 +02:00
|
|
|
def count(n, word):
|
|
|
|
|
if n == 1:
|
2023-10-12 23:45:36 +02:00
|
|
|
return f"{n} {word}"
|
2018-06-01 11:04:45 +02:00
|
|
|
else:
|
2023-10-12 23:45:36 +02:00
|
|
|
return f"{n} {word}s"
|
2018-06-01 11:04:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def printlist(x, width=70, indent=4, file=None):
|
|
|
|
|
"""Print the elements of iterable x to stdout.
|
|
|
|
|
|
|
|
|
|
Optional arg width (default 70) is the maximum line length.
|
|
|
|
|
Optional arg indent (default 4) is the number of blanks with which to
|
|
|
|
|
begin each line.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
blanks = ' ' * indent
|
|
|
|
|
# Print the sorted list: 'x' may be a '--random' list or a set()
|
|
|
|
|
print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
|
|
|
|
|
initial_indent=blanks, subsequent_indent=blanks),
|
|
|
|
|
file=file)
|
2019-04-26 04:08:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_warning(msg):
|
2020-04-23 19:03:52 +02:00
|
|
|
support.print_warning(msg)
|
2019-06-13 01:09:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
orig_unraisablehook = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def regrtest_unraisable_hook(unraisable):
|
|
|
|
|
global orig_unraisablehook
|
|
|
|
|
support.environment_altered = True
|
2021-10-13 01:52:22 +02:00
|
|
|
support.print_warning("Unraisable exception")
|
2020-08-03 22:51:23 +02:00
|
|
|
old_stderr = sys.stderr
|
|
|
|
|
try:
|
2021-10-11 23:07:21 +02:00
|
|
|
support.flush_std_streams()
|
2021-10-13 14:08:18 +02:00
|
|
|
sys.stderr = support.print_warning.orig_stderr
|
2020-08-03 22:51:23 +02:00
|
|
|
orig_unraisablehook(unraisable)
|
2021-04-16 14:33:10 +02:00
|
|
|
sys.stderr.flush()
|
2020-08-03 22:51:23 +02:00
|
|
|
finally:
|
|
|
|
|
sys.stderr = old_stderr
|
2019-06-13 01:09:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_unraisable_hook():
|
|
|
|
|
global orig_unraisablehook
|
|
|
|
|
orig_unraisablehook = sys.unraisablehook
|
|
|
|
|
sys.unraisablehook = regrtest_unraisable_hook
|
2021-03-23 00:17:05 +01:00
|
|
|
|
|
|
|
|
|
2021-04-16 14:33:10 +02:00
|
|
|
orig_threading_excepthook = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def regrtest_threading_excepthook(args):
|
|
|
|
|
global orig_threading_excepthook
|
|
|
|
|
support.environment_altered = True
|
2021-10-13 01:52:22 +02:00
|
|
|
support.print_warning(f"Uncaught thread exception: {args.exc_type.__name__}")
|
2021-04-16 14:33:10 +02:00
|
|
|
old_stderr = sys.stderr
|
|
|
|
|
try:
|
2021-10-11 23:07:21 +02:00
|
|
|
support.flush_std_streams()
|
2021-10-13 14:08:18 +02:00
|
|
|
sys.stderr = support.print_warning.orig_stderr
|
2021-04-16 14:33:10 +02:00
|
|
|
orig_threading_excepthook(args)
|
|
|
|
|
sys.stderr.flush()
|
|
|
|
|
finally:
|
|
|
|
|
sys.stderr = old_stderr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_threading_excepthook():
|
|
|
|
|
global orig_threading_excepthook
|
|
|
|
|
import threading
|
|
|
|
|
orig_threading_excepthook = threading.excepthook
|
|
|
|
|
threading.excepthook = regrtest_threading_excepthook
|
|
|
|
|
|
|
|
|
|
|
2021-03-23 00:17:05 +01:00
|
|
|
def clear_caches():
|
|
|
|
|
# Clear the warnings registry, so they can be displayed again
|
|
|
|
|
for mod in sys.modules.values():
|
|
|
|
|
if hasattr(mod, '__warningregistry__'):
|
|
|
|
|
del mod.__warningregistry__
|
|
|
|
|
|
|
|
|
|
# Flush standard output, so that buffered data is sent to the OS and
|
|
|
|
|
# associated Python objects are reclaimed.
|
|
|
|
|
for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__):
|
|
|
|
|
if stream is not None:
|
|
|
|
|
stream.flush()
|
|
|
|
|
|
2023-10-18 01:20:36 +02:00
|
|
|
# Clear assorted module caches.
|
|
|
|
|
# Don't worry about resetting the cache if the module is not loaded
|
|
|
|
|
try:
|
|
|
|
|
distutils_dir_util = sys.modules['distutils.dir_util']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
distutils_dir_util._path_created.clear()
|
|
|
|
|
|
2021-03-23 00:17:05 +01:00
|
|
|
try:
|
|
|
|
|
re = sys.modules['re']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
re.purge()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
_strptime = sys.modules['_strptime']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
_strptime._regex_cache.clear()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
urllib_parse = sys.modules['urllib.parse']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
urllib_parse.clear_cache()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
urllib_request = sys.modules['urllib.request']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
urllib_request.urlcleanup()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
linecache = sys.modules['linecache']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
linecache.clearcache()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
mimetypes = sys.modules['mimetypes']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
mimetypes._default_mime_types()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
filecmp = sys.modules['filecmp']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
filecmp._cache.clear()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
struct = sys.modules['struct']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
struct._clearcache()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
doctest = sys.modules['doctest']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
doctest.master = None
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ctypes = sys.modules['ctypes']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
ctypes._reset_cache()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
typing = sys.modules['typing']
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
for f in typing._cleanups:
|
|
|
|
|
f()
|
[3.11] gh-108822: Backport libregrtest changes from the main branch (#108820)
* Revert "[3.11] gh-101634: regrtest reports decoding error as failed test (#106169) (#106175)"
This reverts commit d5418e97fc524420011a370ba3c2c3cf6a89a74f.
* Revert "[3.11] bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (GH-30895) (GH-103342)"
This reverts commit ecb09a849689764193e0115d27e220f82b5f6d9f.
* Revert "gh-95027: Fix regrtest stdout encoding on Windows (GH-98492)"
This reverts commit b2aa28eec56d07b9c6777b02b7247cf21839de9f.
* Revert "[3.11] gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253) (GH-94408)"
This reverts commit 0122ab235b5acb52dd99fd05d8802a00f438b828.
* Revert "Run Tools/scripts/reindent.py (GH-94225)"
This reverts commit f0f3a424afb00a15ce8c0140dd218f5b33929be6.
* Revert "gh-94052: Don't re-run failed tests with --python option (GH-94054)"
This reverts commit 1347607db12012f6458ffcba48d8ad797083812e.
* Revert "[3.11] gh-84461: Fix Emscripten umask and permission issues (GH-94002) (GH-94006)"
This reverts commit 10731849184a3101ed18683b0128d689f1671c3f.
* gh-93353: regrtest checks for leaked temporary files (#93776)
When running tests with -jN, create a temporary directory per process
and mark a test as "environment changed" if a test leaks a temporary
file or directory.
(cherry picked from commit e566ce5496f1bad81c431aaee65e36d5e44771c5)
* gh-93353: Fix regrtest for -jN with N >= 2 (GH-93813)
(cherry picked from commit 36934a16e86f34d69ba2d41630fb5b4d06d59cff)
* gh-93353: regrtest supports checking tmp files with -j2 (#93909)
regrtest now also implements checking for leaked temporary files and
directories when using -jN for N >= 2. Use tempfile.mkdtemp() to
create the temporary directory. Skip this check on WASI.
(cherry picked from commit 4f85cec9e2077681b3dacc3108e646d509b720bf)
* gh-84461: Fix Emscripten umask and permission issues (GH-94002)
- Emscripten's default umask is too strict, see
https://github.com/emscripten-core/emscripten/issues/17269
- getuid/getgid and geteuid/getegid are stubs that always return 0
(root). Disable effective uid/gid syscalls and fix tests that use
chmod() current user.
- Cannot drop X bit from directory.
(cherry picked from commit 2702e408fd0e0dd7aec396b4cf8c7ce9caae81d8)
* gh-94052: Don't re-run failed tests with --python option (#94054)
(cherry picked from commit 0ff7b996f5d836e63cdaf652c7aa734285261096)
* Run Tools/scripts/reindent.py (#94225)
Reindent files which were not properly formatted (PEP 8: 4 spaces).
Remove also some trailing spaces.
(cherry picked from commit e87ada48a9e5d9d03f9759138869216df0d7383a)
* gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253)
Co-authored-by: Victor Stinner <vstinner@python.org>
(cherry picked from commit 199ba233248ab279f445e0809c2077976f0711bc)
* gh-96465: Clear fractions hash lru_cache under refleak testing (GH-96689)
Automerge-Triggered-By: GH:zware
(cherry picked from commit 9c8f3794337457b1d905a9fa0f38c2978fe32abd)
* gh-95027: Fix regrtest stdout encoding on Windows (#98492)
On Windows, when the Python test suite is run with the -jN option,
the ANSI code page is now used as the encoding for the stdout
temporary file, rather than using UTF-8 which can lead to decoding
errors.
(cherry picked from commit ec1f6f5f139868dc2c1116a7c7c878c38c668d53)
* gh-98903: Test suite fails with exit code 4 if no tests ran (#98904)
The Python test suite now fails wit exit code 4 if no tests ran. It
should help detecting typos in test names and test methods.
* Add "EXITCODE_" constants to Lib/test/libregrtest/main.py.
* Fix a typo: "NO TEST RUN" becomes "NO TESTS RAN"
(cherry picked from commit c76db37c0d23174cbffd6fa978d39693890ef020)
* gh-100086: Add build info to test.libregrtest (#100093)
The Python test runner (libregrtest) now logs Python build information like
"debug" vs "release" build, or LTO and PGO optimizations.
(cherry picked from commit 3c892022472eb975360fb3f0caa6f6fcc6fbf220)
* bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (#30895)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit 995386071f96e4cfebfa027a71ca9134e4651d2a)
* gh-82054: allow test runner to split test_asyncio to execute in parallel by sharding. (#103927)
This runs test_asyncio sub-tests in parallel using sharding from Cinder. This suite is typically the longest-pole in runs because it is a test package with a lot of further sub-tests otherwise run serially. By breaking out the sub-tests as independent modules we can run a lot more in parallel.
After porting we can see the direct impact on a multicore system.
Without this change:
Running make test is 5 min 26 seconds
With this change:
Running make test takes 3 min 39 seconds
That'll vary based on system and parallelism. On a `-j 4` run similar to what CI and buildbot systems often do, it reduced the overall test suite completion latency by 10%.
The drawbacks are that this implementation is hacky and due to the sorting of the tests it obscures when the asyncio tests occur and involves changing CPython test infrastructure but, the wall time saved it is worth it, especially in low-core count CI runs as it pulls a long tail. The win for productivity and reserved CI resource usage is significant.
Future tests that deserve to be refactored into split up suites to benefit from are test_concurrent_futures and the way the _test_multiprocessing suite gets run for all start methods. As exposed by passing the -o flag to python -m test to get a list of the 10 longest running tests.
---------
Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google, LLC]
(cherry picked from commit 9e011e7c77dad7d0bbb944c44891531606caeb21)
* Display the sanitizer config in the regrtest header. (#105301)
Display the sanitizers present in libregrtest.
Having this in the CI output for tests with the relevant environment
variable displayed will help make it easier to do what we need to
create an equivalent local test run.
(cherry picked from commit 852348ab65783601e0844b6647ea033668b45c11)
* gh-101634: regrtest reports decoding error as failed test (#106169)
When running the Python test suite with -jN option, if a worker stdout
cannot be decoded from the locale encoding report a failed testn so the
exitcode is non-zero.
(cherry picked from commit 2ac3eec103cf450aaaebeb932e51155d2e7fb37b)
* gh-108223: test.pythoninfo and libregrtest log Py_NOGIL (#108238)
Enable with --disable-gil --without-pydebug:
$ make pythoninfo|grep NOGIL
sysconfig[Py_NOGIL]: 1
$ ./python -m test
...
== Python build: nogil debug
...
(cherry picked from commit 5afe0c17ca14df430736e549542a4b85e7e7c7ac)
* gh-90791: test.pythoninfo logs ASAN_OPTIONS env var (#108289)
* Cleanup libregrtest code logging ASAN_OPTIONS.
* Fix a typo on "ASAN_OPTIONS" vs "MSAN_OPTIONS".
(cherry picked from commit 3a1ac87f8f89d3206b46a0df4908afae629d669d)
* gh-108388: regrtest splits test_asyncio package (#108393)
Currently, test_asyncio package is only splitted into sub-tests when
using command "./python -m test". With this change, it's also
splitted when passing it on the command line:
"./python -m test test_asyncio".
Remove the concept of "STDTESTS". Python is now mature enough to not
have to bother with that anymore. Removing STDTESTS simplify the
code.
(cherry picked from commit 174e9da0836844a2138cc8915dd305cb2cd7a583)
* regrtest computes statistics (#108793)
test_netrc, test_pep646_syntax and test_xml_etree now return results
in the test_main() function.
Changes:
* Rewrite TestResult as a dataclass with a new State class.
* Add test.support.TestStats class and Regrtest.stats_dict attribute.
* libregrtest.runtest functions now modify a TestResult instance
in-place.
* libregrtest summary lists the number of run tests and skipped
tests, and denied resources.
* Add TestResult.has_meaningful_duration() method.
* Compute TestResult duration in the upper function.
* Use time.perf_counter() instead of time.monotonic().
* Regrtest: rename 'resource_denieds' attribute to 'resource_denied'.
* Rename CHILD_ERROR to MULTIPROCESSING_ERROR.
* Use match/case syntadx to have different code depending on the
test state.
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
(cherry picked from commit d4e534cbb35678c82b3a1276826af55d7bfc23b6)
* gh-108822: Add Changelog entry for regrtest statistics (#108821)
---------
Co-authored-by: Christian Heimes <christian@python.org>
Co-authored-by: Zachary Ware <zach@python.org>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Co-authored-by: Joshua Herman <zitterbewegung@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
2023-09-03 19:21:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_build_info():
|
|
|
|
|
# Get most important configure and build options as a list of strings.
|
|
|
|
|
# Example: ['debug', 'ASAN+MSAN'] or ['release', 'LTO+PGO'].
|
|
|
|
|
|
|
|
|
|
config_args = sysconfig.get_config_var('CONFIG_ARGS') or ''
|
|
|
|
|
cflags = sysconfig.get_config_var('PY_CFLAGS') or ''
|
|
|
|
|
cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or ''
|
|
|
|
|
ldflags_nodist = sysconfig.get_config_var('PY_LDFLAGS_NODIST') or ''
|
|
|
|
|
|
|
|
|
|
build = []
|
|
|
|
|
|
|
|
|
|
# --disable-gil
|
|
|
|
|
if sysconfig.get_config_var('Py_NOGIL'):
|
|
|
|
|
build.append("nogil")
|
|
|
|
|
|
|
|
|
|
if hasattr(sys, 'gettotalrefcount'):
|
|
|
|
|
# --with-pydebug
|
|
|
|
|
build.append('debug')
|
|
|
|
|
|
|
|
|
|
if '-DNDEBUG' in (cflags + cflags_nodist):
|
|
|
|
|
build.append('without_assert')
|
|
|
|
|
else:
|
|
|
|
|
build.append('release')
|
|
|
|
|
|
|
|
|
|
if '--with-assertions' in config_args:
|
|
|
|
|
build.append('with_assert')
|
|
|
|
|
elif '-DNDEBUG' not in (cflags + cflags_nodist):
|
|
|
|
|
build.append('with_assert')
|
|
|
|
|
|
|
|
|
|
# --enable-framework=name
|
|
|
|
|
framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
|
|
|
|
|
if framework:
|
|
|
|
|
build.append(f'framework={framework}')
|
|
|
|
|
|
|
|
|
|
# --enable-shared
|
|
|
|
|
shared = int(sysconfig.get_config_var('PY_ENABLE_SHARED') or '0')
|
|
|
|
|
if shared:
|
|
|
|
|
build.append('shared')
|
|
|
|
|
|
|
|
|
|
# --with-lto
|
|
|
|
|
optimizations = []
|
|
|
|
|
if '-flto=thin' in ldflags_nodist:
|
|
|
|
|
optimizations.append('ThinLTO')
|
|
|
|
|
elif '-flto' in ldflags_nodist:
|
|
|
|
|
optimizations.append('LTO')
|
|
|
|
|
|
2023-09-20 17:45:23 +02:00
|
|
|
if support.check_cflags_pgo():
|
|
|
|
|
# PGO (--enable-optimizations)
|
[3.11] gh-108822: Backport libregrtest changes from the main branch (#108820)
* Revert "[3.11] gh-101634: regrtest reports decoding error as failed test (#106169) (#106175)"
This reverts commit d5418e97fc524420011a370ba3c2c3cf6a89a74f.
* Revert "[3.11] bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (GH-30895) (GH-103342)"
This reverts commit ecb09a849689764193e0115d27e220f82b5f6d9f.
* Revert "gh-95027: Fix regrtest stdout encoding on Windows (GH-98492)"
This reverts commit b2aa28eec56d07b9c6777b02b7247cf21839de9f.
* Revert "[3.11] gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253) (GH-94408)"
This reverts commit 0122ab235b5acb52dd99fd05d8802a00f438b828.
* Revert "Run Tools/scripts/reindent.py (GH-94225)"
This reverts commit f0f3a424afb00a15ce8c0140dd218f5b33929be6.
* Revert "gh-94052: Don't re-run failed tests with --python option (GH-94054)"
This reverts commit 1347607db12012f6458ffcba48d8ad797083812e.
* Revert "[3.11] gh-84461: Fix Emscripten umask and permission issues (GH-94002) (GH-94006)"
This reverts commit 10731849184a3101ed18683b0128d689f1671c3f.
* gh-93353: regrtest checks for leaked temporary files (#93776)
When running tests with -jN, create a temporary directory per process
and mark a test as "environment changed" if a test leaks a temporary
file or directory.
(cherry picked from commit e566ce5496f1bad81c431aaee65e36d5e44771c5)
* gh-93353: Fix regrtest for -jN with N >= 2 (GH-93813)
(cherry picked from commit 36934a16e86f34d69ba2d41630fb5b4d06d59cff)
* gh-93353: regrtest supports checking tmp files with -j2 (#93909)
regrtest now also implements checking for leaked temporary files and
directories when using -jN for N >= 2. Use tempfile.mkdtemp() to
create the temporary directory. Skip this check on WASI.
(cherry picked from commit 4f85cec9e2077681b3dacc3108e646d509b720bf)
* gh-84461: Fix Emscripten umask and permission issues (GH-94002)
- Emscripten's default umask is too strict, see
https://github.com/emscripten-core/emscripten/issues/17269
- getuid/getgid and geteuid/getegid are stubs that always return 0
(root). Disable effective uid/gid syscalls and fix tests that use
chmod() current user.
- Cannot drop X bit from directory.
(cherry picked from commit 2702e408fd0e0dd7aec396b4cf8c7ce9caae81d8)
* gh-94052: Don't re-run failed tests with --python option (#94054)
(cherry picked from commit 0ff7b996f5d836e63cdaf652c7aa734285261096)
* Run Tools/scripts/reindent.py (#94225)
Reindent files which were not properly formatted (PEP 8: 4 spaces).
Remove also some trailing spaces.
(cherry picked from commit e87ada48a9e5d9d03f9759138869216df0d7383a)
* gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253)
Co-authored-by: Victor Stinner <vstinner@python.org>
(cherry picked from commit 199ba233248ab279f445e0809c2077976f0711bc)
* gh-96465: Clear fractions hash lru_cache under refleak testing (GH-96689)
Automerge-Triggered-By: GH:zware
(cherry picked from commit 9c8f3794337457b1d905a9fa0f38c2978fe32abd)
* gh-95027: Fix regrtest stdout encoding on Windows (#98492)
On Windows, when the Python test suite is run with the -jN option,
the ANSI code page is now used as the encoding for the stdout
temporary file, rather than using UTF-8 which can lead to decoding
errors.
(cherry picked from commit ec1f6f5f139868dc2c1116a7c7c878c38c668d53)
* gh-98903: Test suite fails with exit code 4 if no tests ran (#98904)
The Python test suite now fails wit exit code 4 if no tests ran. It
should help detecting typos in test names and test methods.
* Add "EXITCODE_" constants to Lib/test/libregrtest/main.py.
* Fix a typo: "NO TEST RUN" becomes "NO TESTS RAN"
(cherry picked from commit c76db37c0d23174cbffd6fa978d39693890ef020)
* gh-100086: Add build info to test.libregrtest (#100093)
The Python test runner (libregrtest) now logs Python build information like
"debug" vs "release" build, or LTO and PGO optimizations.
(cherry picked from commit 3c892022472eb975360fb3f0caa6f6fcc6fbf220)
* bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (#30895)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit 995386071f96e4cfebfa027a71ca9134e4651d2a)
* gh-82054: allow test runner to split test_asyncio to execute in parallel by sharding. (#103927)
This runs test_asyncio sub-tests in parallel using sharding from Cinder. This suite is typically the longest-pole in runs because it is a test package with a lot of further sub-tests otherwise run serially. By breaking out the sub-tests as independent modules we can run a lot more in parallel.
After porting we can see the direct impact on a multicore system.
Without this change:
Running make test is 5 min 26 seconds
With this change:
Running make test takes 3 min 39 seconds
That'll vary based on system and parallelism. On a `-j 4` run similar to what CI and buildbot systems often do, it reduced the overall test suite completion latency by 10%.
The drawbacks are that this implementation is hacky and due to the sorting of the tests it obscures when the asyncio tests occur and involves changing CPython test infrastructure but, the wall time saved it is worth it, especially in low-core count CI runs as it pulls a long tail. The win for productivity and reserved CI resource usage is significant.
Future tests that deserve to be refactored into split up suites to benefit from are test_concurrent_futures and the way the _test_multiprocessing suite gets run for all start methods. As exposed by passing the -o flag to python -m test to get a list of the 10 longest running tests.
---------
Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google, LLC]
(cherry picked from commit 9e011e7c77dad7d0bbb944c44891531606caeb21)
* Display the sanitizer config in the regrtest header. (#105301)
Display the sanitizers present in libregrtest.
Having this in the CI output for tests with the relevant environment
variable displayed will help make it easier to do what we need to
create an equivalent local test run.
(cherry picked from commit 852348ab65783601e0844b6647ea033668b45c11)
* gh-101634: regrtest reports decoding error as failed test (#106169)
When running the Python test suite with -jN option, if a worker stdout
cannot be decoded from the locale encoding report a failed testn so the
exitcode is non-zero.
(cherry picked from commit 2ac3eec103cf450aaaebeb932e51155d2e7fb37b)
* gh-108223: test.pythoninfo and libregrtest log Py_NOGIL (#108238)
Enable with --disable-gil --without-pydebug:
$ make pythoninfo|grep NOGIL
sysconfig[Py_NOGIL]: 1
$ ./python -m test
...
== Python build: nogil debug
...
(cherry picked from commit 5afe0c17ca14df430736e549542a4b85e7e7c7ac)
* gh-90791: test.pythoninfo logs ASAN_OPTIONS env var (#108289)
* Cleanup libregrtest code logging ASAN_OPTIONS.
* Fix a typo on "ASAN_OPTIONS" vs "MSAN_OPTIONS".
(cherry picked from commit 3a1ac87f8f89d3206b46a0df4908afae629d669d)
* gh-108388: regrtest splits test_asyncio package (#108393)
Currently, test_asyncio package is only splitted into sub-tests when
using command "./python -m test". With this change, it's also
splitted when passing it on the command line:
"./python -m test test_asyncio".
Remove the concept of "STDTESTS". Python is now mature enough to not
have to bother with that anymore. Removing STDTESTS simplify the
code.
(cherry picked from commit 174e9da0836844a2138cc8915dd305cb2cd7a583)
* regrtest computes statistics (#108793)
test_netrc, test_pep646_syntax and test_xml_etree now return results
in the test_main() function.
Changes:
* Rewrite TestResult as a dataclass with a new State class.
* Add test.support.TestStats class and Regrtest.stats_dict attribute.
* libregrtest.runtest functions now modify a TestResult instance
in-place.
* libregrtest summary lists the number of run tests and skipped
tests, and denied resources.
* Add TestResult.has_meaningful_duration() method.
* Compute TestResult duration in the upper function.
* Use time.perf_counter() instead of time.monotonic().
* Regrtest: rename 'resource_denieds' attribute to 'resource_denied'.
* Rename CHILD_ERROR to MULTIPROCESSING_ERROR.
* Use match/case syntadx to have different code depending on the
test state.
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
(cherry picked from commit d4e534cbb35678c82b3a1276826af55d7bfc23b6)
* gh-108822: Add Changelog entry for regrtest statistics (#108821)
---------
Co-authored-by: Christian Heimes <christian@python.org>
Co-authored-by: Zachary Ware <zach@python.org>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Co-authored-by: Joshua Herman <zitterbewegung@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
2023-09-03 19:21:53 +02:00
|
|
|
optimizations.append('PGO')
|
|
|
|
|
if optimizations:
|
|
|
|
|
build.append('+'.join(optimizations))
|
|
|
|
|
|
|
|
|
|
# --with-address-sanitizer
|
|
|
|
|
sanitizers = []
|
|
|
|
|
if support.check_sanitizer(address=True):
|
|
|
|
|
sanitizers.append("ASAN")
|
|
|
|
|
# --with-memory-sanitizer
|
|
|
|
|
if support.check_sanitizer(memory=True):
|
|
|
|
|
sanitizers.append("MSAN")
|
|
|
|
|
# --with-undefined-behavior-sanitizer
|
|
|
|
|
if support.check_sanitizer(ub=True):
|
|
|
|
|
sanitizers.append("UBSAN")
|
|
|
|
|
if sanitizers:
|
|
|
|
|
build.append('+'.join(sanitizers))
|
|
|
|
|
|
|
|
|
|
# --with-trace-refs
|
|
|
|
|
if hasattr(sys, 'getobjects'):
|
|
|
|
|
build.append("TraceRefs")
|
|
|
|
|
# --enable-pystats
|
|
|
|
|
if hasattr(sys, '_stats_on'):
|
|
|
|
|
build.append("pystats")
|
|
|
|
|
# --with-valgrind
|
|
|
|
|
if sysconfig.get_config_var('WITH_VALGRIND'):
|
|
|
|
|
build.append("valgrind")
|
|
|
|
|
# --with-dtrace
|
|
|
|
|
if sysconfig.get_config_var('WITH_DTRACE'):
|
|
|
|
|
build.append("dtrace")
|
|
|
|
|
|
|
|
|
|
return build
|
2023-10-12 23:45:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath:
|
|
|
|
|
if tmp_dir:
|
|
|
|
|
tmp_dir = os.path.expanduser(tmp_dir)
|
|
|
|
|
else:
|
|
|
|
|
# When tests are run from the Python build directory, it is best practice
|
|
|
|
|
# to keep the test files in a subfolder. This eases the cleanup of leftover
|
|
|
|
|
# files using the "make distclean" command.
|
|
|
|
|
if sysconfig.is_python_build():
|
|
|
|
|
if not support.is_wasi:
|
|
|
|
|
tmp_dir = sysconfig.get_config_var('abs_builddir')
|
|
|
|
|
if tmp_dir is None:
|
|
|
|
|
tmp_dir = sysconfig.get_config_var('abs_srcdir')
|
|
|
|
|
if not tmp_dir:
|
|
|
|
|
# gh-74470: On Windows, only srcdir is available. Using
|
|
|
|
|
# abs_builddir mostly matters on UNIX when building
|
|
|
|
|
# Python out of the source tree, especially when the
|
|
|
|
|
# source tree is read only.
|
|
|
|
|
tmp_dir = sysconfig.get_config_var('srcdir')
|
2023-12-01 18:28:09 +01:00
|
|
|
if not tmp_dir:
|
|
|
|
|
raise RuntimeError(
|
|
|
|
|
"Could not determine the correct value for tmp_dir"
|
|
|
|
|
)
|
2023-10-12 23:45:36 +02:00
|
|
|
tmp_dir = os.path.join(tmp_dir, 'build')
|
|
|
|
|
else:
|
|
|
|
|
# WASI platform
|
|
|
|
|
tmp_dir = sysconfig.get_config_var('projectbase')
|
2023-12-01 18:28:09 +01:00
|
|
|
if not tmp_dir:
|
|
|
|
|
raise RuntimeError(
|
|
|
|
|
"sysconfig.get_config_var('projectbase') "
|
|
|
|
|
f"unexpectedly returned {tmp_dir!r} on WASI"
|
|
|
|
|
)
|
2023-10-12 23:45:36 +02:00
|
|
|
tmp_dir = os.path.join(tmp_dir, 'build')
|
|
|
|
|
|
|
|
|
|
# When get_temp_dir() is called in a worker process,
|
|
|
|
|
# get_temp_dir() path is different than in the parent process
|
|
|
|
|
# which is not a WASI process. So the parent does not create
|
|
|
|
|
# the same "tmp_dir" than the test worker process.
|
|
|
|
|
os.makedirs(tmp_dir, exist_ok=True)
|
|
|
|
|
else:
|
|
|
|
|
tmp_dir = tempfile.gettempdir()
|
|
|
|
|
|
|
|
|
|
return os.path.abspath(tmp_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fix_umask():
|
|
|
|
|
if support.is_emscripten:
|
|
|
|
|
# Emscripten has default umask 0o777, which breaks some tests.
|
|
|
|
|
# see https://github.com/emscripten-core/emscripten/issues/17269
|
|
|
|
|
old_mask = os.umask(0)
|
|
|
|
|
if old_mask == 0o777:
|
|
|
|
|
os.umask(0o027)
|
|
|
|
|
else:
|
|
|
|
|
os.umask(old_mask)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_work_dir(parent_dir: StrPath, worker: bool = False) -> StrPath:
|
|
|
|
|
# Define a writable temp dir that will be used as cwd while running
|
|
|
|
|
# the tests. The name of the dir includes the pid to allow parallel
|
|
|
|
|
# testing (see the -j option).
|
|
|
|
|
# Emscripten and WASI have stubbed getpid(), Emscripten has only
|
|
|
|
|
# milisecond clock resolution. Use randint() instead.
|
|
|
|
|
if support.is_emscripten or support.is_wasi:
|
|
|
|
|
nounce = random.randint(0, 1_000_000)
|
|
|
|
|
else:
|
|
|
|
|
nounce = os.getpid()
|
|
|
|
|
|
|
|
|
|
if worker:
|
|
|
|
|
work_dir = WORK_DIR_PREFIX + str(nounce)
|
|
|
|
|
else:
|
|
|
|
|
work_dir = WORKER_WORK_DIR_PREFIX + str(nounce)
|
|
|
|
|
work_dir += os_helper.FS_NONASCII
|
|
|
|
|
work_dir = os.path.join(parent_dir, work_dir)
|
|
|
|
|
return work_dir
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@contextlib.contextmanager
|
|
|
|
|
def exit_timeout():
|
|
|
|
|
try:
|
|
|
|
|
yield
|
|
|
|
|
except SystemExit as exc:
|
|
|
|
|
# bpo-38203: Python can hang at exit in Py_Finalize(), especially
|
|
|
|
|
# on threading._shutdown() call: put a timeout
|
|
|
|
|
if threading_helper.can_start_thread:
|
|
|
|
|
faulthandler.dump_traceback_later(EXIT_TIMEOUT, exit=True)
|
|
|
|
|
sys.exit(exc.code)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove_testfn(test_name: TestName, verbose: int) -> None:
|
|
|
|
|
# Try to clean up os_helper.TESTFN if left behind.
|
|
|
|
|
#
|
|
|
|
|
# While tests shouldn't leave any files or directories behind, when a test
|
|
|
|
|
# fails that can be tedious for it to arrange. The consequences can be
|
|
|
|
|
# especially nasty on Windows, since if a test leaves a file open, it
|
|
|
|
|
# cannot be deleted by name (while there's nothing we can do about that
|
|
|
|
|
# here either, we can display the name of the offending test, which is a
|
|
|
|
|
# real help).
|
|
|
|
|
name = os_helper.TESTFN
|
|
|
|
|
if not os.path.exists(name):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
nuker: Callable[[str], None]
|
|
|
|
|
if os.path.isdir(name):
|
|
|
|
|
import shutil
|
|
|
|
|
kind, nuker = "directory", shutil.rmtree
|
|
|
|
|
elif os.path.isfile(name):
|
|
|
|
|
kind, nuker = "file", os.unlink
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError(f"os.path says {name!r} exists but is neither "
|
|
|
|
|
f"directory nor file")
|
|
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
|
print_warning(f"{test_name} left behind {kind} {name!r}")
|
|
|
|
|
support.environment_altered = True
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
import stat
|
|
|
|
|
# fix possible permissions problems that might prevent cleanup
|
|
|
|
|
os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
|
|
|
|
nuker(name)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
print_warning(f"{test_name} left behind {kind} {name!r} "
|
|
|
|
|
f"and it couldn't be removed: {exc}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def abs_module_name(test_name: TestName, test_dir: StrPath | None) -> TestName:
|
|
|
|
|
if test_name.startswith('test.') or test_dir:
|
|
|
|
|
return test_name
|
|
|
|
|
else:
|
|
|
|
|
# Import it from the test package
|
|
|
|
|
return 'test.' + test_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# gh-90681: When rerunning tests, we might need to rerun the whole
|
|
|
|
|
# class or module suite if some its life-cycle hooks fail.
|
|
|
|
|
# Test level hooks are not affected.
|
|
|
|
|
_TEST_LIFECYCLE_HOOKS = frozenset((
|
|
|
|
|
'setUpClass', 'tearDownClass',
|
|
|
|
|
'setUpModule', 'tearDownModule',
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def normalize_test_name(test_full_name, *, is_error=False):
|
|
|
|
|
short_name = test_full_name.split(" ")[0]
|
|
|
|
|
if is_error and short_name in _TEST_LIFECYCLE_HOOKS:
|
|
|
|
|
if test_full_name.startswith(('setUpModule (', 'tearDownModule (')):
|
|
|
|
|
# if setUpModule() or tearDownModule() failed, don't filter
|
|
|
|
|
# tests with the test file name, don't use use filters.
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# This means that we have a failure in a life-cycle hook,
|
|
|
|
|
# we need to rerun the whole module or class suite.
|
|
|
|
|
# Basically the error looks like this:
|
|
|
|
|
# ERROR: setUpClass (test.test_reg_ex.RegTest)
|
|
|
|
|
# or
|
|
|
|
|
# ERROR: setUpModule (test.test_reg_ex)
|
|
|
|
|
# So, we need to parse the class / module name.
|
|
|
|
|
lpar = test_full_name.index('(')
|
|
|
|
|
rpar = test_full_name.index(')')
|
|
|
|
|
return test_full_name[lpar + 1: rpar].split('.')[-1]
|
|
|
|
|
return short_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def adjust_rlimit_nofile():
|
|
|
|
|
"""
|
|
|
|
|
On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256)
|
|
|
|
|
for our test suite to succeed. Raise it to something more reasonable. 1024
|
|
|
|
|
is a common Linux default.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
import resource
|
|
|
|
|
except ImportError:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
fd_limit, max_fds = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
|
|
|
|
|
|
|
|
desired_fds = 1024
|
|
|
|
|
|
|
|
|
|
if fd_limit < desired_fds and fd_limit < max_fds:
|
|
|
|
|
new_fd_limit = min(desired_fds, max_fds)
|
|
|
|
|
try:
|
|
|
|
|
resource.setrlimit(resource.RLIMIT_NOFILE,
|
|
|
|
|
(new_fd_limit, max_fds))
|
|
|
|
|
print(f"Raised RLIMIT_NOFILE: {fd_limit} -> {new_fd_limit}")
|
|
|
|
|
except (ValueError, OSError) as err:
|
|
|
|
|
print_warning(f"Unable to raise RLIMIT_NOFILE from {fd_limit} to "
|
|
|
|
|
f"{new_fd_limit}: {err}.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2023-12-01 20:09:00 +01:00
|
|
|
def format_resources(use_resources: Iterable[str]):
|
2023-10-12 23:45:36 +02:00
|
|
|
use_resources = set(use_resources)
|
|
|
|
|
all_resources = set(ALL_RESOURCES)
|
|
|
|
|
|
|
|
|
|
# Express resources relative to "all"
|
|
|
|
|
relative_all = ['all']
|
|
|
|
|
for name in sorted(all_resources - use_resources):
|
|
|
|
|
relative_all.append(f'-{name}')
|
|
|
|
|
for name in sorted(use_resources - all_resources):
|
|
|
|
|
relative_all.append(f'{name}')
|
|
|
|
|
all_text = ','.join(relative_all)
|
|
|
|
|
all_text = f"resources: {all_text}"
|
|
|
|
|
|
|
|
|
|
# List of enabled resources
|
|
|
|
|
text = ','.join(sorted(use_resources))
|
|
|
|
|
text = f"resources ({len(use_resources)}): {text}"
|
|
|
|
|
|
|
|
|
|
# Pick the shortest string (prefer relative to all if lengths are equal)
|
|
|
|
|
if len(all_text) <= len(text):
|
|
|
|
|
return all_text
|
|
|
|
|
else:
|
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_cpu_count():
|
|
|
|
|
if hasattr(os, 'sched_getaffinity'):
|
|
|
|
|
return len(os.sched_getaffinity(0))
|
|
|
|
|
else:
|
|
|
|
|
return os.cpu_count()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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),
|
|
|
|
|
"%s-endian" % sys.byteorder)
|
|
|
|
|
print("== Python build:", ' '.join(get_build_info()))
|
|
|
|
|
print("== cwd:", os.getcwd())
|
|
|
|
|
|
2023-12-01 20:09:00 +01:00
|
|
|
cpu_count: object = os.cpu_count()
|
2023-10-12 23:45:36 +02:00
|
|
|
if cpu_count:
|
|
|
|
|
affinity = process_cpu_count()
|
|
|
|
|
if affinity and affinity != cpu_count:
|
|
|
|
|
cpu_count = f"{affinity} (process) / {cpu_count} (system)"
|
|
|
|
|
print("== CPU count:", cpu_count)
|
|
|
|
|
print("== encodings: locale=%s FS=%s"
|
|
|
|
|
% (locale.getencoding(), sys.getfilesystemencoding()))
|
|
|
|
|
|
|
|
|
|
if use_resources:
|
|
|
|
|
text = format_resources(use_resources)
|
|
|
|
|
print(f"== {text}")
|
|
|
|
|
else:
|
|
|
|
|
print("== resources: all test resources are disabled, "
|
|
|
|
|
"use -u option to unskip tests")
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
msan = support.check_sanitizer(memory=True)
|
|
|
|
|
ubsan = support.check_sanitizer(ub=True)
|
|
|
|
|
sanitizers = []
|
|
|
|
|
if asan:
|
|
|
|
|
sanitizers.append("address")
|
|
|
|
|
if msan:
|
|
|
|
|
sanitizers.append("memory")
|
|
|
|
|
if ubsan:
|
|
|
|
|
sanitizers.append("undefined behavior")
|
|
|
|
|
if sanitizers:
|
|
|
|
|
print(f"== sanitizers: {', '.join(sanitizers)}")
|
|
|
|
|
for sanitizer, env_var in (
|
|
|
|
|
(asan, "ASAN_OPTIONS"),
|
|
|
|
|
(msan, "MSAN_OPTIONS"),
|
|
|
|
|
(ubsan, "UBSAN_OPTIONS"),
|
|
|
|
|
):
|
|
|
|
|
options= os.environ.get(env_var)
|
|
|
|
|
if sanitizer and options is not None:
|
|
|
|
|
print(f"== {env_var}={options!r}")
|
|
|
|
|
|
|
|
|
|
print(flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cleanup_temp_dir(tmp_dir: StrPath):
|
|
|
|
|
import glob
|
|
|
|
|
|
|
|
|
|
path = os.path.join(glob.escape(tmp_dir), TMP_PREFIX + '*')
|
|
|
|
|
print("Cleanup %s directory" % tmp_dir)
|
|
|
|
|
for name in glob.glob(path):
|
|
|
|
|
if os.path.isdir(name):
|
|
|
|
|
print("Remove directory: %s" % name)
|
|
|
|
|
os_helper.rmtree(name)
|
|
|
|
|
else:
|
|
|
|
|
print("Remove file: %s" % name)
|
|
|
|
|
os_helper.unlink(name)
|
|
|
|
|
|
|
|
|
|
WINDOWS_STATUS = {
|
|
|
|
|
0xC0000005: "STATUS_ACCESS_VIOLATION",
|
|
|
|
|
0xC00000FD: "STATUS_STACK_OVERFLOW",
|
|
|
|
|
0xC000013A: "STATUS_CONTROL_C_EXIT",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def get_signal_name(exitcode):
|
|
|
|
|
if exitcode < 0:
|
|
|
|
|
signum = -exitcode
|
|
|
|
|
try:
|
|
|
|
|
return signal.Signals(signum).name
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
return WINDOWS_STATUS[exitcode]
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
return None
|