Skip to content

Commit 3dd4fdc

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.15] gh-154211: Skip C-stack exhaustion tests when the C stack is huge (GH-154212) (GH-154216)
(cherry picked from commit 863b312) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent b7c3b52 commit 3dd4fdc

20 files changed

Lines changed: 73 additions & 17 deletions

Lib/test/list_tests.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from functools import cmp_to_key
77

88
from test import seq_tests
9-
from test.support import ALWAYS_EQ, NEVER_EQ
9+
from test.support import ALWAYS_EQ, NEVER_EQ, skip_if_huge_c_stack
1010
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow
1111

1212

@@ -60,6 +60,7 @@ def test_repr(self):
6060
self.assertEqual(str(a2), "[0, 1, 2, [...], 3]")
6161
self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]")
6262

63+
@skip_if_huge_c_stack(200_000)
6364
@skip_wasi_stack_overflow()
6465
@skip_emscripten_stack_overflow()
6566
def test_repr_deep(self):

Lib/test/mapping_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ def __repr__(self):
629629
d = self._full_mapping({1: BadRepr()})
630630
self.assertRaises(Exc, repr, d)
631631

632+
@support.skip_if_huge_c_stack()
632633
@support.skip_wasi_stack_overflow()
633634
@support.skip_emscripten_stack_overflow()
634635
@support.skip_if_sanitizer("requires deep stack", ub=True)

Lib/test/support/__init__.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +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",
48+
"skip_if_unlimited_stack_size", "skip_if_huge_c_stack",
4949
# sys
5050
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
5151
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
@@ -2827,6 +2827,32 @@ def exceeds_recursion_limit():
28272827
return 150_000
28282828

28292829

2830+
def skip_if_huge_c_stack(depth=150_000):
2831+
"""Skip decorator for tests which cannot overflow the C stack.
2832+
2833+
Tests exhausting the C stack with *depth* recursive calls cannot
2834+
trigger the recursion protection if the C stack is too large (e.g.
2835+
with a large or unlimited RLIMIT_STACK), and either fail, or run
2836+
for a very long time, or crash, or consume all memory.
2837+
"""
2838+
try:
2839+
from _testinternalcapi import get_c_recursion_remaining
2840+
except ImportError:
2841+
# Fall back to checking for an unlimited stack size.
2842+
huge = False
2843+
if not (is_emscripten or is_wasi) and os.name != "nt":
2844+
import resource
2845+
soft, hard = resource.getrlimit(resource.RLIMIT_STACK)
2846+
huge = soft == hard and soft in (-1, 0xFFFF_FFFF_FFFF_FFFF)
2847+
else:
2848+
remaining = get_c_recursion_remaining()
2849+
# A negative value means integer overflow in the estimate
2850+
# (e.g. with an unlimited RLIMIT_STACK).
2851+
huge = remaining >= depth or remaining < 0
2852+
return unittest.skipIf(
2853+
huge, f"the C stack is large enough for {depth} recursive calls")
2854+
2855+
28302856
# Windows doesn't have os.uname() but it doesn't support s390x.
28312857
is_s390x = hasattr(os, 'uname') and os.uname().machine == 's390x'
28322858
skip_on_s390x = unittest.skipIf(is_s390x, 'skipped on s390x')

Lib/test/test_ast/test_ast.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
from test import support
2828
from test.support import os_helper
29-
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, skip_if_unlimited_stack_size
29+
from test.support import (skip_emscripten_stack_overflow,
30+
skip_wasi_stack_overflow,
31+
skip_if_unlimited_stack_size, skip_if_huge_c_stack)
3032
from test.support.ast_helper import ASTTestMixin
3133
from test.support.import_helper import ensure_lazy_imports
3234
from test.test_ast.utils import to_tuple
@@ -1017,7 +1019,7 @@ def next(self):
10171019
enum._test_simple_enum(_Precedence, _ast_unparse._Precedence)
10181020

10191021
@support.cpython_only
1020-
@skip_if_unlimited_stack_size
1022+
@support.skip_if_huge_c_stack(100_000 if sys.platform == "android" else 500_000)
10211023
@skip_wasi_stack_overflow()
10221024
@skip_emscripten_stack_overflow()
10231025
def test_ast_recursion_limit(self):
@@ -2026,7 +2028,7 @@ def test_level_as_none(self):
20262028
exec(code, ns)
20272029
self.assertIn('sleep', ns)
20282030

2029-
@skip_if_unlimited_stack_size
2031+
@skip_if_huge_c_stack()
20302032
@skip_emscripten_stack_overflow()
20312033
def test_recursion_direct(self):
20322034
e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1))
@@ -2035,7 +2037,7 @@ def test_recursion_direct(self):
20352037
with support.infinite_recursion():
20362038
compile(ast.Expression(e), "<test>", "eval")
20372039

2038-
@skip_if_unlimited_stack_size
2040+
@skip_if_huge_c_stack()
20392041
@skip_emscripten_stack_overflow()
20402042
def test_recursion_indirect(self):
20412043
e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1))

