[pypy-svn] r26678 - in pypy/dist/pypy: objspace/cpy objspace/cpy/test rpython/rctypes rpython/rctypes/test

arigo at codespeak.net arigo at codespeak.net
Tue May 2 17:50:15 CEST 2006


Author: arigo
Date: Tue May  2 17:50:12 2006
New Revision: 26678

Modified:
   pypy/dist/pypy/objspace/cpy/ann_policy.py
   pypy/dist/pypy/objspace/cpy/capi.py
   pypy/dist/pypy/objspace/cpy/ctypes_base.py
   pypy/dist/pypy/objspace/cpy/objspace.py
   pypy/dist/pypy/objspace/cpy/test/test_compile.py
   pypy/dist/pypy/objspace/cpy/wrappable.py
   pypy/dist/pypy/rpython/rctypes/afunc.py
   pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py
Log:
Support exceptions in the CPyObjSpace, both when running on top of
CPython and when translated.  When translated, an extra call to
rctypes_pyerrchecker() is inserted, which does the PyErr_Occurred() and
PyErr_Fetch() magic to turn it into an OperationError.  With some hacks
the existing trampoline() function is able to do the converse: turn an
OperationError into the CPython exception it contains.



Modified: pypy/dist/pypy/objspace/cpy/ann_policy.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/ann_policy.py	(original)
+++ pypy/dist/pypy/objspace/cpy/ann_policy.py	Tue May  2 17:50:12 2006
@@ -2,7 +2,7 @@
 from pypy.annotation.pairtype import pair
 from pypy.annotation import model as annmodel
 from pypy.interpreter.error import OperationError
