From 7bbdf6d3d3ac6ca2b9963fbdbd45861f331073c2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 26 Jun 2026 16:36:41 +0300 Subject: [PATCH 1/4] [3.13] gh-152236: Fix skips on `_testcapi.set_nomemory` tests (GH-152253) (cherry picked from commit 1cbe460eb6c5f40980463f381b3096c92320ff84) Co-authored-by: sobolevn --- Lib/test/support/__init__.py | 18 +++++++++++++++++- Lib/test/test_atexit.py | 1 - Lib/test/test_capi/test_mem.py | 4 +--- Lib/test/test_class.py | 6 +----- Lib/test/test_exceptions.py | 18 +++--------------- Lib/test/test_interpreters/test_stress.py | 11 ++++++----- Lib/test/test_repl.py | 7 +------ Lib/test/test_weakref.py | 2 +- 8 files changed, 30 insertions(+), 37 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 737ce94a120431..6d8a1e786a4b5f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -34,7 +34,7 @@ "requires_gil_enabled", "requires_linux_version", "requires_mac_ver", "check_syntax_error", "requires_gzip", "requires_bz2", "requires_lzma", - "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", + "bigmemtest", "nomemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "requires_zlib", "has_fork_support", "requires_fork", "has_subprocess_support", "requires_subprocess", @@ -1215,6 +1215,22 @@ def wrapper(self): return wrapper return decorator +def nomemtest(f): + """Check that we can use this test with `_testcapi.set_nomemory`.""" + from .import_helper import import_module + + @functools.wraps(f) + def internal(*args, **kwargs): + import_module('_testcapi') + return f(*args, **kwargs) + + return unittest.skipIf( + # Python built with Py_TRACE_REFS fail with a fatal error in + # _PyRefchain_Trace() on memory allocation error. + Py_TRACE_REFS, + 'cannot test Py_TRACE_REFS build', + )(cpython_only(internal)) + def bigaddrspacetest(f): """Decorator for tests that fill the address space.""" def wrapper(self): diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index 913b7556be83af..4bd3c8e567e904 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -100,6 +100,5 @@ def callback(): self.assertEqual(os.read(r, len(expected)), expected) os.close(r) - if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_mem.py b/Lib/test/test_capi/test_mem.py index 6ab7b685c2e18b..d67809956ac29d 100644 --- a/Lib/test/test_capi/test_mem.py +++ b/Lib/test/test_capi/test_mem.py @@ -112,9 +112,7 @@ def test_pyobject_forbidden_bytes_is_freed(self): def test_pyobject_freed_is_freed(self): self.check_pyobject_is_freed('check_pyobject_freed_is_freed') - # Python built with Py_TRACE_REFS fail with a fatal error in - # _PyRefchain_Trace() on memory allocation error. - @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + @support.nomemtest def test_set_nomemory(self): code = """if 1: import _testcapi diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 086aee9024c2e3..b2a815b03f02e8 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -447,7 +447,6 @@ def __delattr__(self, *args): def testHasAttrString(self): import sys - from test.support import import_helper _testlimitedcapi = import_helper.import_module('_testlimitedcapi') class A: @@ -995,11 +994,8 @@ class C: C.a = X() C.a = X() - @cpython_only + @support.nomemtest def test_detach_materialized_dict_no_memory(self): - # Skip test if _testcapi is not available: - import_helper.import_module('_testcapi') - code = """if 1: import test.support import _testcapi diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index ca1d2aa86bd0c8..c6a1cac2fe976f 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1531,11 +1531,7 @@ def recurse_in_body_and_except(): sys.setrecursionlimit(recursionlimit) - @cpython_only - # Python built with Py_TRACE_REFS fail with a fatal error in - # _PyRefchain_Trace() on memory allocation error. - @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') - @unittest.skipIf(_testcapi is None, "requires _testcapi") + @support.nomemtest def test_recursion_normalizing_with_no_memory(self): # Issue #30697. Test that in the abort that occurs when there is no # memory left and the size of the Python frames stack is greater than @@ -1719,11 +1715,7 @@ def test_unhandled(self): self.assertIn("test message", report) self.assertTrue(report.endswith("\n")) - @cpython_only - # Python built with Py_TRACE_REFS fail with a fatal error in - # _PyRefchain_Trace() on memory allocation error. - @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') - @unittest.skipIf(_testcapi is None, "requires _testcapi") + @support.nomemtest def test_memory_error_in_PyErr_PrintEx(self): code = """if 1: import _testcapi @@ -1857,12 +1849,8 @@ def test_memory_error_in_subinterp(self): rc, _, err = script_helper.assert_python_ok("-c", code) self.assertIn(b'MemoryError', err) - @cpython_only - # Python built with Py_TRACE_REFS fail with a fatal error in - # _PyRefchain_Trace() on memory allocation error. - @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + @support.nomemtest def test_exec_set_nomemory_hang(self): - import_module("_testcapi") # gh-134163: A MemoryError inside code that was wrapped by a try/except # block would lead to an infinite loop. diff --git a/Lib/test/test_interpreters/test_stress.py b/Lib/test/test_interpreters/test_stress.py index a39c9352f728d3..27d42d963f5f0d 100644 --- a/Lib/test/test_interpreters/test_stress.py +++ b/Lib/test/test_interpreters/test_stress.py @@ -5,7 +5,7 @@ from test.support import import_helper from test.support import threading_helper # Raise SkipTest if subinterpreters not supported. -import_helper.import_module('_interpreters') +_interpreters = import_helper.import_module('_interpreters') from test.support import interpreters from test.support.interpreters import InterpreterError from .utils import TestBase @@ -75,12 +75,13 @@ def run(): start.set() support.gc_collect() + @support.nomemtest def test_create_interpreter_no_memory(self): - import _interpreters - _testcapi = import_helper.import_module("_testcapi") + import _testcapi - with self.assertRaises(InterpreterError): - _testcapi.set_nomemory(0, 1) + assertion = self.assertRaises(InterpreterError) + _testcapi.set_nomemory(0, 1) + with assertion: _interpreters.create() diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 16109820beea74..9115e8cebabaca 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -17,7 +17,6 @@ SHORT_TIMEOUT, ) from test.support.script_helper import kill_python -from test.support.import_helper import import_module try: import pty @@ -99,12 +98,8 @@ def run_on_interactive_mode(source): @support.force_not_colorized_test_class class TestInteractiveInterpreter(unittest.TestCase): - @cpython_only - # Python built with Py_TRACE_REFS fail with a fatal error in - # _PyRefchain_Trace() on memory allocation error. - @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + @support.nomemtest def test_no_memory(self): - import_module("_testcapi") # Issue #30696: Fix the interactive interpreter looping endlessly when # no memory. Check also that the fix does not break the interactive # loop when an exception is raised. diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 1a820d089d663d..d1523342ce1073 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1022,7 +1022,7 @@ def __del__(self): pass del x support.gc_collect() - @support.cpython_only + @support.nomemtest def test_no_memory_when_clearing(self): # gh-118331: Make sure we do not raise an exception from the destructor # when clearing weakrefs if allocating the intermediate tuple fails. From 593060fbc6a78240fbc147cc02dcb960910442e2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 26 Jun 2026 16:51:43 +0300 Subject: [PATCH 2/4] Update Lib/test/test_atexit.py --- Lib/test/test_atexit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index 4bd3c8e567e904..913b7556be83af 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -100,5 +100,6 @@ def callback(): self.assertEqual(os.read(r, len(expected)), expected) os.close(r) + if __name__ == "__main__": unittest.main() From 5bdda220bad6fc0c44888c84a3960df0127e30f6 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 26 Jun 2026 17:15:05 +0300 Subject: [PATCH 3/4] Fix CI --- Lib/test/test_class.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index b2a815b03f02e8..72e6d1676f594f 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -1,6 +1,7 @@ "Test the functionality of Python classes implementing operators." import unittest +from test import support from test.support import cpython_only, import_helper, script_helper testmeths = [ From b0894d22744d60217f5617b3ff74f27f97db5675 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 26 Jun 2026 17:42:39 +0300 Subject: [PATCH 4/4] Fix CI --- Lib/test/test_interpreters/test_stress.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_interpreters/test_stress.py b/Lib/test/test_interpreters/test_stress.py index 27d42d963f5f0d..e3060cbd8bccd4 100644 --- a/Lib/test/test_interpreters/test_stress.py +++ b/Lib/test/test_interpreters/test_stress.py @@ -79,9 +79,8 @@ def run(): def test_create_interpreter_no_memory(self): import _testcapi - assertion = self.assertRaises(InterpreterError) - _testcapi.set_nomemory(0, 1) - with assertion: + with self.assertRaises(InterpreterError): + _testcapi.set_nomemory(0, 1) _interpreters.create()