[pypy-svn] r26600 - in pypy/dist/pypy: interpreter objspace/cpy objspace/cpy/test

arigo at codespeak.net arigo at codespeak.net
Sun Apr 30 16:00:26 CEST 2006


Author: arigo
Date: Sun Apr 30 16:00:22 2006
New Revision: 26600

Added:
   pypy/dist/pypy/objspace/cpy/ctypes_base.py   (contents, props changed)
Modified:
   pypy/dist/pypy/interpreter/error.py
   pypy/dist/pypy/objspace/cpy/ann_policy.py
   pypy/dist/pypy/objspace/cpy/capi.py
   pypy/dist/pypy/objspace/cpy/objspace.py
   pypy/dist/pypy/objspace/cpy/test/test_objspace.py
   pypy/dist/pypy/objspace/cpy/test/test_wrappable.py
   pypy/dist/pypy/objspace/cpy/wrappable.py
Log:
Start supporting exceptions in the CPyObjSpace: the idea is
that in RPython there are only a few exception classes, which
represent internal errors and shouldn't escape, apart from
the OperationError, which represents an app-level exception.

    - when calling C API functions, wrap any exception into
      an OperationError back for the RPython caller;

    - when wrapping an RPython function to expose it to the
      C API, unwrap OperationErrors generated by RPython.

Some more rctypes support seems to be needed for the first point.



Modified: pypy/dist/pypy/interpreter/error.py
==============================================================================
--- pypy/dist/pypy/interpreter/error.py	(original)
+++ pypy/dist/pypy/interpreter/error.py	Sun Apr 30 16:00:22 2006
@@ -11,8 +11,8 @@
 
     OperationError instances have three public attributes (and no .args),
     w_type, w_value and application_traceback, which contain the wrapped
-    type and value describing the exception, and the unwrapped list of
-    (frame, instruction_position) making the application-level traceback.
+    type and value describing the exception, and a chained list of
+    PyTraceback objects making the application-level traceback.
     """
 
     def __init__(self, w_type, w_value, tb=None):

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	Sun Apr 30 16:00:22 2006
@@ -1,5 +1,8 @@
 from pypy.translator.goal.ann_override import PyPyAnnotatorPolicy
 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
 
 class CPyAnnotatorPolicy(PyPyAnnotatorPolicy):
     """Annotation policy to compile CPython extension modules with
@@ -8,7 +11,6 @@
 
     def __init__(self, space):
         PyPyAnnotatorPolicy.__init__(self, single_space=space)
-        self.spaces = {}
 
     def no_more_blocks_to_annotate(self, annotator):
         PyPyAnnotatorPolicy.no_more_blocks_to_annotate(self, annotator)
@@ -26,3 +28,10 @@
                                                     w_obj)
             # restart this loop: for all we know follow_annotations()
             # could have found new objects
+
+        # force w_type, w_value attributes into the OperationError class
+        classdef = annotator.bookkeeper.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))

Modified: pypy/dist/pypy/objspace/cpy/capi.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/capi.py	(original)
+++ pypy/dist/pypy/objspace/cpy/capi.py	Sun Apr 30 16:00:22 2006
@@ -1,16 +1,12 @@
+"""
+CTypes declarations for the CPython API.
+"""
 import sys
 import ctypes
 from ctypes import *
 from pypy.rpython.rctypes.tool import ctypes_platform
-from pypy.rpython.rctypes import apyobject
 ##from pypy.rpython.rctypes.implementation import CALLBACK_FUNCTYPE
-
-class W_Object(py_object):
-    "A py_object subclass, representing wrapped objects for the CPyObjSpace."
-    def __repr__(self):
-        return 'W_Object(%r)' % (self.value,)
-
-apyobject.register_py_object_subclass(W_Object)
+from pypy.objspace.cpy.ctypes_base import W_Object, cpyapi
 
 
 ###############################################################
@@ -54,51 +50,63 @@
 ###########################################################
 # ____________________ Object Protocol ____________________
 
-PyObject_GetAttr = pythonapi.PyObject_GetAttr
+PyObject_GetAttr = cpyapi.PyObject_GetAttr
 PyObject_GetAttr.argtypes = [W_Object, W_Object]
 PyObject_GetAttr.restype = W_Object
 
-PyObject_GetItem = pythonapi.PyObject_GetItem
+PyObject_GetItem = cpyapi.PyObject_GetItem
 PyObject_GetItem.argtypes = [W_Object, W_Object]
 PyObject_GetItem.restype = W_Object
 