-from pypy.objspace.cpy.ctypes_base import W_Object
+from pypy.objspace.cpy.ctypes_base import W_Object, rctypes_pyerrchecker
 
 class CPyAnnotatorPolicy(PyPyAnnotatorPolicy):
     """Annotation policy to compile CPython extension modules with
@@ -30,8 +30,15 @@
             # could have found new objects
 
         # force w_type, w_value attributes into the OperationError class
-        classdef = annotator.bookkeeper.getuniqueclassdef(OperationError)
+        bk = annotator.bookkeeper
+        classdef = bk.getuniqueclassdef(OperationError)
         s_instance = annmodel.SomeInstance(classdef=classdef)
         for name in ['w_type', 'w_value']:
-            s_instance.setattr(annotator.bookkeeper.immutablevalue(name),
-                               annotator.bookkeeper.valueoftype(W_Object))
+            s_instance.setattr(bk.immutablevalue(name),
+                               bk.valueoftype(W_Object))
+
+        # annotate rctypes_pyerrchecker()
+        uniquekey = rctypes_pyerrchecker
+        s_pyerrchecker = bk.immutablevalue(rctypes_pyerrchecker)
+        s_result = bk.emulate_pbc_call(uniquekey, s_pyerrchecker, [])
+        assert annmodel.s_None.contains(s_result)

Modified: pypy/dist/pypy/objspace/cpy/capi.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/capi.py	(original)
+++ pypy/dist/pypy/objspace/cpy/capi.py	Tue May  2 17:50:12 2006
@@ -86,6 +86,14 @@
 PyIter_Next.argtypes = [W_Object]
 PyIter_Next.restype = W_Object
 
+PyObject_IsTrue = cpyapi.PyObject_IsTrue
+PyObject_IsTrue.argtypes = [W_Object]
+PyObject_IsTrue.restype = c_int
+
+PyObject_Type = cpyapi.PyObject_Type
+PyObject_Type.argtypes = [W_Object]
+PyObject_Type.restype = W_Object
+
 
 ###########################################################
 # ____________________ Number Protocol ____________________
@@ -111,6 +119,14 @@
 PySequence_SetItem.restype = c_int
 
 
+########################################################
+# ____________________ Type Objects ____________________
+
+PyType_IsSubtype = cpyapi.PyType_IsSubtype
+PyType_IsSubtype.argtypes = [W_Object, W_Object]
+PyType_IsSubtype.restype = c_int
+
+
 ###########################################################
 # ____________________ Numeric Objects ____________________
 
@@ -178,13 +194,6 @@
 PyImport_ImportModule.argtypes = [c_char_p]
 PyImport_ImportModule.restype = W_Object
 
-# "RAW" because it comes from pythonapi instead of cpyapi
-# which makes it raise the set exception directly instead
-# of wrapping it into an OperationError
-RAW_PyErr_SetObject = pythonapi.PyErr_SetObject
-RAW_PyErr_SetObject.argtypes = [W_Object, W_Object]
-RAW_PyErr_SetObject.restype = None
-
 
 ##############################################################
 # ____________________ Built-in functions ____________________
@@ -201,3 +210,27 @@
 ##PyCFunction_NewEx = cpyapi.PyCFunction_NewEx
 ##PyCFunction_NewEx.argtypes = [POINTER(PyMethodDef), W_Object, W_Object]
 ##PyCFunction_NewEx.restype = W_Object
+
+
+##############################################################
+# ____________________ Exception handling ____________________
+
+# "RAW" because it comes from pythonapi instead of cpyapi.
+# The normal error handling (wrapping CPython exceptions into
+# an OperationError) is disabled.
+RAW_PyErr_SetObject = pythonapi.PyErr_SetObject
+RAW_PyErr_SetObject.argtypes = [W_Object, W_Object]
+RAW_PyErr_SetObject.restype = None
+RAW_PyErr_SetObject._rctypes_pyerrchecker_ = None
+
+RAW_PyErr_Occurred = pythonapi.PyErr_Occurred
+RAW_PyErr_Occurred.argtypes = []
+RAW_PyErr_Occurred.restype = c_int
+RAW_PyErr_Occurred._rctypes_pyerrchecker_ = None
+
+RAW_PyErr_Fetch = pythonapi.PyErr_Fetch
+RAW_PyErr_Fetch.argtypes = [POINTER(W_Object),
+                            POINTER(W_Object),
+                            POINTER(W_Object)]
+RAW_PyErr_Fetch.restype = None
+RAW_PyErr_Fetch._rctypes_pyerrchecker_ = None

Modified: pypy/dist/pypy/objspace/cpy/ctypes_base.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/ctypes_base.py	(original)
+++ pypy/dist/pypy/objspace/cpy/ctypes_base.py	Tue May  2 17:50:12 2006
@@ -26,6 +26,15 @@
 class LevelError(Exception):
     pass
 
+def rctypes_pyerrchecker():
+    from pypy.objspace.cpy.capi import RAW_PyErr_Occurred, RAW_PyErr_Fetch
+    if RAW_PyErr_Occurred():
+        w_exc = W_Object()
+        w_val = W_Object()
+        w_tb  = W_Object()
+        RAW_PyErr_Fetch(byref(w_exc), byref(w_val), byref(w_tb))
+        raise OperationError(w_exc, w_val)    # XXX traceback
+
 class CPyAPI(PyDLL):
     """Class of the singleton 'cpyapi' object, out of which C functions
     are getattr'd.  It returns C function whose exception behavior matches
