[pypy-svn] r72748 - in pypy/branch/cpython-extension/pypy/module/cpyext: . include test

afa at codespeak.net afa at codespeak.net
Wed Mar 24 20:11:05 CET 2010


Author: afa
Date: Wed Mar 24 20:11:03 2010
New Revision: 72748

Modified:
   pypy/branch/cpython-extension/pypy/module/cpyext/api.py
   pypy/branch/cpython-extension/pypy/module/cpyext/boolobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/dictobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/include/pyerrors.h
   pypy/branch/cpython-extension/pypy/module/cpyext/methodobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py
   pypy/branch/cpython-extension/pypy/module/cpyext/object.py
   pypy/branch/cpython-extension/pypy/module/cpyext/pyerrors.py
   pypy/branch/cpython-extension/pypy/module/cpyext/pythonrun.py
   pypy/branch/cpython-extension/pypy/module/cpyext/stringobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
   pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py
Log:
Change exception handling in cpyext:
- ensure that every C function properly declares its error value
- check consistency between the return value and the exception stored in the threadstate
- remove from_ref_ex(), not needed any more.


Modified: pypy/branch/cpython-extension/pypy/module/cpyext/api.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/api.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/api.py	Wed Mar 24 20:11:03 2010
@@ -44,6 +44,7 @@
 globals().update(rffi_platform.configure(CConfig_constants))
 
 _NOT_SPECIFIED = object()
+CANNOT_FAIL = object()
 
 class ApiFunction:
     def __init__(self, argtypes, restype, callable, borrowed, error):
@@ -64,12 +65,18 @@
         assert len(self.argnames) == len(self.argtypes)
 
 def cpython_api(argtypes, restype, borrowed=False, error=_NOT_SPECIFIED):
-    if restype is PyObject and error is _NOT_SPECIFIED:
-        error = None
+    if error is _NOT_SPECIFIED:
+        if restype is PyObject:
+            error = lltype.nullptr(PyObject.TO)
+        elif restype is lltype.Void:
+            error = CANNOT_FAIL
 
     def decorate(func):
         api_function = ApiFunction(argtypes, restype, func, borrowed, error)
 
+        if error is _NOT_SPECIFIED:
+            raise ValueError("function %s has no return value for exceptions"
+                             % func)
         def unwrapper(space, *args):
             "NOT_RPYTHON: XXX unsure"
             newargs = []
@@ -171,7 +178,6 @@
 def make_ref(space, w_obj, borrowed=False):
     if w_obj is None:
         return lltype.nullptr(PyObject.TO)
-        #raise NullPointerException("Trying to pass a NULL reference")
     assert isinstance(w_obj, W_Root)
     state = space.fromcache(State)
     py_obj = state.py_objects_w2r.get(w_obj)
@@ -209,9 +215,9 @@
     return py_obj
 
 def from_ref(space, ref):
-    state = space.fromcache(State)
     if not ref:
-        raise NullPointerException("Null pointer dereference!")
+        return None
+    state = space.fromcache(State)
     ptr = ctypes.addressof(ref._obj._storage)
     try:
         obj = state.py_objects_r2w[ptr]
@@ -251,24 +257,25 @@
             retval = callable(space, *boxed_args)
             print >>sys.stderr, " DONE"
         except OperationError, e:
+            failed = True
             e.normalize_exception(space)
             state.exc_type = e.w_type
             state.exc_value = e.get_w_value(space)
         except BaseException, e:
+            failed = True
             state.exc_type = space.w_SystemError
             state.exc_value = space.wrap(str(e))
             import traceback
             traceback.print_exc()
+        else:
+            failed = False
 
-        if state.exc_value is not None:
-            restype = callable.api_func.restype
-            if restype is lltype.Void:
-                return
-            if restype is PyObject:
-                return lltype.nullptr(PyObject.TO)
-            if restype in (Py_ssize_t, rffi.INT_real):
-                return rffi.cast(restype, -1)
-            assert False, "Unknown return type"
+        if failed:
+            error_value = callable.api_func.error_value
+            if error_value is CANNOT_FAIL:
+                raise SystemError("The function %r was not supposed to fail"
+                                  % (callable,))
+            return error_value
 
         if callable.api_func.restype is PyObject:
             retval = make_ref(space, retval, borrowed=callable.api_func.borrowed)
@@ -413,6 +420,8 @@
 
 def generic_cpy_call(space, func, *args, **kwargs):
     from pypy.module.cpyext.macros import Py_DECREF
+    from pypy.module.cpyext.pyerrors import PyErr_Occurred
+
     decref_args = kwargs.pop("decref_args", True)
     assert not kwargs
     boxed_args = []
@@ -424,9 +433,24 @@
     result = func(*boxed_args)
     try:
         FT = lltype.typeOf(func).TO
