Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r77572:9375ee9a4707
Date: 2015-05-26 13:33 +0200
http://bitbucket.org/pypy/pypy/changeset/9375ee9a4707/

Log:    Tweak: change 'structwrapper' to be used as a wrapper for all built-
        in functions, not only those that take a struct/union argument.
        This gives a nice place to store the function name, and give an
        error message that is the same as CPython in case of wrong number of
        arguments.

diff --git a/pypy/module/_cffi_backend/ffi_obj.py 
b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -10,7 +10,7 @@
 from pypy.module._cffi_backend import parse_c_type, realize_c_type
 from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
 from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
-from pypy.module._cffi_backend import cbuffer, func, cgc, structwrapper
+from pypy.module._cffi_backend import cbuffer, func, cgc, wrapper
 from pypy.module._cffi_backend import cffi_opcode
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.cdataobj import W_CData
@@ -478,7 +478,7 @@
 corresponding <ctype> object.
 It can also be used on 'cdata' instance to get its C type."""
         #
-        if isinstance(w_arg, structwrapper.W_StructWrapper):
+        if isinstance(w_arg, wrapper.W_FunctionWrapper):
             return w_arg.typeof(self)
         return self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CDATA)
 
diff --git a/pypy/module/_cffi_backend/lib_obj.py 
b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -12,7 +12,7 @@
 from pypy.module._cffi_backend.realize_c_type import getop, getarg
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
-from pypy.module._cffi_backend.structwrapper import W_StructWrapper
+from pypy.module._cffi_backend.wrapper import W_FunctionWrapper
 
 
 class W_LibObject(W_Root):
@@ -49,7 +49,7 @@
             num += 1
         self.ffi.included_ffis_libs = includes[:]
 
-    def _build_cpython_func(self, g):
+    def _build_cpython_func(self, g, fnname):
         # Build a function: in the PyPy version, these are all equivalent
         # and 'g->address' is a pointer to a function of exactly the
         # C type specified --- almost: arguments that are structs or
@@ -64,10 +64,8 @@
         #
         ptr = rffi.cast(rffi.CCHARP, g.c_address)
         assert ptr
-        w_cdata = W_CData(self.space, ptr, w_ct)
-        if locs is not None:
-            w_cdata = W_StructWrapper(w_cdata, locs, rawfunctype)
-        return w_cdata
+        return W_FunctionWrapper(self.space, ptr, w_ct,
+                                 locs, rawfunctype, fnname)
 
     @jit.elidable_promote()
     def _get_attr_elidable(self, attr):
@@ -100,7 +98,7 @@
                 op == cffi_opcode.OP_CPYTHON_BLTN_N or
                 op == cffi_opcode.OP_CPYTHON_BLTN_O):
                 # A function
-                w_result = self._build_cpython_func(g)
+                w_result = self._build_cpython_func(g, attr)
                 #
             elif op == cffi_opcode.OP_GLOBAL_VAR:
                 # A global variable of the exact type specified here
@@ -210,7 +208,7 @@
         #
         if ((isinstance(w_value, W_CData) and
                 isinstance(w_value.ctype, W_CTypeFunc))
-            or isinstance(w_value, W_StructWrapper)):
+            or isinstance(w_value, W_FunctionWrapper)):
             # '&func' is 'func' in C, for a constant function 'func'
             return w_value
         #
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py 
b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -740,3 +740,45 @@
         raises(AttributeError, ffi.addressof, lib, 'unknown_var')
         raises(AttributeError, ffi.addressof, lib, "FOOBAR")
         assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom
+
+    def test_defines__CFFI_(self):
+        # Check that we define the macro _CFFI_ automatically.
+        # It should be done before including Python.h, so that PyPy's Python.h
+        # can check for it.
+        ffi, lib = self.prepare("""
+            #define CORRECT 1
+        """, "test_defines__CFFI_", """
+            #ifdef _CFFI_
+            #    define CORRECT 1
+            #endif
+        """)
+        assert lib.CORRECT == 1
+
+    def test_unpack_args(self):
+        ffi, lib = self.prepare(
+            "void foo0(void); void foo1(int); void foo2(int, int);",
+            "test_unpack_args", """
+                void foo0(void) { }
+                void foo1(int x) { }
+                void foo2(int x, int y) { }
+            """)
+        assert 'foo0' in repr(lib.foo0)
+        assert 'foo1' in repr(lib.foo1)
+        assert 'foo2' in repr(lib.foo2)
+        lib.foo0()
+        lib.foo1(42)
+        lib.foo2(43, 44)
+        e1 = raises(TypeError, lib.foo0, 42)
+        e2 = raises(TypeError, lib.foo0, 43, 44)
+        e3 = raises(TypeError, lib.foo1)
+        e4 = raises(TypeError, lib.foo1, 43, 44)
+        e5 = raises(TypeError, lib.foo2)
+        e6 = raises(TypeError, lib.foo2, 42)
+        e7 = raises(TypeError, lib.foo2, 45, 46, 47)
+        assert str(e1.value) == "foo0() takes no arguments (1 given)"
+        assert str(e2.value) == "foo0() takes no arguments (2 given)"
+        assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+        assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+        assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
+        assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
+        assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"
diff --git a/pypy/module/_cffi_backend/structwrapper.py 
b/pypy/module/_cffi_backend/wrapper.py
rename from pypy/module/_cffi_backend/structwrapper.py
rename to pypy/module/_cffi_backend/wrapper.py
--- a/pypy/module/_cffi_backend/structwrapper.py
+++ b/pypy/module/_cffi_backend/wrapper.py
@@ -1,3 +1,4 @@
+from pypy.interpreter.error import oefmt
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app
@@ -10,7 +11,7 @@
 from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
 
 
-class W_StructWrapper(W_Root):
+class W_FunctionWrapper(W_Root):
     """A wrapper around a real W_CData which points to a function
     generated in the C code.  The real W_CData has got no struct/union
     argument (only pointers to it), and no struct/union return type
