Skip to content

Commit 61e0366

Browse files
authored
gh-143460: Skip infinite recusion tests for infinite stack size (#143606)
Avoid tests being killed due to OOM on Linux if a system is configured with 'ulimit -s unlimited' by skipping tests relying on infinite recursion. While unclear if Python should support 'ulimit -s unlimited', we should at least try to avoid failing a PGO build running tests due to an unlimited stack size being set. Signed-off-by: Jan André Reuter <j.reuter@fz-juelich.de>
1 parent c696f33 commit 61e0366

File tree

9 files changed

+37
-1
lines changed

9 files changed

+37
-1
lines changed

Lib/test/pickletester.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,6 +2438,7 @@ def test_reduce_None(self):
24382438
with self.assertRaises(TypeError):
24392439
self.dumps(c)
24402440

2441+
@support.skip_if_unlimited_stack_size
24412442
@no_tracing
24422443
def test_bad_getattr(self):
24432444
# Issue #3514: crash when there is an infinite loop in __getattr__

Lib/test/support/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"check__all__", "skip_if_buggy_ucrt_strfptime",
4646
"check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer",
4747
"requires_limited_api", "requires_specialization", "thread_unsafe",
48+
"skip_if_unlimited_stack_size",
4849
# sys
4950
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
5051
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
@@ -1771,6 +1772,25 @@ def skip_if_pgo_task(test):
17711772
return test if ok else unittest.skip(msg)(test)
17721773

17731774

1775+
def skip_if_unlimited_stack_size(test):
1776+
"""Skip decorator for tests not run when an unlimited stack size is configured.
1777+
1778+
Tests using support.infinite_recursion([...]) may otherwise run into
1779+
an infinite loop, running until the memory on the system is filled and
1780+
crashing due to OOM.
1781+
1782+
See https://github.com/python/cpython/issues/143460.
1783+
"""
1784+
if is_wasi or os.name == "nt":
1785+
return test
1786+
1787+
import resource
1788+
curlim, maxlim = resource.getrlimit(resource.RLIMIT_STACK)
1789+
unlimited_stack_size_cond = curlim == maxlim and curlim in (-1, 0xFFFF_FFFF_FFFF_FFFF)
1790+
reason = "Not run due to unlimited stack size"
1791+
return unittest.skipIf(unlimited_stack_size_cond, reason)(test)
1792+
1793+
17741794
def detect_api_mismatch(ref_api, other_api, *, ignore=()):
17751795
"""Returns the set of items in ref_api not in other_api, except for a
17761796
defined list of items to be ignored in this check.

Lib/test/test_ast/test_ast.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from test import support
2727
from test.support import os_helper
28-
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow
28+
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, skip_if_unlimited_stack_size
2929
from test.support.ast_helper import ASTTestMixin
3030
from test.support.import_helper import ensure_lazy_imports
3131
from test.test_ast.utils import to_tuple
@@ -989,6 +989,7 @@ def next(self):
989989
enum._test_simple_enum(_Precedence, _ast_unparse._Precedence)
990990

991991
@support.cpython_only
992+
@skip_if_unlimited_stack_size
992993
@skip_wasi_stack_overflow()
993994
@skip_emscripten_stack_overflow()
994995
def test_ast_recursion_limit(self):
@@ -1127,6 +1128,7 @@ def test_pickling(self):
11271128
ast2 = pickle.loads(pickle.dumps(tree, protocol))
11281129
self.assertEqual(to_tuple(ast2), to_tuple(tree))
11291130

1131+
@skip_if_unlimited_stack_size
11301132
def test_copy_with_parents(self):
11311133
# gh-120108
11321134
code = """
@@ -1974,6 +1976,7 @@ def test_level_as_none(self):
19741976
exec(code, ns)
19751977
self.assertIn('sleep', ns)
19761978

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

1988+
@skip_if_unlimited_stack_size
19851989
@skip_emscripten_stack_overflow()
19861990
def test_recursion_indirect(self):
19871991
e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1))

