import _testlimitedcapi
except ImportError:
_testlimitedcapi = None
+try:
+ import _testinternalcapi
+except ImportError:
+ _testinternalcapi = None
import struct
import collections
import itertools
@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, (), ())
+ self.assertLess(lower_sp, this_sp)
+ # Add an (arbitrary) extra 20% for safety
+ safe_margin = (this_sp - lower_sp) * 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)
return PyLong_FromLong(remaining);
}
+static PyObject*
+get_stack_pointer(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ uintptr_t here_addr = _Py_get_machine_stack_pointer();
+ return PyLong_FromSize_t(here_addr);
+}
+
+static PyObject*
+get_stack_margin(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ return PyLong_FromSize_t(_PyOS_STACK_MARGIN_BYTES);
+}
static PyObject*
test_bswap(PyObject *self, PyObject *Py_UNUSED(args))
{"get_configs", get_configs, METH_NOARGS},
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
{"get_c_recursion_remaining", get_c_recursion_remaining, METH_NOARGS},
+ {"get_stack_pointer", get_stack_pointer, METH_NOARGS},
+ {"get_stack_margin", get_stack_margin, METH_NOARGS},
{"test_bswap", test_bswap, METH_NOARGS},
{"test_popcount", test_popcount, METH_NOARGS},
{"test_bit_length", test_bit_length, METH_NOARGS},