@@ -21,17 +22,19 @@
     """
     _immutable_ = True
 
-    def __init__(self, w_cdata, locs, rawfunctype):
-        space = w_cdata.space
-        ctype = w_cdata.ctype
+    def __init__(self, space, fnptr, ctype, locs, rawfunctype, fnname):
         assert isinstance(ctype, W_CTypeFunc)
-        assert len(ctype.fargs) == len(locs)
+        assert ctype.cif_descr is not None     # not for '...' functions
+        assert locs is None or len(ctype.fargs) == len(locs)
         #
         self.space = space
-        self.w_cdata = w_cdata
+        self.fnptr = fnptr
+        self.ctype = ctype
         self.locs = locs
-        self.fargs = ctype.fargs
         self.rawfunctype = rawfunctype
+        self.fnname = fnname
+        self.nargs_expected = len(ctype.fargs) - (locs is not None and
+                                                  locs[0] == 'R')
 
     def typeof(self, ffi):
         return self.rawfunctype.unwrap_as_fnptr(ffi)
@@ -41,12 +44,12 @@
         # replaces struct/union arguments with ptr-to-struct/union arguments
         space = self.space
         locs = self.locs
-        result_w = args_w[:]
-        for i in range(start_index, min(len(args_w), len(locs))):
+        fargs = self.ctype.fargs
+        for i in range(start_index, len(locs)):
             if locs[i] != 'A':
                 continue
             w_arg = args_w[i]
-            farg = self.fargs[i]      # <ptr to struct/union>
+            farg = fargs[i]      # <ptr to struct/union>
             assert isinstance(farg, W_CTypePtrOrArray)
             if isinstance(w_arg, W_CData) and w_arg.ctype is farg.ctitem:
                 # fast way: we are given a W_CData "struct", so just make
@@ -62,25 +65,49 @@
                 if space.is_w(w_arg, space.w_None):
                     continue
                 w_arg = farg.newp(w_arg)
-            result_w[i] = w_arg
-        return result_w
+            args_w[i] = w_arg
 
     def descr_call(self, args_w):
-        # If the result we want to present to the user is "returns struct",
-        # then internally allocate the struct and pass a pointer to it as
-        # a first argument.
-        if self.locs[0] == 'R':
-            w_result_cdata = self.fargs[0].newp(self.space.w_None)
-            args_w = [w_result_cdata] + args_w
-            self.w_cdata.call(self._prepare(args_w, 1))
-            assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
-            return w_result_cdata.structobj
-        else:
-            return self.w_cdata.call(self._prepare(args_w, 0))
+        if len(args_w) != self.nargs_expected:
+            space = self.space
+            if self.nargs_expected == 0:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes no arguments (%d given)",
+                            self.fnname, len(args_w))
+            elif self.nargs_expected == 1:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes exactly one argument (%d given)",
+                            self.fnname, len(args_w))
+            else:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes exactly %d arguments (%d given)",
+                            self.fnname, self.nargs_expected, len(args_w))
+        #
+        if self.locs is not None:
+            # This case is if there are structs as arguments or return values.
+            # If the result we want to present to the user is "returns struct",
+            # then internally allocate the struct and pass a pointer to it as
+            # a first argument.
+            if self.locs[0] == 'R':
+                w_result_cdata = self.ctype.fargs[0].newp(self.space.w_None)
+                args_w = [w_result_cdata] + args_w
+                self._prepare(args_w, 1)
+                self.ctype._call(self.fnptr, args_w)    # returns w_None
+                assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
+                return w_result_cdata.structobj
+            else:
+                args_w = args_w[:]
+                self._prepare(args_w, 0)
+        #
+        return self.ctype._call(self.fnptr, args_w)
 
+    def descr_repr(self, space):
+        return space.wrap("<FFIFunctionWrapper for %s()>" % (self.fnname,))
 
-W_StructWrapper.typedef = TypeDef(
-        'FFIFuncStructWrapper',
-        __call__ = interp2app(W_StructWrapper.descr_call),
+
+W_FunctionWrapper.typedef = TypeDef(
+        'FFIFunctionWrapper',
+        __repr__ = interp2app(W_FunctionWrapper.descr_repr),
+        __call__ = interp2app(W_FunctionWrapper.descr_call),
         )
-W_StructWrapper.typedef.acceptable_as_base_class = False
+W_FunctionWrapper.typedef.acceptable_as_base_class = False
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to