-        if FT.RESULT is not lltype.Void:
-            ret = from_ref_ex(space, result)
-            Py_DECREF(space, ret)
+        if FT.RESULT is PyObject:
+            ret = from_ref(space, result)
+
+            # Check for exception consistency
+            has_error = PyErr_Occurred(space) is not None
+            has_result = ret is not None
+            if has_error and has_result:
+                raise OperationError(space.w_SystemError, space.wrap(
+                    "An exception was set, but function returned a value"))
+            elif not has_error and not has_result:
+                raise OperationError(space.w_SystemError, space.wrap(
+                    "Function returned a NULL result without setting an exception"))
+
+            if has_error:
+                state = space.fromcache(State)
+                state.check_and_raise_exception()
+
+            Py_DECREF(space, ret) # XXX WHY??
             return ret
     finally:
         if decref_args:
@@ -434,19 +458,3 @@
                 if arg is not None and isinstance(arg, W_Root):
                     Py_DECREF(space, arg)
 
-def from_ref_ex(space, result):
-    try:
-        ret = from_ref(space, result)
-    except NullPointerException:
-        state = space.fromcache(State)
-        state.check_and_raise_exception()
-        assert False, "NULL returned but no exception set"
-    except InvalidPointerException:
-        if not we_are_translated():
-            import sys
-            print >>sys.stderr, "Calling a C function return an invalid PyObject" \
-                    " pointer."
-        raise
-    return ret
-
-

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/boolobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/boolobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/boolobject.py	Wed Mar 24 20:11:03 2010
@@ -1,7 +1,8 @@
 from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import cpython_api, PyObject, general_check
+from pypy.module.cpyext.api import cpython_api, PyObject, CANNOT_FAIL
+from pypy.module.cpyext.api import general_check
 
- at cpython_api([PyObject], rffi.INT_real)
+ at cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyBool_Check(space, w_obj):
     w_type = space.w_bool
     return general_check(space, w_obj, w_type)

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/dictobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/dictobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/dictobject.py	Wed Mar 24 20:11:03 2010
@@ -1,19 +1,19 @@
 from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import cpython_api, PyObject
+from pypy.module.cpyext.api import cpython_api, PyObject, CANNOT_FAIL
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 
 @cpython_api([], PyObject)
 def PyDict_New(space):
     return space.newdict()
 
- at cpython_api([PyObject], rffi.INT_real)
+ at cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyDict_Check(space, w_obj):
     w_type = space.w_dict
     w_obj_type = space.type(w_obj)
     return space.is_w(w_obj_type, w_type) or space.is_true(space.issubtype(w_obj_type, w_type))
 
 
- at cpython_api([PyObject, rffi.CCHARP, PyObject], rffi.INT_real)
+ at cpython_api([PyObject, rffi.CCHARP, PyObject], rffi.INT_real, error=-1)
 def PyDict_SetItemString(space, w_dict, key_ptr, w_obj):
     if PyDict_Check(space, w_dict):
         key = rffi.charp2str(key_ptr)

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/include/pyerrors.h
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/include/pyerrors.h	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/include/pyerrors.h	Wed Mar 24 20:11:03 2010
@@ -10,7 +10,7 @@
 PyAPI_DATA(PyObject *) PyExc_Exception;
 void PyErr_SetString(PyObject *, char *);
 PyObject * PyErr_Occurred(void);
-void PyErr_Clear(PyObject *, char *);
+void PyErr_Clear();
 
 #ifdef __cplusplus
 }

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/methodobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/methodobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/methodobject.py	Wed Mar 24 20:11:03 2010
@@ -4,11 +4,13 @@
 from pypy.interpreter.argument import Arguments
 from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
 from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.function import BuiltinFunction, Method
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import PyObject, from_ref, \
-        make_ref, generic_cpy_call, from_ref_ex
+        make_ref, generic_cpy_call
 from pypy.module.cpyext.state import State
+from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.rlib.objectmodel import we_are_translated
 
 

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py	Wed Mar 24 20:11:03 2010
@@ -1,6 +1,6 @@
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, cpython_struct, PyObject, \
-        METH_STATIC, METH_CLASS, METH_COEXIST, general_check
+        METH_STATIC, METH_CLASS, METH_COEXIST, general_check, CANNOT_FAIL
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import PyCFunction_NewEx, PyDescr_NewMethod
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
@@ -69,7 +69,7 @@
             dict_w[methodname] = w_obj
 
 
- at cpython_api([PyObject], rffi.INT_real)
+ at cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyModule_Check(space, w_obj):
     w_type = space.gettypeobject(Module.typedef)
     return general_check(space, w_obj, w_type)

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/object.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/object.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/object.py	Wed Mar 24 20:11:03 2010
@@ -1,6 +1,6 @@
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, PyObject, make_ref, from_ref, \
-        generic_cpy_call
+        generic_cpy_call, CANNOT_FAIL
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.macros import Py_INCREF, Py_DECREF
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr, W_PyCTypeObject, W_PyCObject
@@ -34,12 +34,12 @@
 def PyObject_Not(space, w_obj):
     return not space.is_true(w_obj)
 
