diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 7ea279291..b18f16a6e 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1,6 +1,9 @@ import unittest from test.support import (cpython_only, is_wasi, requires_limited_api, Py_DEBUG, - set_recursion_limit, skip_on_s390x, import_helper) + set_recursion_limit, skip_on_s390x, + skip_emscripten_stack_overflow, + skip_wasi_stack_overflow, skip_if_sanitizer, + import_helper) try: import _testcapi except ImportError: @@ -9,12 +12,15 @@ try: import _testlimitedcapi except ImportError: _testlimitedcapi = None +try: + import _testinternalcapi +except ImportError: + _testinternalcapi = None import struct import collections import itertools import gc import contextlib -import sys import types @@ -169,7 +175,7 @@ class CFunctionCallsErrorMessages(unittest.TestCase): print, 0, sep=1, end=2, file=3, flush=4, foo=5) def test_varargs18_kw(self): - # _PyArg_UnpackKeywordsWithVararg() + # _PyArg_UnpackKeywords() with varpos msg = r"invalid keyword argument for print\(\)$" with self.assertRaisesRegex(TypeError, msg): print(0, 1, **{BadStr('foo'): ','}) @@ -617,9 +623,6 @@ def testfunction_kw(self, *, kw): return self -ADAPTIVE_WARMUP_DELAY = 2 - - @unittest.skipIf(_testcapi is None, "requires _testcapi") class TestPEP590(unittest.TestCase): @@ -696,8 +699,8 @@ class TestPEP590(unittest.TestCase): UnaffectedType2 = _testcapi.make_vectorcall_class(SuperType) # Aside: Quickly check that the C helper actually made derived types - self.assertTrue(issubclass(UnaffectedType1, DerivedType)) - self.assertTrue(issubclass(UnaffectedType2, SuperType)) + self.assertIsSubclass(UnaffectedType1, DerivedType) + self.assertIsSubclass(UnaffectedType2, SuperType) # Initial state: tp_call self.assertEqual(instance(), "tp_call") @@ -803,17 +806,18 @@ class TestPEP590(unittest.TestCase): def test_setvectorcall(self): from _testcapi import function_setvectorcall + _testinternalcapi = import_helper.import_module("_testinternalcapi") def f(num): return num + 1 assert_equal = self.assertEqual num = 10 assert_equal(11, f(num)) function_setvectorcall(f) - # make sure specializer is triggered by running > 50 times - for _ in range(10 * ADAPTIVE_WARMUP_DELAY): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): assert_equal("overridden", f(num)) def test_setvectorcall_load_attr_specialization_skip(self): from _testcapi import function_setvectorcall + _testinternalcapi = import_helper.import_module("_testinternalcapi") class X: def __getattribute__(self, attr): @@ -825,11 +829,12 @@ class TestPEP590(unittest.TestCase): function_setvectorcall(X.__getattribute__) # make sure specialization doesn't trigger # when vectorcall is overridden - for _ in range(ADAPTIVE_WARMUP_DELAY): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): assert_equal("overridden", x.a) def test_setvectorcall_load_attr_specialization_deopt(self): from _testcapi import function_setvectorcall + _testinternalcapi = import_helper.import_module("_testinternalcapi") class X: def __getattribute__(self, attr): @@ -841,19 +846,24 @@ class TestPEP590(unittest.TestCase): assert_equal = self.assertEqual x = X() # trigger LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN specialization - for _ in range(ADAPTIVE_WARMUP_DELAY): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): assert_equal("a", get_a(x)) function_setvectorcall(X.__getattribute__) # make sure specialized LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN # gets deopted due to overridden vectorcall - for _ in range(ADAPTIVE_WARMUP_DELAY): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): assert_equal("overridden", get_a(x)) @requires_limited_api def test_vectorcall_limited_incoming(self): from _testcapi import pyobject_vectorcall - obj = _testlimitedcapi.LimitedVectorCallClass() - self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called") + for cls in (_testlimitedcapi.LimitedVectorCallClass, + _testlimitedcapi.LimitedRelativeVectorCallClass): + with self.subTest(cls=cls): + obj = cls() + self.assertEqual( + pyobject_vectorcall(obj, (), ()), + "vectorcall called") @requires_limited_api def test_vectorcall_limited_outgoing(self): @@ -1031,9 +1041,29 @@ class TestErrorMessagesSuggestions(unittest.TestCase): @cpython_only class TestRecursion(unittest.TestCase): + def test_margin_is_sufficient(self): + + def get_sp(): + return _testinternalcapi.get_stack_pointer() + + this_sp = _testinternalcapi.get_stack_pointer() + lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ()) + if _testcapi._Py_STACK_GROWS_DOWN: + self.assertLess(lower_sp, this_sp) + safe_margin = this_sp - lower_sp + else: + self.assertLess(this_sp, lower_sp) + safe_margin = lower_sp - this_sp + # Add an (arbitrary) extra 20% for safety + safe_margin *= 6 / 5 + self.assertLess(safe_margin, _testinternalcapi.get_stack_margin()) + @skip_on_s390x @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") + @skip_if_sanitizer("requires deep stack", thread=True) @unittest.skipIf(_testcapi is None, "requires _testcapi") + @skip_emscripten_stack_overflow() + @skip_wasi_stack_overflow() def test_super_deep(self): def recurse(n): @@ -1058,13 +1088,21 @@ class TestRecursion(unittest.TestCase): recurse(90_000) with self.assertRaises(RecursionError): recurse(101_000) - c_recurse(100) + c_recurse(50) with self.assertRaises(RecursionError): c_recurse(90_000) - c_py_recurse(90) + c_py_recurse(50) with self.assertRaises(RecursionError): c_py_recurse(100_000) + def test_recursion_with_kwargs(self): + # GH-137883: The interpreter forgot to check the recursion limit when + # calling with keywords. + def recurse_kw(a=0): + recurse_kw(a=0) + with self.assertRaises(RecursionError): + recurse_kw() + class TestFunctionWithManyArgs(unittest.TestCase): def test_function_with_many_args(self):