[pypy-commit] pypy cpyext-callopt: (arigato, plan_rich) some structural changes, cannot have different typeobjects for such a function object, does not work currently. need a way to pass over the gateway without __args__ for 3 specializations

plan_rich pypy.commits at gmail.com
Thu Mar 2 12:31:53 EST 2017


Author: Richard Plangger <planrichi at gmail.com>
Branch: cpyext-callopt
Changeset: r90495:dbba78b270fd
Date: 2017-03-02 18:31 +0100
http://bitbucket.org/pypy/pypy/changeset/dbba78b270fd/

Log:	(arigato, plan_rich) some structural changes, cannot have different
	typeobjects for such a function object, does not work currently.
	need a way to pass over the gateway without __args__ for 3
	specializations

diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -500,6 +500,8 @@
         self.unwrap.append("space.truncatedint_w(%s)" % (self.nextarg(),))
 
     def make_fastfunc(unwrap_spec, func):
+        if hasattr(func, '__cpyext_dispatch_hack'):
+            raise FastFuncNotSupported
         unwrap_info = UnwrapSpec_FastFunc_Unwrap()
         unwrap_info.apply_over(unwrap_spec)
         narg = unwrap_info.n
@@ -692,6 +694,9 @@
                 arity, fastfunc = UnwrapSpec_FastFunc_Unwrap.make_fastfunc(
                                                  unwrap_spec, func)
             except FastFuncNotSupported:
+                if hasattr(func, '__cpyext_dispatch_hack'):
+                    self.__class__ = BuiltinCPyExtCallHack
+                    self._func__args__ = []
                 if unwrap_spec == [ObjSpace, Arguments]:
                     self.__class__ = BuiltinCodePassThroughArguments0
                     self.func__args__ = func
@@ -769,6 +774,21 @@
 
 # (verbose) performance hack below
 
+class BuiltinCPyExtCallHack(BuiltinCode):
+    _immutable_ = True
+
+    def fastcall_0(self, space, w_func):
+        # METH_NOARGS
+        try:
+            w_result = self.fastfunc_0(space)
+        except DescrMismatch:
+            raise oefmt(space.w_SystemError, "unexpected DescrMismatch error")
+        except Exception as e:
+            self.handle_exception(space, e)
+            w_result = None
+        if w_result is None:
+            w_result = space.w_None
+        return w_result
 
 class BuiltinCodePassThroughArguments0(BuiltinCode):
     _immutable_ = True
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -3,7 +3,7 @@
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.function import ClassMethod, Method, StaticMethod
-from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.gateway import interp2app, interpindirect2app
 from pypy.interpreter.typedef import (
     GetSetProperty, TypeDef, interp_attrproperty, interp_attrproperty_w)
 from pypy.objspace.std.typeobject import W_TypeObject
@@ -43,17 +43,28 @@
     _dealloc(space, py_obj)
 
 class W_PyCFunctionObject(W_Root):
-    # TODO create a slightly different class depending on the c_ml_flags
     def __init__(self, space, ml, w_self, w_module=None):
         self.ml = ml
         self.name = rffi.charp2str(rffi.cast(rffi.CCHARP,self.ml.c_ml_name))
         self.w_self = w_self
         self.w_module = w_module
 
-    def call(self, space, w_self, w_args, w_kw):
+    def descr_call(self, space, __args__):
+        # specialize depending on the W_PyCFunctionObject
+        args_w, kw_w = __args__.unpack()
+
+        w_args = space.newtuple(args_w)
+        if len(kw_w) != 0:
+            # XXX __args__.unpack is slow
+            w_kw = space.newdict()
+            for key, w_obj in kw_w.items():
+                space.setitem(w_kw, space.newtext(key), w_obj)
+        else:
+            w_kw = None
+
         # Call the C function
-        if w_self is None:
-            w_self = self.w_self
+        # generic version if either METH_KEYWORDS or METH_OLDARGS is
+        # specified
         flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
         flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
         if not flags & METH_KEYWORDS and space.is_true(w_kw):
@@ -64,21 +75,7 @@
         length = space.int_w(space.len(w_args))
         if flags & METH_KEYWORDS:
             func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
-            return generic_cpy_call(space, func, w_self, w_args, w_kw)
-        elif flags & METH_NOARGS:
-            if length == 0:
-                return generic_cpy_call(space, func, w_self, None)
-            raise oefmt(space.w_TypeError,
-                        "%s() takes no arguments", self.name)
-        elif flags & METH_O:
-            if length != 1:
-                raise oefmt(space.w_TypeError,
-                            "%s() takes exactly one argument (%d given)",
-                            self.name, length)
-            w_arg = space.getitem(w_args, space.newint(0))
-            return generic_cpy_call(space, func, w_self, w_arg)
-        elif flags & METH_VARARGS:
-            return generic_cpy_call(space, func, w_self, w_args)
+            return generic_cpy_call(space, func, self, w_args, w_kw)
         else: # METH_OLDARGS, the really old style
             size = length
             if size == 1:
@@ -87,7 +84,9 @@
                 w_arg = None
             else:
                 w_arg = w_args
-            return generic_cpy_call(space, func, w_self, w_arg)
+            return generic_cpy_call(space, func, self, w_arg)
+
+    descr_call.__cpyext_dispatch_hack = True
 
     def get_doc(self, space):
         doc = self.ml.c_ml_doc
@@ -96,20 +95,23 @@
         else:
             return space.w_None
 
