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