- at cpython_api([PyObject, PyObject], rffi.INT_real)
+ at cpython_api([PyObject, PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyObject_HasAttr(space, w_obj, w_name):
     w_res = operation.hasattr(space, w_obj, w_name)
     return space.is_true(w_res)
 
- at cpython_api([PyObject, PyObject, PyObject], rffi.INT_real)
+ at cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
 def PyObject_SetAttr(space, w_obj, w_name, w_value):
     operation.setattr(space, w_obj, w_name, w_value)
     return 0

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/pyerrors.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/pyerrors.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/pyerrors.py	Wed Mar 24 20:11:03 2010
@@ -6,8 +6,9 @@
 @cpython_api([PyObject, rffi.CCHARP], lltype.Void)
 def PyErr_SetString(space, w_type, message_ptr):
     message = rffi.charp2str(message_ptr)
-    w_obj = space.call_function(w_type, space.wrap(message))
-    raise OperationError(w_type, w_obj)
+    state = space.fromcache(State)
+    state.exc_type = w_type
+    state.exc_value = space.call_function(w_type, space.wrap(message))
 
 @cpython_api([], PyObject)
 def PyErr_Occurred(space):

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/pythonrun.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/pythonrun.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/pythonrun.py	Wed Mar 24 20:11:03 2010
@@ -1,6 +1,6 @@
 from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import cpython_api
+from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL
 
- at cpython_api([], rffi.INT_real)
+ at cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
 def Py_IsInitialized(space):
     return 1

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/stringobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/stringobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/stringobject.py	Wed Mar 24 20:11:03 2010
@@ -11,6 +11,6 @@
     s = rffi.charp2str(char_p)
     return space.wrap(s)
 
- at cpython_api([PyObject], Py_ssize_t)
+ at cpython_api([PyObject], Py_ssize_t, error=-1)
 def PyString_Size(space, w_obj):
     return space.int_w(space.len(w_obj))

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	Wed Mar 24 20:11:03 2010
@@ -17,7 +17,7 @@
 def PyPy_Crash1(space):
     1/0
 
- at api.cpython_api([], lltype.Signed)
+ at api.cpython_api([], lltype.Signed, error=-1)
 def PyPy_Crash2(space):
     1/0
 
@@ -366,7 +366,6 @@
 
 
     def test_internal_exceptions(self):
-        skip("Useful to see how programming errors look like")
         import sys
         init = """
         if (Py_IsInitialized())
@@ -384,14 +383,41 @@
                 return NULL;
             return PyFloat_FromDouble(a);
         }
+        static PyObject* foo_crash3(PyObject* self, PyObject *args)
+        {
+            int a = PyPy_Crash2();
+            if (a == -1)
+                PyErr_Clear();
+            return PyFloat_FromDouble(a);
+        }
+        static PyObject* foo_crash4(PyObject* self, PyObject *args)
+        {
+            int a = PyPy_Crash2();
+            return PyFloat_FromDouble(a);
+        }
+        static PyObject* foo_clear(PyObject* self, PyObject *args)
+        {
+            PyErr_Clear();
+            return NULL;
+        }
         static PyMethodDef methods[] = {
             { "crash1", foo_crash1, METH_NOARGS },
             { "crash2", foo_crash2, METH_NOARGS },
+            { "crash3", foo_crash3, METH_NOARGS },
+            { "crash4", foo_crash4, METH_NOARGS },
+            { "clear",  foo_clear, METH_NOARGS },
             { NULL }
         };
         """
         module = self.import_module(name='foo', init=init, body=body)
-        module.crash1()
-        module.crash2()
+        # uncaught interplevel exceptions are turned into SystemError
+        raises(SystemError, module.crash1)
+        raises(SystemError, module.crash2)
+        # caught exception
+        assert module.crash3() == -1
+        # An exception was set, but function returned a value
+        raises(SystemError, module.crash4)
+        # No exception set, but NULL returned
+        raises(SystemError, module.clear)
 
 

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py	Wed Mar 24 20:11:03 2010
@@ -8,7 +8,7 @@
 def PyTuple_New(space, size):
     return space.newtuple([space.w_None] * size)
 
- at cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real)
+ at cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
 def PyTuple_SetItem(space, w_t, pos, w_obj):
     assert isinstance(w_t, W_TupleObject)
     w_t.wrappeditems[pos] = w_obj

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py	Wed Mar 24 20:11:03 2010
@@ -16,7 +16,7 @@
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.modsupport import PyMethodDef, convert_method_defs
 from pypy.module.cpyext.state import State
-from pypy.module.cpyext.methodobject import from_ref_ex, generic_cpy_call
+from pypy.module.cpyext.methodobject import generic_cpy_call
 
 
 PyTypeObject = lltype.ForwardReference()
@@ -238,7 +238,7 @@
 def PyType_Ready(space, pto):
     "Implemented in typeobject.c"
 
- at cpython_api([PyTypeObjectPtr], rffi.INT_real)
+ at cpython_api([PyTypeObjectPtr], rffi.INT_real, error=-1)
 def PyPyType_Register(space, pto):
     state = space.fromcache(State)
     ptr = ctypes.addressof(pto._obj._storage)



More information about the Pypy-commit mailing list