https://github.com/python/cpython/commit/3de6d3a100d75929864ef235cce81e8b8e80b07a
commit: 3de6d3a100d75929864ef235cce81e8b8e80b07a
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-11-07T14:50:40Z
summary:
gh-161430: Omit tests expected to be skipped in regrtest (#161432)
No longer list tests expected to be skipped in the final report.
Strip also "test." prefix in TestResults to make the output shorter.
files:
A Lib/test/libregrtest/skipped.py
A Misc/NEWS.d/next/Tests/2026-11-05-16-20-23.gh-issue-161430.M7b8N6.rst
M Lib/test/libregrtest/results.py
M Lib/test/support/__init__.py
M Lib/test/test_perf_profiler.py
M Lib/test/test_perfmaps.py
M Lib/test/test_samply_profiler.py
diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py
index 3475f645729182c..4096bfc7191b99b 100644
--- a/Lib/test/libregrtest/results.py
+++ b/Lib/test/libregrtest/results.py
@@ -5,6 +5,7 @@
from .runtests import RunTests
from .result import State, TestResult, TestStats, Location
+from .skipped import expected_to_be_skipped
from .utils import (
StrPath, TestName, TestTuple, TestList, FilterDict,
printlist, count, format_duration)
@@ -98,6 +99,8 @@ def get_exitcode(self, fail_env_changed: bool, fail_rerun:
bool) -> int:
def accumulate_result(self, result: TestResult, runtests: RunTests) ->
None:
test_name = result.test_name
+ # Remove "test." prefix added by some test packages
+ test_name = test_name.removeprefix('test.')
rerun = runtests.rerun
fail_env_changed = runtests.fail_env_changed
@@ -204,6 +207,16 @@ def write_junit(self, filename: StrPath) -> None:
for s in ET.tostringlist(root):
f.write(s)
+ def split_expected_skip(self, tests):
+ skipped = []
+ expected = []
+ for name in tests:
+ if expected_to_be_skipped(name):
+ expected.append(name)
+ else:
+ skipped.append(name)
+ return (skipped, expected)
+
def display_result(self, tests: TestTuple, quiet: bool, print_slowest:
bool) -> None:
ansi = get_colors()
green = ansi.GREEN
@@ -221,13 +234,20 @@ def display_result(self, tests: TestTuple, quiet: bool,
print_slowest: bool) ->
all_tests = []
omitted = set(tests) - self.get_executed()
+ # Filter tests expected to be skipped
+ skipped, expected_skip = self.split_expected_skip(self.skipped)
+ if expected_skip:
+ print()
+ count_text = count(len(expected_skip), "test")
+ print(f"{count_text} expected to be skipped (not listed)")
+
# less important
all_tests.append(
(sorted(omitted), "test", f"{yellow}{{}} omitted:{reset}")
)
if not quiet:
all_tests.append(
- (self.skipped, "test", f"{yellow}{{}} skipped:{reset}")
+ (skipped, "test", f"{yellow}{{}} skipped:{reset}")
)
all_tests.append(
(
diff --git a/Lib/test/libregrtest/skipped.py b/Lib/test/libregrtest/skipped.py
new file mode 100644
index 000000000000000..1116929a502f84f
--- /dev/null
+++ b/Lib/test/libregrtest/skipped.py
@@ -0,0 +1,329 @@
+"""
+Ruleset to check if a test is expected to be skipped.
+Tests are mostly filtered using sys.platform, except a few exceptions.
+"""
+
+import os
+import sys
+import sysconfig
+from test import support
+
+
+# On a WASI CI, the main regrtest process is run on the build Python and test
+# worker processes are run on the host Python. Detect WASI using
+# _PYTHON_HOST_PLATFORM environment variable. Below, IS_WASI should be checked
+# explicitly since test.support constants (such as support.has_socket_support)
+# are set by the build Python which is different.
+HOST_PLATFORM = os.environ.get('_PYTHON_HOST_PLATFORM', '')
+IS_WASI = support.is_wasi or HOST_PLATFORM.startswith('wasi')
+
+
+NEED_MULTIPROCESSING = {
+ 'test_multiprocessing_fork',
+ 'test_multiprocessing_forkserver',
+ 'test_multiprocessing_main_handling',
+ 'test_multiprocessing_spawn',
+}
+SELECT_TESTS = {
+ 'test_devpoll',
+ 'test_epoll',
+ 'test_kqueue',
+ 'test_poll',
+}
+NEED_TESTMULTIPHASE = {
+ # Lib/test/test_importlib/util.py requires _testmultiphase
+ 'test_compileall',
+ 'test_import',
+ 'test_pkgutil',
+ 'test_pyclbr',
+ 'test_runpy',
+}
+NEED_WORKING_THREADING = {
+ 'test_concurrent_futures',
+ 'test_queue',
+ 'test_thread',
+ 'test_threadedtempfile',
+ 'test_threading',
+ 'test_threading_local',
+ 'test_webbrowser',
+}
+NEED_TKINTER = {
+ 'test_idle',
+ 'test_tcl',
+ 'test_tkinter',
+ 'test_ttk',
+ 'test_ttk_textonly',
+ 'test_turtle',
+}
+
+
+# List of tests expected to be skipped on sys.platform
+EXPECTED_SKIP = set()
+if support.Py_GIL_DISABLED:
+ # Need GIL build
+ EXPECTED_SKIP.add('test_interpreters')
+else:
+ # Need Free Threading build
+ EXPECTED_SKIP.add('test_free_threading')
+
+if not support.has_socket_support or IS_WASI:
+ # Need socket support
+ EXPECTED_SKIP |= {
+ 'test_asyncgen',
+ 'test_asyncio',
+ 'test_docxmlrpc',
+ 'test_ftplib',
+ 'test_httplib',
+ 'test_httpservers',
+ 'test_imaplib',
+ 'test_mailbox',
+ 'test_poplib',
+ 'test_select',
+ 'test_selectors',
+ 'test_smtplib',
+ 'test_socket',
+ 'test_urllib',
+ 'test_urllib2',
+ 'test_urllib2_localnet',
+ 'test_urllib_response',
+ 'test_wsgiref',
+ 'test_xml_dom_xmlbuilder',
+ 'test_xmlrpc',
+ }
+ EXPECTED_SKIP |= SELECT_TESTS
+if not support.has_subprocess_support or IS_WASI:
+ # Need subprocess support
+ EXPECTED_SKIP |= {
+ 'test_argparse',
+ 'test_asyncio.test_subprocess',
+ 'test_c_stack_unwind',
+ 'test_cext',
+ 'test_clinic',
+ 'test_cmd_line',
+ 'test_embed',
+ 'test_faulthandler',
+ 'test_file_eintr',
+ 'test_gdb',
+ 'test_generated_cases',
+ 'test_getopt',
+ 'test_optparse',
+ 'test_perf_profiler',
+ 'test_regrtest',
+ 'test_repl',
+ 'test_samply_profiler',
+ 'test_subprocess',
+ 'test_tools',
+ }
+if not support.has_fork_support or IS_WASI:
+ # Need fork
+ EXPECTED_SKIP |= {
+ 'test_fork1',
+ 'test_multiprocessing_forkserver',
+ 'test_wait3',
+ 'test_wait4',
+ }
+if not(sys.platform in ("linux", "darwin") or support.MS_WINDOWS):
+ # Need Py_SUPPORTS_REMOTE_DEBUG macro
+ EXPECTED_SKIP.add('test_remote_pdb')
+if not sysconfig.is_python_build():
+ # Skip tests on installed Python.
+ # Tests only relevant for from-source Python builds
+ EXPECTED_SKIP |= {
+ # Parser/asdl.py is not installed
+ 'test_asdl_parser',
+ # Tools/clinic/ is not installed
+ 'test_clinic',
+ # python-gdb.py is not installed
+ 'test_gdb',
+ # Tools/cases_generator is not installed
+ 'test_generated_cases',
+ }
+if not sysconfig.get_config_var('WITH_DTRACE'):
+ # Need ./configure --with-dtrace
+ EXPECTED_SKIP.add('test_dtrace')
+
+
+if support.MS_WINDOWS:
+ # Tests specific to non-Windows platforms
+ EXPECTED_SKIP |= {
+ 'test_asyncio.test_unix_events', # UNIX only
+ 'test_dbm_gnu', # Need dbm.gnu module
+ 'test_dbm_ndbm', # Need dbm.ndbm module
+ 'test_fcntl', # Need fcntl module
+ 'test_free_threading.test_dbm_gnu', # Need dbm.gnu module
+ 'test_free_threading.test_grp', # Need grp module
+ 'test_free_threading.test_pwd', # Need pwd module
+ 'test_free_threading.test_readline', # Need readline module
+ 'test_free_threading.test_resource', # Need resource module
+ 'test_free_threading.test_syslog', # Need syslog module
+ 'test_gdb', # test_gdb doesn't work on Windows
+ 'test_grp', # Need grp module
+ 'test_ioctl', # Need fcntl module
+ 'test_openpty', # Need os.openpty()
+ 'test_pty', # Need termios module
+ 'test_pwd', # Need pwd module
+ 'test_readline', # Need readline module
+ 'test_resource', # Need resource module
+ 'test_syslog', # Need syslog module
+ 'test_termios', # Need termios module
+ 'test_threadsignals', # Can't test signal on Windows
+ 'test_tty', # Need termios and tty modules
+ 'test_wait3', # Need os.wait3()
+ 'test_wait4', # Need os.wait4()
+ }
+else:
+ EXPECTED_SKIP |= {
+ # Tests specific to Windows
+ 'test_asyncio.test_windows_events',
+ 'test_asyncio.test_windows_utils',
+ 'test_msvcrt',
+ 'test_os.test_windows',
+ 'test_startfile', # Need os.startfile()
+ 'test_winapi',
+ 'test_winconsoleio',
+ 'test_winreg',
+ 'test_winsound',
+ 'test_wmi',
+ }
+if sys.platform in ("win32", 'darwin'):
+ EXPECTED_SKIP.add('test_multiprocessing_fork')
+if sys.platform in ("win32", "cygwin"):
+ EXPECTED_SKIP.add('test_multiprocessing_forkserver')
+
+if not support.is_android:
+ # Test specific to Android
+ EXPECTED_SKIP.add('test_android')
+
+
+if not support.is_apple:
+ # Test specific to Apple
+ EXPECTED_SKIP.add('test_apple')
+
+
+if sys.platform == "darwin":
+ # On macOS, running gdb fails badly:
+ # https://github.com/python/cpython/pull/161520
+ EXPECTED_SKIP.add('test_gdb')
+
+
+if support.is_apple_mobile:
+ # configure disables multiple extension modules on Apple mobile
+ EXPECTED_SKIP |= {
+ 'test_curses', # need _curses
+ 'test_dbm_gnu', # need _gdbm
+ 'test_grp', # need grp
+ 'test_pwd', # need pwd
+ 'test_readline', # need readline
+ 'test_syslog', # need syslog
+ }
+ # configure disables _multiprocessing on Apple mobile
+ EXPECTED_SKIP |= NEED_MULTIPROCESSING
+ # configure disables _tkinter on Apple mobile
+ EXPECTED_SKIP |= NEED_TKINTER
+
+
+if support.is_emscripten:
+ # configure disables multiple extension modules on Emscripten
+ EXPECTED_SKIP |= {
+ 'test__interpchannels', # need _interpchannels
+ 'test__interpreters', # need _interpreters
+ 'test_crossinterp', # need _interpreters
+ 'test_curses', # need _curses
+ 'test_dbm_gnu', # need _gdbm
+ 'test_dbm_ndbm', # need _dbm
+ 'test_grp', # need grp
+ 'test_pwd', # need pwd
+ 'test_readline', # need readline
+ 'test_resource', # need resource
+ 'test_syslog', # need syslog
+ }
+ # configure disables _multiprocessing on Emscripten
+ EXPECTED_SKIP |= NEED_MULTIPROCESSING
+
+ # Missing os.openpty() function on Emscripten
+ EXPECTED_SKIP.add('test_openpty')
+
+ EXPECTED_SKIP |= {
+ # Incompatible with Emscripten's mmap emulation
+ 'test_mmap',
+ # Cannot create socketpair on Emscripten/WASI
+ 'test_selectors',
+ }
+
+
+if support.is_android or support.is_apple_mobile or support.is_wasm32 or
IS_WASI:
+ EXPECTED_SKIP |= {
+ # pty is not available on these platforms
+ 'test_pty',
+ # venv is not available on these platforms
+ 'test_venv',
+ }
+
+
+if IS_WASI:
+ # configure disables multiple extension modules on WASI
+ EXPECTED_SKIP |= {
+ 'test__interpchannels', # need _interpchannels
+ 'test__interpreters', # need _interpreters
+ 'test_crossinterp', # need _interpreters
+ 'test_curses', # need _curses
+ 'test_dbm_gnu', # need _gdbm
+ 'test_dbm_ndbm', # need _dbm
+ 'test_external_inspection', # need _remote_debugging
+ 'test_fcntl', # need fcntl
+ 'test_gc_stats', # need _remote_debugging
+ 'test_grp', # need grp
+ 'test_ioctl', # need fcntl
+ 'test_mmap', # need mmap
+ 'test_pwd', # need pwd
+ 'test_pyrepl', # need termios
+ 'test_remote_pdb', # need _remote_debugging
+ 'test_resource', # need resource
+ 'test_syslog', # need syslog
+ 'test_termios', # need termios
+ 'test_tty', # need termios
+ 'test_xxlimited', # need xxlimited
+ }
+ # configure disables _multiprocessing and _testmultiphase
+ # extension modules on WASI
+ EXPECTED_SKIP |= NEED_MULTIPROCESSING
+ EXPECTED_SKIP |= NEED_TESTMULTIPHASE
+
+ # Missing os.openpty() function on WASI
+ EXPECTED_SKIP.add('test_openpty')
+
+
+def expected_to_be_skipped(test_name):
+ # Test EXPECTED_SKIP
+ test_name = test_name.removeprefix('test.')
+ if test_name in EXPECTED_SKIP:
+ return True
+ package = test_name.split('.')[0]
+ if package in EXPECTED_SKIP:
+ return True
+
+ # Custom ruleset
+ if (test_name in NEED_WORKING_THREADING
+ or package in NEED_WORKING_THREADING):
+ # Detect whether Python can start new threads
+ # - wasm32-wasi does not have threading.
+ # - wasm32-emscripten can be compiled with or without pthread
+ # support (-s USE_PTHREADS / __EMSCRIPTEN_PTHREADS__).
+ if IS_WASI:
+ return True
+ from test.support import threading_helper
+ return not threading_helper.can_start_thread
+
+ if test_name in SELECT_TESTS:
+ # For example, test_epoll needs select.epoll()
+ import select
+ name = test_name.removeprefix('test_')
+ return not hasattr(select, name)
+
+ if test_name in ('test_perf_profiler', 'test_perfmaps',
'test_samply_profiler'):
+ # Need perf trampoline profiling: configure defines the
+ # PY_HAVE_PERF_TRAMPOLINE macro on Linux (x86-64 and AArch64)
+ # and on macOS.
+ return not support.supports_perf_trampoline_profiling()
+
+ return False
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index c14c50dae5bedcb..df3ae0890c0f126 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -3614,3 +3614,10 @@ def inject_memory_error_cm(start=0, stop=0):
yield
finally:
_testcapi.remove_mem_hooks()
+
+
+def supports_perf_trampoline_profiling():
+ perf_trampoline = sysconfig.get_config_var("PY_HAVE_PERF_TRAMPOLINE")
+ if not perf_trampoline:
+ return False
+ return int(perf_trampoline) == 1
diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py
index 0ebc0792cfc8222..8a2c4889d9471c4 100644
--- a/Lib/test/test_perf_profiler.py
+++ b/Lib/test/test_perf_profiler.py
@@ -23,14 +23,7 @@
raise unittest.SkipTest("test crash randomly on ASAN/MSAN build")
-def supports_trampoline_profiling():
- perf_trampoline = sysconfig.get_config_var("PY_HAVE_PERF_TRAMPOLINE")
- if not perf_trampoline:
- return False
- return int(perf_trampoline) == 1
-
-
-if not supports_trampoline_profiling():
+if not support.supports_perf_trampoline_profiling():
raise unittest.SkipTest("perf trampoline profiling not supported")
diff --git a/Lib/test/test_perfmaps.py b/Lib/test/test_perfmaps.py
index ee4eb50033c4704..e6e5985233d1a8f 100644
--- a/Lib/test/test_perfmaps.py
+++ b/Lib/test/test_perfmaps.py
@@ -1,20 +1,14 @@
import os
import sys
-import sysconfig
import unittest
+from test import support
try:
from _testinternalcapi import perf_map_state_teardown, write_perf_map_entry
except ImportError:
raise unittest.SkipTest("requires _testinternalcapi")
-def supports_trampoline_profiling():
- perf_trampoline = sysconfig.get_config_var("PY_HAVE_PERF_TRAMPOLINE")
- if not perf_trampoline:
- return False
- return int(perf_trampoline) == 1
-
-if not supports_trampoline_profiling():
+if not support.supports_perf_trampoline_profiling():
raise unittest.SkipTest("perf trampoline profiling not supported")
class TestPerfMapWriting(unittest.TestCase):
diff --git a/Lib/test/test_samply_profiler.py b/Lib/test/test_samply_profiler.py
index b9ac6586f4f7f55..ae3f047b15194a8 100644
--- a/Lib/test/test_samply_profiler.py
+++ b/Lib/test/test_samply_profiler.py
@@ -1,7 +1,6 @@
import unittest
import subprocess
import sys
-import sysconfig
import os
import pathlib
from test import support
@@ -20,14 +19,7 @@
raise unittest.SkipTest("test crash randomly on ASAN/MSAN build")
-def supports_trampoline_profiling():
- perf_trampoline = sysconfig.get_config_var("PY_HAVE_PERF_TRAMPOLINE")
- if not perf_trampoline:
- return False
- return int(perf_trampoline) == 1
-
-
-if not supports_trampoline_profiling():
+if not support.supports_perf_trampoline_profiling():
raise unittest.SkipTest("perf trampoline profiling not supported")
diff --git
a/Misc/NEWS.d/next/Tests/2026-11-05-16-20-23.gh-issue-161430.M7b8N6.rst
b/Misc/NEWS.d/next/Tests/2026-11-05-16-20-23.gh-issue-161430.M7b8N6.rst
new file mode 100644
index 000000000000000..b53d2f01d5ce141
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2026-11-05-16-20-23.gh-issue-161430.M7b8N6.rst
@@ -0,0 +1,2 @@
+No longer list tests expected to be skipped in the final report. Patch by
+Victor Stinner.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]