+class W_PyCFunctionObjectVarArgsOnly(W_PyCFunctionObject):
+    def descr_call(self, space, __args__):
+        # METH_VARARGS only
+        func = self.ml.c_ml_meth
+        return generic_cpy_call(space, func, self, space.newtuple(args_w))
+
 class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject):
-    def call(self, space, w_self, w_args, w_kw):
-        # Call the C function
-        if w_self is None:
-            w_self = self.w_self
+    def descr_call(self, space):
+        # METH_NOARGS
         func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_self, None)
+        return generic_cpy_call(space, func, self, None)
 
 class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject):
-    def call(self, space, w_self, w_o, w_kw):
-        if w_self is None:
-            w_self = self.w_self
+    def descr_call(self, space, w_o):
+        # special case for calling with flags METH_O
         func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_self, w_o)
+        return generic_cpy_call(space, func, self, w_o)
 
 class W_PyCMethodObject(W_PyCFunctionObject):
     w_self = None
@@ -204,28 +206,6 @@
         space.setitem(w_kw, space.newtext(key), w_obj)
     return self.call(space, w_self, w_args, w_kw)
 
-def cfunction_descr_call_noargs(space, w_self):
-    # special case for calling with flags METH_NOARGS
-    self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self)
-    return self.call(space, None, None, None)
-
-def cfunction_descr_call_single_object(space, w_self, w_o):
-    # special case for calling with flags METH_O
-    self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self)
-    return self.call(space, None, w_o, None)
-
-def cfunction_descr_call(space, w_self, __args__):
-    # specialize depending on the W_PyCFunctionObject
-    self = space.interp_w(W_PyCFunctionObject, w_self)
-    args_w, kw_w = __args__.unpack()
-    # XXX __args__.unpack is slow
-    w_args = space.newtuple(args_w)
-    w_kw = space.newdict()
-    for key, w_obj in kw_w.items():
-        space.setitem(w_kw, space.newtext(key), w_obj)
-    ret = self.call(space, None, w_args, w_kw)
-    return ret
-
 def cmethod_descr_call(space, w_self, __args__):
     self = space.interp_w(W_PyCFunctionObject, w_self)
     args_w, kw_w = __args__.unpack()
@@ -251,10 +231,9 @@
         w_cls = space.type(w_obj)
     return Method(space, w_function, w_cls, space.w_None)
 
-
 W_PyCFunctionObject.typedef = TypeDef(
     'builtin_function_or_method',
-    __call__ = interp2app(cfunction_descr_call),
+    __call__ = interp2app(W_PyCFunctionObject.descr_call),
     __doc__ = GetSetProperty(W_PyCFunctionObject.get_doc),
     __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObject),
     __name__ = interp_attrproperty('name', cls=W_PyCFunctionObject,
@@ -262,26 +241,6 @@
     )
 W_PyCFunctionObject.typedef.acceptable_as_base_class = False
 
-W_PyCFunctionObjectNoArgs.typedef = TypeDef(
-    'builtin_function_or_method', W_PyCFunctionObject.typedef,
-    __call__ = interp2app(cfunction_descr_call_noargs),
-    __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc),
-    __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs),
-    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs,
-        wrapfn="newtext_or_none"),
-    )
-W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False
-
-W_PyCFunctionObjectSingleObject.typedef = TypeDef(
-    'builtin_function_or_method', W_PyCFunctionObject.typedef,
-    __call__ = interp2app(cfunction_descr_call_single_object),
-    __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc),
-    __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject),
-    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject,
-        wrapfn="newtext_or_none"),
-    )
-W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False
-
 W_PyCMethodObject.typedef = TypeDef(
     'method',
     __get__ = interp2app(cmethod_descr_get),
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -1,13 +1,14 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, cpython_struct, \
         METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \
-        METH_NOARGS, METH_O
+        METH_NOARGS, METH_O, METH_VARARGS
 from pypy.module.cpyext.pyobject import PyObject, as_pyobj
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import (
     W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
     PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New,
-    W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject)
+    W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject,
+    W_PyCFunctionObjectVarArgsOnly)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import oefmt
@@ -79,6 +80,8 @@
         return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name)
     if flags == METH_O:
         return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name)
+    if flags == METH_VARARGS:
+        return W_PyCFunctionObjectVarArgsOnly(space, method, w_self, w_name)
     return W_PyCFunctionObject(space, method, w_self, w_name)
 
 def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None):
diff --git a/pypy/module/cpyext/test/test_methodobject.py b/pypy/module/cpyext/test/test_methodobject.py
--- a/pypy/module/cpyext/test/test_methodobject.py
+++ b/pypy/module/cpyext/test/test_methodobject.py
@@ -40,6 +40,14 @@
              }
              '''
              ),
+            ('getarg_VARARGS', 'METH_VARARGS',
+             '''
+             PyObject * i;
+             i = PyLong_FromLong((long)PyObject_Length(args));
+             Py_INCREF(i);
+             return i;
+             '''
+             ),
             ('isCFunction', 'METH_O',
              '''
              if(PyCFunction_Check(args)) {
@@ -83,6 +91,10 @@
         raises(TypeError, mod.getarg_NO, 1)
         raises(TypeError, mod.getarg_NO, 1, 1)
 
+        assert mod.getarg_VARARGS() == 0
+        assert mod.getarg_VARARGS(1) == 1
+        raises(TypeError, mod.getarg_VARARGS, k=1)
+
         assert mod.getarg_OLD(1) == 1
         assert mod.getarg_OLD() is None
         assert mod.getarg_OLD(1, 2) == (1, 2)


More information about the pypy-commit mailing list