Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,7 @@ def test_reduce_None(self):
with self.assertRaises(TypeError):
self.dumps(c)

@support.skip_if_unlimited_stack_size
@no_tracing
def test_bad_getattr(self):
# Issue #3514: crash when there is an infinite loop in __getattr__
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"check__all__", "skip_if_buggy_ucrt_strfptime",
"check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer",
"requires_limited_api", "requires_specialization", "thread_unsafe",
"skip_if_unlimited_stack_size",
# sys
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
Expand Down Expand Up @@ -1771,6 +1772,25 @@ def skip_if_pgo_task(test):
return test if ok else unittest.skip(msg)(test)


def skip_if_unlimited_stack_size(test):
"""Skip decorator for tests not run when an unlimited stack size is configured.

Tests using support.infinite_recursion([...]) may otherwise run into
an infinite loop, running until the memory on the system is filled and
crashing due to OOM.

See https://github.com/python/cpython/issues/143460.
"""
if is_wasi or os.name == "nt":
return test

import resource
curlim, maxlim = resource.getrlimit(resource.RLIMIT_STACK)
unlimited_stack_size_cond = curlim == maxlim and curlim in (-1, 0xFFFF_FFFF_FFFF_FFFF)
reason = "Not run due to unlimited stack size"
return unittest.skipIf(unlimited_stack_size_cond, reason)(test)


def detect_api_mismatch(ref_api, other_api, *, ignore=()):
"""Returns the set of items in ref_api not in other_api, except for a
defined list of items to be ignored in this check.
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from test import support
from test.support import os_helper
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, skip_if_unlimited_stack_size
from test.support.ast_helper import ASTTestMixin
from test.support.import_helper import ensure_lazy_imports
from test.test_ast.utils import to_tuple
Expand Down Expand Up @@ -989,6 +989,7 @@ def next(self):
enum._test_simple_enum(_Precedence, _ast_unparse._Precedence)

@support.cpython_only
@skip_if_unlimited_stack_size
@skip_wasi_stack_overflow()
@skip_emscripten_stack_overflow()
def test_ast_recursion_limit(self):
Expand Down Expand Up @@ -1127,6 +1128,7 @@ def test_pickling(self):
ast2 = pickle.loads(pickle.dumps(tree, protocol))
self.assertEqual(to_tuple(ast2), to_tuple(tree))

@skip_if_unlimited_stack_size
def test_copy_with_parents(self):
# gh-120108
code = """
Expand Down Expand Up @@ -1974,6 +1976,7 @@ def test_level_as_none(self):
exec(code, ns)
self.assertIn('sleep', ns)

@skip_if_unlimited_stack_size
@skip_emscripten_stack_overflow()
def test_recursion_direct(self):
e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1))
Expand All @@ -1982,6 +1985,7 @@ def test_recursion_direct(self):
with support.infinite_recursion():
compile(ast.Expression(e), "<test>", "eval")

@skip_if_unlimited_stack_size
@skip_emscripten_stack_overflow()
def test_recursion_indirect(self):
e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1))
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ def test_setstate_subclasses(self):
self.assertIs(type(r[0]), tuple)

@support.skip_if_sanitizer("thread sanitizer crashes in __tsan::FuncEntry", thread=True)
@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
def test_recursive_pickle(self):
with replaced_module('functools', self.module):
Expand Down Expand Up @@ -2139,6 +2140,7 @@ def orig(a: int) -> nonexistent: ...
@support.skip_on_s390x
@unittest.skipIf(support.is_wasi, "WASI has limited C stack")
@support.skip_if_sanitizer("requires deep stack", ub=True, thread=True)
@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
def test_lru_recursion(self):

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_isinstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def __bases__(self):
self.assertRaises(RecursionError, issubclass, int, X())
self.assertRaises(RecursionError, isinstance, 1, X())

@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_infinite_recursion_via_bases_tuple(self):
Expand All @@ -328,6 +329,7 @@ def __getattr__(self, attr):
with self.assertRaises(RecursionError):
issubclass(Failure(), int)

@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_infinite_cycle_in_bases(self):
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_json/test_recursion.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def default(self, o):
self.fail("didn't raise ValueError on default recursion")


@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_highly_nested_objects_decoding(self):
Expand All @@ -84,6 +85,7 @@ def test_highly_nested_objects_decoding(self):
with support.infinite_recursion():
self.loads('[' * very_deep + '1' + ']' * very_deep)

@support.skip_if_unlimited_stack_size
@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.requires_resource('cpu')
Expand All @@ -99,6 +101,7 @@ def test_highly_nested_objects_encoding(self):
with support.infinite_recursion(5000):
self.dumps(d)

@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_endless_recursion(self):
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ def test_recursive(depth, limit):
""")
script_helper.assert_python_ok("-c", code)

@support.skip_if_unlimited_stack_size
def test_recursion(self):
# Test infinite_recursion() and get_recursion_available() functions.
def recursive_function(depth):
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_tomllib/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def test_deepcopy(self):
}
self.assertEqual(obj_copy, expected_obj)

@support.skip_if_unlimited_stack_size
def test_inline_array_recursion_limit(self):
with support.infinite_recursion(max_depth=100):
available = support.get_recursion_available()
Expand All @@ -104,6 +105,7 @@ def test_inline_array_recursion_limit(self):
recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]"
tomllib.loads(recursive_array_toml)

@support.skip_if_unlimited_stack_size
def test_inline_table_recursion_limit(self):
with support.infinite_recursion(max_depth=100):
available = support.get_recursion_available()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Skip tests relying on infinite recusion if stack size is unlimited.
Loading