-PyObject_SetItem = pythonapi.PyObject_SetItem
+PyObject_SetItem = cpyapi.PyObject_SetItem
 PyObject_SetItem.argtypes = [W_Object, W_Object, W_Object]
 PyObject_SetItem.restype = c_int
 
-PyObject_Call = pythonapi.PyObject_Call
+PyObject_Call = cpyapi.PyObject_Call
 PyObject_Call.argtypes = [W_Object, W_Object, W_Object]
 PyObject_Call.restype = W_Object
 
-PyObject_CallFunctionObjArgs = pythonapi.PyObject_CallFunctionObjArgs
+PyObject_CallFunctionObjArgs = cpyapi.PyObject_CallFunctionObjArgs
 PyObject_CallFunctionObjArgs.restype = W_Object
 #PyObject_CallFunctionObjArgs.argtypes = [W_Object, ..., final NULL]
 
-PyObject_RichCompare = pythonapi.PyObject_RichCompare
+PyObject_RichCompare = cpyapi.PyObject_RichCompare
 PyObject_RichCompare.argtypes = [W_Object, W_Object, c_int]
 PyObject_RichCompare.restype = W_Object
 
-PyObject_RichCompareBool = pythonapi.PyObject_RichCompareBool
+PyObject_RichCompareBool = cpyapi.PyObject_RichCompareBool
 PyObject_RichCompareBool.argtypes = [W_Object, W_Object, c_int]
 PyObject_RichCompareBool.restype = c_int
 
-PyObject_GetIter = pythonapi.PyObject_GetIter
+PyObject_GetIter = cpyapi.PyObject_GetIter
 PyObject_GetIter.argtypes = [W_Object]
 PyObject_GetIter.restype = W_Object
 
-PyIter_Next = pythonapi.PyIter_Next
+PyIter_Next = cpyapi.PyIter_Next
 PyIter_Next.argtypes = [W_Object]
 PyIter_Next.restype = W_Object
 
 
+###########################################################
+# ____________________ Number Protocol ____________________
+
+PyNumber_Add = cpyapi.PyNumber_Add
+PyNumber_Add.argtypes = [W_Object, W_Object]
+PyNumber_Add.restype = W_Object
+
+PyNumber_Subtract = cpyapi.PyNumber_Subtract
+PyNumber_Subtract.argtypes = [W_Object, W_Object]
+PyNumber_Subtract.restype = W_Object
+
+
 #############################################################
 # ____________________ Sequence Protocol ____________________
 
-PySequence_Tuple = pythonapi.PySequence_Tuple
+PySequence_Tuple = cpyapi.PySequence_Tuple
 PySequence_Tuple.argtypes = [W_Object]
 PySequence_Tuple.restype = W_Object
 
-PySequence_SetItem = pythonapi.PySequence_SetItem
+PySequence_SetItem = cpyapi.PySequence_SetItem
 PySequence_SetItem.argtypes = [W_Object, Py_ssize_t, W_Object]
 PySequence_SetItem.restype = c_int
 
@@ -106,11 +114,11 @@
 ###########################################################
 # ____________________ Numeric Objects ____________________
 
-PyInt_FromLong = pythonapi.PyInt_FromLong
+PyInt_FromLong = cpyapi.PyInt_FromLong
 PyInt_FromLong.argtypes = [c_long]
 PyInt_FromLong.restype = W_Object
 
-PyInt_AsLong = pythonapi.PyInt_AsLong
+PyInt_AsLong = cpyapi.PyInt_AsLong
 PyInt_AsLong.argtypes = [W_Object]
 PyInt_AsLong.restype = c_long
 
@@ -118,15 +126,15 @@
 ###################################################
 # ____________________ Strings ____________________
 
-PyString_FromStringAndSize = pythonapi.PyString_FromStringAndSize
+PyString_FromStringAndSize = cpyapi.PyString_FromStringAndSize
 PyString_FromStringAndSize.argtypes = [c_char_p, Py_ssize_t]
 PyString_FromStringAndSize.restype = W_Object
 
-PyString_InternInPlace = pythonapi.PyString_InternInPlace
+PyString_InternInPlace = cpyapi.PyString_InternInPlace
 PyString_InternInPlace.argtypes = [POINTER(W_Object)]
 PyString_InternInPlace.restype = None
 
-PyString_AsString = pythonapi.PyString_AsString
+PyString_AsString = cpyapi.PyString_AsString
 PyString_AsString.argtypes = [W_Object]
 PyString_AsString.restype = c_char_p
 
@@ -134,7 +142,7 @@
 ##################################################
 # ____________________ Tuples ____________________
 