Lib/test/test_call.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set_recursion_limit, skip_on_s390x,
44
skip_emscripten_stack_overflow,
55
skip_wasi_stack_overflow, skip_if_sanitizer,
6-
import_helper)
6+
skip_if_huge_c_stack, import_helper)
77
try:
88
import _testcapi
99
except ImportError:
@@ -1062,6 +1062,7 @@ def get_sp():
10621062
@unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack")
10631063
@skip_if_sanitizer("requires deep stack", thread=True)
10641064
@unittest.skipIf(_testcapi is None, "requires _testcapi")
1065+
@skip_if_huge_c_stack(90_000)
10651066
@skip_emscripten_stack_overflow()
10661067
@skip_wasi_stack_overflow()
10671068
def test_super_deep(self):

Lib/test/test_class.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ class Custom:
555555
self.assertFalse(hasattr(o, "__call__"))
556556
self.assertFalse(hasattr(c, "__call__"))
557557

558+
@support.skip_if_huge_c_stack()
558559
@support.skip_emscripten_stack_overflow()
559560
@support.skip_wasi_stack_overflow()
560561
def testSFBug532646(self):

Lib/test/test_compile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ def test_yet_more_evil_still_undecodable(self):
724724

725725
@support.cpython_only
726726
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
727+
@support.skip_if_huge_c_stack(100_000 if sys.platform == "android" else 500_000)
727728
@support.skip_emscripten_stack_overflow()
728729
def test_compiler_recursion_limit(self):
729730
# Compiler frames are small

Lib/test/test_copy.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ def test_deepcopy_list(self):
377377
self.assertIsNot(x, y)
378378
self.assertIsNot(x[0], y[0])
379379

380+
@support.skip_if_huge_c_stack()
380381
@support.skip_emscripten_stack_overflow()
381382
@support.skip_wasi_stack_overflow()
382383
def test_deepcopy_reflexive_list(self):
@@ -406,6 +407,7 @@ def test_deepcopy_tuple_of_immutables(self):
406407
y = copy.deepcopy(x)
407408
self.assertIs(x, y)
408409

410+
@support.skip_if_huge_c_stack()
409411
@support.skip_emscripten_stack_overflow()
410412
@support.skip_wasi_stack_overflow()
411413
def test_deepcopy_reflexive_tuple(self):
@@ -449,6 +451,7 @@ def test_deepcopy_frozendict(self):
449451
self.assertIsNot(x[0], y[0])
450452
self.assertIs(y[0]['foo'], y)
451453

454+
@support.skip_if_huge_c_stack()
452455
@support.skip_emscripten_stack_overflow()
453456
@support.skip_wasi_stack_overflow()
454457
def test_deepcopy_reflexive_dict(self):
@@ -609,6 +612,7 @@ def __eq__(self, other):
609612
self.assertIsNot(y, x)
610613
self.assertIsNot(y.foo, x.foo)
611614

615+
@support.skip_if_huge_c_stack()
612616
def test_deepcopy_reflexive_inst(self):
613617
class C:
614618
pass
@@ -671,6 +675,7 @@ def __eq__(self, other):
671675
self.assertEqual(y, x)
672676
self.assertIsNot(y.foo, x.foo)
673677

678+
@support.skip_if_huge_c_stack()
674679
def test_reconstruct_reflexive(self):
675680
class C(object):
676681
pass

Lib/test/test_ctypes/test_as_parameter.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
c_short, c_int, c_long, c_longlong,
66
c_byte, c_wchar, c_float, c_double,
77
ArgumentError)
8-
from test.support import import_helper, skip_if_sanitizer, skip_emscripten_stack_overflow
8+
from test.support import (import_helper, skip_if_sanitizer,
9+
skip_emscripten_stack_overflow, skip_if_huge_c_stack)
910
_ctypes_test = import_helper.import_module("_ctypes_test")
1011

1112

@@ -193,6 +194,7 @@ class S8I(Structure):
193194
(9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
194195

195196
@skip_if_sanitizer('requires deep stack', thread=True)
197+
@skip_if_huge_c_stack()
196198
@skip_emscripten_stack_overflow()
197199
def test_recursive_as_param(self):
198200
class A:

Lib/test/test_descr.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3774,6 +3774,7 @@ def f(a): return a
37743774
encoding='latin1', errors='replace')
37753775
self.assertEqual(ba, b'abc\xbd?')
37763776

3777+
@support.skip_if_huge_c_stack()
37773778
@support.skip_wasi_stack_overflow()
37783779
@support.skip_emscripten_stack_overflow()
37793780
def test_recursive_call(self):
@@ -5122,6 +5123,7 @@ class Thing:
51225123
# CALL_METHOD_DESCRIPTOR_O
51235124
deque.append(thing, thing)
51245125

5126+
@support.skip_if_huge_c_stack()
51255127
@support.skip_emscripten_stack_overflow()
51265128
@support.skip_wasi_stack_overflow()
51275129
def test_repr_as_str(self):

0 commit comments

Comments
 (0)