Lib/test/test_functools.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ def test_setstate_subclasses(self):
438438
self.assertIs(type(r[0]), tuple)
439439

440440
@support.skip_if_sanitizer("thread sanitizer crashes in __tsan::FuncEntry", thread=True)
441+
@support.skip_if_unlimited_stack_size
441442
@support.skip_emscripten_stack_overflow()
442443
def test_recursive_pickle(self):
443444
with replaced_module('functools', self.module):
@@ -2139,6 +2140,7 @@ def orig(a: int) -> nonexistent: ...
21392140
@support.skip_on_s390x
21402141
@unittest.skipIf(support.is_wasi, "WASI has limited C stack")
21412142
@support.skip_if_sanitizer("requires deep stack", ub=True, thread=True)
2143+
@support.skip_if_unlimited_stack_size
21422144
@support.skip_emscripten_stack_overflow()
21432145
def test_lru_recursion(self):
21442146

Lib/test/test_isinstance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ def __bases__(self):
317317
self.assertRaises(RecursionError, issubclass, int, X())
318318
self.assertRaises(RecursionError, isinstance, 1, X())
319319

320+
@support.skip_if_unlimited_stack_size
320321
@support.skip_emscripten_stack_overflow()
321322
@support.skip_wasi_stack_overflow()
322323
def test_infinite_recursion_via_bases_tuple(self):
@@ -328,6 +329,7 @@ def __getattr__(self, attr):
328329
with self.assertRaises(RecursionError):
329330
issubclass(Failure(), int)
330331

332+
@support.skip_if_unlimited_stack_size
331333
@support.skip_emscripten_stack_overflow()
332334
@support.skip_wasi_stack_overflow()
333335
def test_infinite_cycle_in_bases(self):

Lib/test/test_json/test_recursion.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def default(self, o):
6868
self.fail("didn't raise ValueError on default recursion")
6969

7070

71+
@support.skip_if_unlimited_stack_size
7172
@support.skip_emscripten_stack_overflow()
7273
@support.skip_wasi_stack_overflow()
7374
def test_highly_nested_objects_decoding(self):
@@ -84,6 +85,7 @@ def test_highly_nested_objects_decoding(self):
8485
with support.infinite_recursion():
8586
self.loads('[' * very_deep + '1' + ']' * very_deep)
8687

88+
@support.skip_if_unlimited_stack_size
8789
@support.skip_wasi_stack_overflow()
8890
@support.skip_emscripten_stack_overflow()
8991
@support.requires_resource('cpu')
@@ -99,6 +101,7 @@ def test_highly_nested_objects_encoding(self):
99101
with support.infinite_recursion(5000):
100102
self.dumps(d)
101103

104+
@support.skip_if_unlimited_stack_size
102105
@support.skip_emscripten_stack_overflow()
103106
@support.skip_wasi_stack_overflow()
104107
def test_endless_recursion(self):

Lib/test/test_support.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ def test_recursive(depth, limit):
672672
""")
673673
script_helper.assert_python_ok("-c", code)
674674

675+
@support.skip_if_unlimited_stack_size
675676
def test_recursion(self):
676677
# Test infinite_recursion() and get_recursion_available() functions.
677678
def recursive_function(depth):

Lib/test/test_tomllib/test_misc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def test_deepcopy(self):
9393
}
9494
self.assertEqual(obj_copy, expected_obj)
9595

96+
@support.skip_if_unlimited_stack_size
9697
def test_inline_array_recursion_limit(self):
9798
with support.infinite_recursion(max_depth=100):
9899
available = support.get_recursion_available()
@@ -104,6 +105,7 @@ def test_inline_array_recursion_limit(self):
104105
recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]"
105106
tomllib.loads(recursive_array_toml)
106107

108+
@support.skip_if_unlimited_stack_size
107109
def test_inline_table_recursion_limit(self):
108110
with support.infinite_recursion(max_depth=100):
109111
available = support.get_recursion_available()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Skip tests relying on infinite recusion if stack size is unlimited.

0 commit comments

Comments
 (0)