-PyTuple_New = pythonapi.PyTuple_New
+PyTuple_New = cpyapi.PyTuple_New
 PyTuple_New.argtypes = [Py_ssize_t]
 PyTuple_New.restype = W_Object
 
@@ -142,11 +150,11 @@
 #################################################
 # ____________________ Lists ____________________
 
-PyList_New = pythonapi.PyList_New
+PyList_New = cpyapi.PyList_New
 PyList_New.argtypes = [Py_ssize_t]
 PyList_New.restype = W_Object
 
-PyList_Append = pythonapi.PyList_Append
+PyList_Append = cpyapi.PyList_Append
 PyList_Append.argtypes = [W_Object, W_Object]
 PyList_Append.restype = c_int
 
@@ -154,11 +162,11 @@
 ########################################################
 # ____________________ Dictionaries ____________________
 
-PyDict_New = pythonapi.PyDict_New
+PyDict_New = cpyapi.PyDict_New
 PyDict_New.argtypes = []
 PyDict_New.restype = W_Object
 
-PyDict_SetItem = pythonapi.PyDict_SetItem
+PyDict_SetItem = cpyapi.PyDict_SetItem
 PyDict_SetItem.argtypes = [W_Object, W_Object, W_Object]
 PyDict_SetItem.restype = c_int
 
@@ -166,23 +174,30 @@
 #####################################################
 # ____________________ Utilities ____________________
 
-PyImport_ImportModule = pythonapi.PyImport_ImportModule
+PyImport_ImportModule = cpyapi.PyImport_ImportModule
 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 ____________________
 
-PyArg_ParseTuple = pythonapi.PyArg_ParseTuple
+PyArg_ParseTuple = cpyapi.PyArg_ParseTuple
 PyArg_ParseTuple.restype = c_int
 #PyArg_ParseTuple.argtypes = [W_Object, c_char_p, ...]
 
-PyArg_ParseTupleAndKeywords = pythonapi.PyArg_ParseTupleAndKeywords
+PyArg_ParseTupleAndKeywords = cpyapi.PyArg_ParseTupleAndKeywords
 PyArg_ParseTupleAndKeywords.restype = c_int
 #PyArg_ParseTupleAndKeywords.argtypes = [W_Object, W_Object,
 #                                        c_char_p, POINTER(c_char_p), ...]
 
-##PyCFunction_NewEx = pythonapi.PyCFunction_NewEx
+##PyCFunction_NewEx = cpyapi.PyCFunction_NewEx
 ##PyCFunction_NewEx.argtypes = [POINTER(PyMethodDef), W_Object, W_Object]
 ##PyCFunction_NewEx.restype = W_Object