@@ -34,6 +43,7 @@
     """
     class _FuncPtr(PyDLL._FuncPtr):
         _flags_ = PyDLL._FuncPtr._flags_
+        _rctypes_pyerrchecker_ = staticmethod(rctypes_pyerrchecker)
 
         def __call__(*args, **kwds):
             try:
@@ -43,8 +53,7 @@
             except:
                 exc, val, tb = sys.exc_info()
                 raise OperationError(W_Object(exc),
-                                     W_Object(val),
-                                     W_Object(tb))
+                                     W_Object(val))   # XXX traceback
 
 cpyapi = CPyAPI.__new__(CPyAPI)
 cpyapi.__dict__ = pythonapi.__dict__.copy()

Modified: pypy/dist/pypy/objspace/cpy/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/objspace.py	(original)
+++ pypy/dist/pypy/objspace/cpy/objspace.py	Tue May  2 17:50:12 2006
@@ -10,6 +10,7 @@
     def initialize(self):
         self.options.geninterp = True
         self.w_int   = W_Object(int)
+        self.w_tuple = W_Object(tuple)
         self.w_None  = W_Object(None)
         self.w_False = W_Object(False)
         self.w_True  = W_Object(True)
@@ -66,6 +67,7 @@
     int_w   = staticmethod(PyInt_AsLong)
     str_w   = staticmethod(PyString_AsString)
     iter    = staticmethod(PyObject_GetIter)
+    type    = staticmethod(PyObject_Type)
 
     add     = staticmethod(PyNumber_Add)
     sub     = staticmethod(PyNumber_Subtract)
@@ -135,3 +137,12 @@
         if not w_res:
             raise OperationError(self.w_StopIteration, self.w_None)
         return w_res
+
+    def is_true(self, w_obj):
+        return PyObject_IsTrue(w_obj) != 0
+
+    def issubtype(self, w_type1, w_type2):
+        if PyType_IsSubtype(w_type1, w_type2):
+            return self.w_True
+        else:
+            return self.w_False

Modified: pypy/dist/pypy/objspace/cpy/test/test_compile.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/test/test_compile.py	(original)
+++ pypy/dist/pypy/objspace/cpy/test/test_compile.py	Tue May  2 17:50:12 2006
@@ -5,6 +5,7 @@
 from pypy.objspace.cpy.ann_policy import CPyAnnotatorPolicy
 from pypy.objspace.cpy.objspace import CPyObjSpace
 import pypy.rpython.rctypes.implementation
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.function import BuiltinFunction
 from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
 from pypy import conftest
@@ -104,3 +105,65 @@
                  annotatorpolicy = CPyAnnotatorPolicy(space))
     res = fn(10, complex)
     assert isinstance(res, int)
+
+# ____________________________________________________________
+
+def test_compile_exception_to_rpython():
+    space = CPyObjSpace()
+    def entrypoint(n):
+        w_sys = space.getbuiltinmodule('sys')
+        w_n = space.wrap(n)
+        try:
+            space.call_method(w_sys, 'exit', w_n)
+        except OperationError, e:
+            # a bit fragile: this test assumes that the OperationError
+            # is *not* normalized
+            return space.newtuple([e.w_type, e.w_value])
+        else:
+            return space.wrap('did not raise??')
+
+    fn = compile(entrypoint, [int],
+                 annotatorpolicy = CPyAnnotatorPolicy(space))
+    result = fn(5)
+    assert result == (SystemExit, 5)
+
+def test_compile_exception_from_rpython():
+    space = CPyObjSpace()
+    w_SystemExit = space.W_Object(SystemExit)
+    def myfunc(n):
+        if n > 0:
+            raise OperationError(w_SystemExit, space.wrap(n))
+        else:
+            return space.wrap(n)
+    myfunc.unwrap_spec = [int]
+    w_myfunc = space.wrap(interp2app(myfunc))
+
+    def entrypoint():
+        return w_myfunc
+
+    fn = compile(entrypoint, [],
+                 annotatorpolicy = CPyAnnotatorPolicy(space))
+    myfunc1 = fn()
+    e = py.test.raises(SystemExit, myfunc1, 5)
+    ex = e.value
+    assert ex.args == (5,)
+
+def test_compile_exception_through_rpython():
+    space = CPyObjSpace()
+    def myfunc(n):
+        w_sys = space.getbuiltinmodule('sys')
+        w_n = space.wrap(n)
+        space.call_method(w_sys, 'exit', w_n)
+        return space.w_None   # should not actually reach this point
+    myfunc.unwrap_spec = [int]
+    w_myfunc = space.wrap(interp2app(myfunc))
+
+    def entrypoint():
+        return w_myfunc
+
+    fn = compile(entrypoint, [],
+                 annotatorpolicy = CPyAnnotatorPolicy(space))
+    myfunc1 = fn()
+    e = py.test.raises(SystemExit, myfunc1, 5)
+    ex = e.value
+    assert ex.args == (5,)

Modified: pypy/dist/pypy/objspace/cpy/wrappable.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/wrappable.py	(original)
+++ pypy/dist/pypy/objspace/cpy/wrappable.py	Tue May  2 17:50:12 2006
@@ -78,9 +78,11 @@
         sourcelines.append('    except ___OperationError, e:')
         sourcelines.append('        ___PyErr_SetObject(e.w_type.value,')
         sourcelines.append('                           e.w_value.value)')
-        sourcelines.append('        return None')  # should never be seen
-        sourcelines.append('    #except ___Exception, e:')
-        sourcelines.append('    #    raise ___RPythonError(XXX)')
+        # the following line is not reached, unless we are translated
+        # in which case it makes the function return (PyObject*)NULL.
+        sourcelines.append('        w_result = ___W_Object()')
+        #sourcelines.append('    except ___Exception, e:')
+        #sourcelines.append('        raise ___RPythonError(XXX)')
         sourcelines.append('    return w_result.value')
         sourcelines.append('')
 

Modified: pypy/dist/pypy/rpython/rctypes/afunc.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/afunc.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/afunc.py	Tue May  2 17:50:12 2006
@@ -114,7 +114,12 @@
         kwds = {}
         if hasattr(cfuncptr, 'llinterp_friendly_version'):
             kwds['_callable'] = cfuncptr.llinterp_friendly_version
+        suppress_pyerr_occurred = False
         if (cfuncptr._flags_ & ctypes._FUNCFLAG_PYTHONAPI) == 0:
+            suppress_pyerr_occurred = True
+        if hasattr(cfuncptr, '_rctypes_pyerrchecker_'):
+            suppress_pyerr_occurred = True
+        if suppress_pyerr_occurred:
             kwds['includes'] = getattr(cfuncptr, 'includes', ())
         #else:
         #   no 'includes': hack to trigger in GenC a PyErr_Occurred() check
@@ -132,6 +137,19 @@
         last_op.args[0].value._set_T(FUNCTYPE)
         last_op.args[0].value._obj._TYPE = FUNCTYPE
 
+        if getattr(cfuncptr, '_rctypes_pyerrchecker_', None):
+            # special extension to support the CPyObjSpace
+            # XXX hackish: someone else -- like the annotator policy --
+            # must ensure that this extra function has been annotated
+            from pypy.translator.translator import graphof
+            func = cfuncptr._rctypes_pyerrchecker_
+            graph = graphof(hop.rtyper.annotator.translator, func)
+            hop.llops.record_extra_call(graph)
+            # build the 'direct_call' operation
+            f = hop.rtyper.getcallable(graph)
+            c = hop.inputconst(lltype.typeOf(f), f)
+            hop.genop('direct_call', [c])
+
         if RESTYPE is lltype.Void:
             return None
         else:

Modified: pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py	Tue May  2 17:50:12 2006
@@ -323,6 +323,36 @@
         assert res == 34
         py.test.raises(OverflowError, 'fn(sys.maxint, 1)')
 
+    def test_compile_pyerrchecker(self):
+        from pypy.rpython.rctypes import apyobject
+        class W_Object(py_object):
+            pass
+        apyobject.register_py_object_subclass(W_Object)
+
+        def mypyerrchecker():
+            # for this test, always raises
+            raise ZeroDivisionError
+
+        PyNumber_Add = pythonapi.PyNumber_Add
+        PyNumber_Add.argtypes = [W_Object, W_Object]
+        PyNumber_Add.restype = W_Object
+        assert PyNumber_Add._flags_ & _FUNCFLAG_PYTHONAPI
+        PyNumber_Add._rctypes_pyerrchecker_ = mypyerrchecker
+        # special extension ^^^ to support the CPyObjSpace
+        try:
+            def fn1(n):
+                if n < 0:
+                    # for this test, force mypyerrchecker() to be annotated
+                    # using this trick
+                    mypyerrchecker()
+                pyobj = W_Object(n)
+                return PyNumber_Add(pyobj, pyobj)
+
+            fn = compile(fn1, [int])
+            py.test.raises(ZeroDivisionError, fn, 64)
+        finally:
+            del PyNumber_Add._rctypes_pyerrchecker_
+
     def test_compile_ctime(self):
         import time
         N = 123456789



More information about the Pypy-commit mailing list