[pypy-commit] pypy default: merge cpyext-recursionlimit which implements Py_EnterRecursiveCall and friends

mattip pypy.commits at gmail.com
Sat May 20 15:42:01 EDT 2017


Author: Matti Picus <matti.picus at gmail.com>
Branch: 
Changeset: r91350:17fddf817604
Date: 2017-05-20 22:34 +0300
http://bitbucket.org/pypy/pypy/changeset/17fddf817604/

Log:	merge cpyext-recursionlimit which implements Py_EnterRecursiveCall
	and friends

diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py
--- a/pypy/module/cpyext/eval.py
+++ b/pypy/module/cpyext/eval.py
@@ -1,6 +1,8 @@
 from pypy.interpreter.error import oefmt
 from pypy.interpreter.astcompiler import consts
 from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.rarithmetic import widen
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP,
     cpython_struct, is_valid_fp)
@@ -227,4 +229,51 @@
     cf.c_cf_flags = rffi.cast(rffi.INT, flags)
     return result
 
+ at cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
+def Py_GetRecursionLimit(space):
+    from pypy.module.sys.vm import getrecursionlimit
+    return space.int_w(getrecursionlimit(space))
 
+ at cpython_api([rffi.INT_real], lltype.Void, error=CANNOT_FAIL)
+def Py_SetRecursionLimit(space, limit):
+    from pypy.module.sys.vm import setrecursionlimit
+    setrecursionlimit(space, widen(limit))
+
+limit = 0 # for testing
+
+ at cpython_api([rffi.CCHARP], rffi.INT_real, error=1)
+def Py_EnterRecursiveCall(space, where):
+    """Marks a point where a recursive C-level call is about to be performed.
+
+    If USE_STACKCHECK is defined, this function checks if the the OS
+    stack overflowed using PyOS_CheckStack().  In this is the case, it
+    sets a MemoryError and returns a nonzero value.
+
+    The function then checks if the recursion limit is reached.  If this is the
+    case, a RuntimeError is set and a nonzero value is returned.
+    Otherwise, zero is returned.
+
+    where should be a string such as " in instance check" to be
+    concatenated to the RuntimeError message caused by the recursion depth
+    limit."""
+    if not we_are_translated():
+        # XXX hack since the stack checks only work translated
+        global limit
+        limit += 1
+        if limit > 10:
+            raise oefmt(space.w_RuntimeError, 
+                 "maximum recursion depth exceeded%s", rffi.charp2str(where))
+        return 0
+    from rpython.rlib.rstack import stack_almost_full
+    if stack_almost_full():
+        raise oefmt(space.w_RuntimeError,
+                 "maximum recursion depth exceeded%s", rffi.charp2str(where))
+    return 0
+
+ at cpython_api([], lltype.Void)
+def Py_LeaveRecursiveCall(space):
+    """Ends a Py_EnterRecursiveCall().  Must be called once for each
+    successful invocation of Py_EnterRecursiveCall()."""
+    # A NOP in PyPy
+    if not we_are_translated():
+        limit = 0
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -490,29 +490,6 @@
     0 on success, -1 on failure."""
     raise NotImplementedError
 
- at cpython_api([rffi.CCHARP], rffi.INT_real, error=1)
-def Py_EnterRecursiveCall(space, where):
-    """Marks a point where a recursive C-level call is about to be performed.
-
-    If USE_STACKCHECK is defined, this function checks if the the OS
-    stack overflowed using PyOS_CheckStack().  In this is the case, it
-    sets a MemoryError and returns a nonzero value.
-
-    The function then checks if the recursion limit is reached.  If this is the
-    case, a RuntimeError is set and a nonzero value is returned.
-    Otherwise, zero is returned.
-
-    where should be a string such as " in instance check" to be
-    concatenated to the RuntimeError message caused by the recursion depth
-    limit."""
-    raise NotImplementedError
-
- at cpython_api([], lltype.Void)
-def Py_LeaveRecursiveCall(space):
-    """Ends a Py_EnterRecursiveCall().  Must be called once for each
-    successful invocation of Py_EnterRecursiveCall()."""
-    raise NotImplementedError
-
 @cpython_api([PyFileObject], lltype.Void)
 def PyFile_IncUseCount(space, p):
     """Increments the PyFileObject's internal use count to indicate
diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -331,3 +331,30 @@
                 def nested_flags():
                     return module.get_flags()""" in ns
         assert ns['nested_flags']() == (1, 0x2000)  # CO_FUTURE_DIVISION
+
+    def test_recursive_function(self):
+        module = self.import_extension('foo', [
+            ("call_recursive", "METH_NOARGS",
+             """
+                int res = 0;
+                int recurse(void) {
+                    if (Py_EnterRecursiveCall(" while calling recurse"))
+                        return -1;
+                    res ++;
+                    return recurse();
+                }
+                int oldlimit = Py_GetRecursionLimit();
+                Py_SetRecursionLimit(200);
+                res = recurse();
+                Py_SetRecursionLimit(oldlimit);
+                if (PyErr_Occurred())
+                    return NULL;
+                return PyLong_FromLong(res);
+             """),], prologue= ''' int recurse(void); '''
+            )
+        try:
+            res = module.call_recursive()
+        except RuntimeError as e:
+            assert 'while calling recurse' in str(e)
+        else:
+            assert False, "expected RuntimeError"  


More information about the pypy-commit mailing list