Added: pypy/dist/pypy/objspace/cpy/ctypes_base.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/objspace/cpy/ctypes_base.py	Sun Apr 30 16:00:22 2006
@@ -0,0 +1,50 @@
+"""
+CTypes base classes to support the particularities of the CPyObjSpace
+when it uses the CPython API: the W_Object class and the cpyapi accessor.
+"""
+
+import sys
+from ctypes import *
+from pypy.rpython.rctypes import apyobject
+from pypy.interpreter.error import OperationError
+from pypy.tool.sourcetools import func_with_new_name
+
+
+class W_Object(py_object):
+    """A py_object subclass, representing wrapped objects for the CPyObjSpace.
+    The reason we don't use py_object directly is that if py_object is
+    specified as the restype of a function, the function call unwraps it
+    automatically.  With W_Object, however, the function call returns a
+    W_Object instance.
+    """
+    def __repr__(self):
+        return 'W_Object(%r)' % (self.value,)
+
+apyobject.register_py_object_subclass(W_Object)
+
+
+class LevelError(Exception):
+    pass
+
+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
+    the one required for the CPyObjSpace: exceptions are wrapped in
+    OperationErrors.
+    """
+    class _FuncPtr(PyDLL._FuncPtr):
+        _flags_ = PyDLL._FuncPtr._flags_
+
+        def __call__(*args, **kwds):
+            try:
+                return PyDLL._FuncPtr.__call__(*args, **kwds)
+            except OperationError, e:
+                raise LevelError, "unexpected OperationError: %r" % (e,)
+            except:
+                exc, val, tb = sys.exc_info()
+                raise OperationError(W_Object(exc),
+                                     W_Object(val),
+                                     W_Object(tb))
+
+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	Sun Apr 30 16:00:22 2006
@@ -5,7 +5,7 @@
 
 
 class CPyObjSpace(baseobjspace.ObjSpace):
-    from pypy.objspace.cpy.capi import W_Object
+    from pypy.objspace.cpy.ctypes_base import W_Object
 
     def initialize(self):
         self.options.geninterp = True
@@ -16,6 +16,7 @@
         self.w_type  = W_Object(type)
         self.w_Exception     = W_Object(Exception)
         self.w_StopIteration = W_Object(StopIteration)
+        self.w_TypeError     = W_Object(TypeError)
         self.wrap_cache = {}
         self.rev_wrap_cache = {}
 
@@ -71,6 +72,9 @@
     str_w   = staticmethod(PyString_AsString)
     iter    = staticmethod(PyObject_GetIter)
 
+    add     = staticmethod(PyNumber_Add)
+    sub     = staticmethod(PyNumber_Subtract)
+
     def call_function(self, w_callable, *args_w):
         args_w += (None,)
         return PyObject_CallFunctionObjArgs(w_callable, *args_w)

Modified: pypy/dist/pypy/objspace/cpy/test/test_objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/test/test_objspace.py	(original)
+++ pypy/dist/pypy/objspace/cpy/test/test_objspace.py	Sun Apr 30 16:00:22 2006
@@ -1,4 +1,5 @@
 from pypy.objspace.cpy.objspace import CPyObjSpace
+from pypy.tool.pytest.appsupport import raises_w
 
 def test_simple():
     space = CPyObjSpace()
@@ -15,3 +16,9 @@
     w_time = demo.measuretime(space, 10, CPyObjSpace.W_Object(int))
     assert isinstance(w_time, CPyObjSpace.W_Object)
     assert isinstance(w_time.value, int)
+
+def test_exception():
+    space = CPyObjSpace()
+    w1 = space.wrap('abc')
+    w2 = space.wrap(11)
+    raises_w(space, space.w_TypeError, space.sub, w1, w2)

Modified: pypy/dist/pypy/objspace/cpy/test/test_wrappable.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/test/test_wrappable.py	(original)
+++ pypy/dist/pypy/objspace/cpy/test/test_wrappable.py	Sun Apr 30 16:00:22 2006
@@ -1,4 +1,5 @@
 from pypy.objspace.cpy.objspace import CPyObjSpace
+from pypy.tool.pytest.appsupport import raises_w
 from pypy.interpreter.function import BuiltinFunction
 from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
 from pypy.interpreter.argument import Arguments
@@ -29,3 +30,11 @@
     w_result = space.call_args(w_entrypoint, args)
     result = space.int_w(w_result)
     assert result == -21
+
+def test_exception():
+    space = CPyObjSpace()
+    func = interp2app(entrypoint1).__spacebind__(space)
+    bltin = BuiltinFunction(func)
+    w_entrypoint = space.wrap(bltin)
+    w1 = space.wrap('not an int')
+    raises_w(space, space.w_TypeError, space.call_function, w_entrypoint, w1)

Modified: pypy/dist/pypy/objspace/cpy/wrappable.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/wrappable.py	(original)
+++ pypy/dist/pypy/objspace/cpy/wrappable.py	Sun Apr 30 16:00:22 2006
@@ -7,6 +7,7 @@
 from pypy.annotation.pairtype import pair, pairtype
 from pypy.objspace.cpy.capi import *
 from pypy.objspace.cpy.objspace import CPyObjSpace
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.function import Function
 from pypy.interpreter.gateway import BuiltinCode, ObjSpace, W_Root
 from pypy.interpreter.gateway import UnwrapSpecRecipe, Signature
@@ -71,16 +72,25 @@
         sourcelines = ['def trampoline(%s):' % (', '.join(tramp.inputargs),)]
         for line in tramp.wrappings:
             sourcelines.append('    ' + line)
-        sourcelines.append('    w_result = ___bltin(%s)' % (
+        sourcelines.append('    try:')
+        sourcelines.append('        w_result = ___bltin(%s)' % (
             ', '.join(tramp.passedargs),))
+        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)')
         sourcelines.append('    return w_result.value')
         sourcelines.append('')
 
         miniglobals = {
-            '___space':        space,
-            '___W_Object':     CPyObjSpace.W_Object,
-            '___PyInt_AsLong': PyInt_AsLong,
-            '___bltin':        bltin,
+            '___space':           space,
+            '___W_Object':        CPyObjSpace.W_Object,
+            '___PyInt_AsLong':    PyInt_AsLong,
+            '___bltin':           bltin,
+            '___OperationError':  OperationError,
+            '___PyErr_SetObject': RAW_PyErr_SetObject,
             }
         exec py.code.Source('\n'.join(sourcelines)).compile() in miniglobals
 



More information about the Pypy-commit mailing list