Author: Antonio Cuni <[email protected]>
Branch: cpyext-avoid-roundtrip
Changeset: r92827:770b53602445
Date: 2017-10-22 17:39 +0200
http://bitbucket.org/pypy/pypy/changeset/770b53602445/

Log:    merge the branch cpyext-refactor-methodobject: now W_PyCMethodObject
        and W_PyCFunctionObject use the same code to dispatch calls, with
        the effect that the latter is as fast as the former; moreover,
        METH_VARARGS uses a special, faster logic

diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -16,11 +16,12 @@
         space.fromcache(State).startup(space)
         method = pypy.module.cpyext.typeobject.get_new_method_def(space)
         # the w_self argument here is a dummy, the only thing done with w_obj
-        # is call space.type on it
-        w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space, 
method, space.w_None)
-        space.appexec([space.type(w_obj)], """(methodtype):
+        # is call type() on it
+        w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space,
+                                                           method, 
space.w_None)
+        space.appexec([w_obj], """(meth):
             from pickle import Pickler
-            Pickler.dispatch[methodtype] = Pickler.save_global
+            Pickler.dispatch[type(meth)] = Pickler.save_global
         """)
 
     def register_atexit(self, function):
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
@@ -15,6 +15,8 @@
     build_type_checkers)
 from pypy.module.cpyext.pyobject import (
     decref, from_ref, make_ref, as_pyobj, make_typedescr)
+from pypy.module.cpyext.state import State
+from pypy.module.cpyext.tupleobject import tuple_from_args_w
 
 PyMethodDef = cts.gettype('PyMethodDef')
 PyCFunction = cts.gettype('PyCFunction')
@@ -44,31 +46,29 @@
     _dealloc(space, py_obj)
 
 class W_PyCFunctionObject(W_Root):
-    # TODO create a slightly different class depending on the c_ml_flags
+    _immutable_fields_ = ["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.flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
         self.w_self = w_self
         self.w_module = w_module
 
-    def call(self, space, w_self, w_args, w_kw):
-        # Call the C function
-        if w_self is None:
-            w_self = self.w_self
-        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):
+    def descr_call(self, space, __args__):
+        return self.call(space, self.w_self, __args__)
+
+    def call(self, space, w_self, __args__):
+        flags = self.flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST)
+        length = len(__args__.arguments_w)
+        if not flags & METH_KEYWORDS and __args__.keywords:
             raise oefmt(space.w_TypeError,
                         "%s() takes no keyword arguments", self.name)
-
-        func = self.ml.c_ml_meth
-        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)
+            return self.call_keywords(space, w_self, __args__)
         elif flags & METH_NOARGS:
             if length == 0:
-                return generic_cpy_call(space, func, w_self, None)
+                return self.call_noargs(space, w_self, __args__)
             raise oefmt(space.w_TypeError,
                         "%s() takes no arguments", self.name)
         elif flags & METH_O:
@@ -76,19 +76,60 @@
                 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)
+            return self.call_o(space, w_self, __args__)
         elif flags & METH_VARARGS:
-            return generic_cpy_call(space, func, w_self, w_args)
-        else: # METH_OLDARGS, the really old style
-            size = length
-            if size == 1:
-                w_arg = space.getitem(w_args, space.newint(0))
-            elif size == 0:
-                w_arg = None
-            else:
-                w_arg = w_args
-            return generic_cpy_call(space, func, w_self, w_arg)
+            return self.call_varargs(space, w_self, __args__)
+        else:
+            return self.call_oldargs(space, w_self, __args__)
+
+    def call_noargs(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        return generic_cpy_call(space, func, w_self, None)
+
+    def call_o(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        w_o = __args__.arguments_w[0]        
+        return generic_cpy_call(space, func, w_self, w_o)
+
+    def call_varargs(self, space, w_self, __args__):
+        state = space.fromcache(State)
+        func = self.ml.c_ml_meth
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args)
+        finally:
+            decref(space, py_args)
+
+    def call_keywords(self, space, w_self, __args__):
+        func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        w_kwargs = None
+        if __args__.keywords:
+            # CCC: we should probably have a @jit.look_inside_iff if the
+            # keyword count is constant, as we do in Arguments.unpack
+            w_kwargs = space.newdict()
+            for i in range(len(__args__.keywords)):
+                key = __args__.keywords[i]
+                w_obj = __args__.keywords_w[i]
+                space.setitem(w_kwargs, space.newtext(key), w_obj)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args, w_kwargs)
+        finally:
+            decref(space, py_args)
+
+    def call_oldargs(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        length = len(__args__.arguments_w)
+        if length == 0:
+            py_args = lltype.nullptr(PyObject.TO)
+        elif length == 1:
+            py_args = make_ref(space, __args__.arguments_w[0])
+        else:
+            py_args = tuple_from_args_w(space, __args__.arguments_w)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args)
+        finally:
+            decref(space, py_args)
 
     def get_doc(self, space):
         doc = self.ml.c_ml_doc
@@ -97,21 +138,6 @@
         else:
             return space.w_None
 
-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
-        func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_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
-        func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_self, w_o)
-
 class W_PyCMethodObject(W_PyCFunctionObject):
     w_self = None
     def __init__(self, space, ml, w_type):
@@ -130,14 +156,13 @@
             self.name, w_objclass.name))
 
     def descr_call(self, space, __args__):
-        args_w, kw_w = __args__.unpack()
-        if len(args_w) < 1:
+        if len(__args__.arguments_w) == 0:
             w_objclass = self.w_objclass
             assert isinstance(w_objclass, W_TypeObject)
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' of '%s' object needs an argument",
                 self.name, w_objclass.name)
-        w_instance = args_w[0]
+        w_instance = __args__.arguments_w[0]
         # XXX: needs a stricter test
         if not space.isinstance_w(w_instance, self.w_objclass):
             w_objclass = self.w_objclass
@@ -145,12 +170,10 @@
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' requires a '%s' object but received a '%T'",
                 self.name, w_objclass.name, w_instance)
-        w_args = space.newtuple(args_w[1:])
-        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, w_instance, w_args, w_kw)
-        return ret
+        #
+        # CCC: we can surely do better than this
+        __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+        return self.call(space, w_instance, __args__)
 
 # PyPy addition, for Cython
 _, _ = build_type_checkers("MethodDescr", W_PyCMethodObject)
@@ -176,6 +199,16 @@
     def __repr__(self):
         return self.space.unwrap(self.descr_method_repr())
 
+    def descr_call(self, space, __args__):
+        if len(__args__.arguments_w) == 0:
+            raise oefmt(space.w_TypeError,
+                "descriptor '%s' of '%s' object needs an argument",
+                self.name, self.w_objclass.getname(space))
+        w_instance = __args__.arguments_w[0] # XXX typecheck missing
+        # CCC: we can surely do better than this
+        __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+        return self.call(space, w_instance, __args__)
+
     def descr_method_repr(self):
         return self.getrepr(self.space,
                             "built-in method '%s' of '%s' object" %
@@ -241,45 +274,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)
-
[email protected]_look_inside
-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
-
[email protected]_look_inside
-def cclassmethod_descr_call(space, w_self, __args__):
-    self = space.interp_w(W_PyCFunctionObject, w_self)
-    args_w, kw_w = __args__.unpack()
-    if len(args_w) < 1:
-        raise oefmt(space.w_TypeError,
-            "descriptor '%s' of '%s' object needs an argument",
-            self.name, self.w_objclass.getname(space))
-    w_instance = args_w[0] # XXX typecheck missing
-    w_args = space.newtuple(args_w[1:])
-    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, w_instance, w_args, w_kw)
-    return ret
-
 def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
     asking_for_bound = (space.is_none(w_cls) or
                         not space.is_w(w_obj, space.w_None) or
@@ -297,7 +291,7 @@
 
 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,
@@ -305,26 +299,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_descriptor',
     __get__ = interp2app(cmethod_descr_get),
@@ -339,7 +313,7 @@
 W_PyCClassMethodObject.typedef = TypeDef(
     'classmethod',
     __get__ = interp2app(cclassmethod_descr_get),
-    __call__ = interp2app(cclassmethod_descr_call),
+    __call__ = interp2app(W_PyCClassMethodObject.descr_call),
     __name__ = interp_attrproperty('name', cls=W_PyCClassMethodObject,
         wrapfn="newtext_or_none"),
     __objclass__ = interp_attrproperty_w('w_objclass',
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,12 @@
 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)
+    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import oefmt
@@ -81,14 +80,6 @@
                       space.newtext(rffi.charp2str(doc)))
     return w_mod   # borrowed result kept alive in PyImport_AddModule()
 
-def _create_pyc_function_object(space, method, w_self, w_name, flags):
-    flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
-    if flags == METH_NOARGS:
-        return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name)
-    if flags == METH_O:
-        return W_PyCFunctionObjectSingleObject(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):
     w_name = space.newtext_or_none(name)
     methods = rffi.cast(rffi.CArrayPtr(PyMethodDef), methods)
@@ -107,8 +98,7 @@
                     raise oefmt(space.w_ValueError,
                             "module functions cannot set METH_CLASS or "
                             "METH_STATIC")
-                w_obj = _create_pyc_function_object(space, method, w_self,
-                                                    w_name, flags)
+                w_obj = W_PyCFunctionObject(space, method, w_self, w_name)
             else:
                 if methodname in dict_w and not (flags & METH_COEXIST):
                     continue
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
@@ -8,14 +8,9 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 
 class AppTestMethodObject(AppTestCpythonExtensionBase):
-    def test_call_METH(self):
+
+    def test_call_METH_NOARGS(self):
         mod = self.import_extension('MyModule', [
-            ('getarg_O', 'METH_O',
-             '''
-             Py_INCREF(args);
-             return args;
-             '''
-             ),
             ('getarg_NO', 'METH_NOARGS',
              '''
              if(args) {
@@ -28,11 +23,62 @@
              }
              '''
              ),
+            ])
+        assert mod.getarg_NO() is None
+        raises(TypeError, mod.getarg_NO, 1)
+        raises(TypeError, mod.getarg_NO, 1, 1)
+
+    def test_call_METH_O(self):
+        mod = self.import_extension('MyModule', [
+            ('getarg_O', 'METH_O',
+             '''
+             Py_INCREF(args);
+             return args;
+             '''
+             ),
+            ])
+        assert mod.getarg_O(1) == 1
+        assert mod.getarg_O.__name__ == "getarg_O"
+        raises(TypeError, mod.getarg_O)
+        raises(TypeError, mod.getarg_O, 1, 1)
+
+    def test_call_METH_VARARGS(self):
+        mod = self.import_extension('MyModule', [
+            ('getarg_VARARGS', 'METH_VARARGS',
+             '''
+             return Py_BuildValue("Ol", args, args->ob_refcnt);
+             '''
+             ),
+            ])
+        # check that we pass the expected tuple of arguments AND that the
+        # recnt is 1. In particular, on PyPy refcnt==1 means that we created
+        # the PyObject tuple directly, without passing from a w_tuple; as
+        # such, the tuple will be immediately freed after the call, without
+        # having to wait until the GC runs.
+        #
+        tup, refcnt = mod.getarg_VARARGS()
+        assert tup == ()
+        # the empty tuple is shared on CPython, so the refcnt will be >1. On
+        # PyPy it is not shared, though.
+        if not self.runappdirect:
+            assert refcnt == 1
+        #
+        tup, refcnt = mod.getarg_VARARGS(1)
+        assert tup == (1,)
+        assert refcnt == 1
+        #
+        tup, refcnt = mod.getarg_VARARGS(1, 2, 3)
+        assert tup == (1, 2, 3)
+        assert refcnt == 1
+        #
+        raises(TypeError, mod.getarg_VARARGS, k=1)
+
+    def test_call_METH_OLDARGS(self):
+        mod = self.import_extension('MyModule', [
             ('getarg_OLD', 'METH_OLDARGS',
              '''
              if(args) {
-                 Py_INCREF(args);
-                 return args;
+                 return Py_BuildValue("Ol", args, args->ob_refcnt);
              }
              else {
                  Py_INCREF(Py_None);
@@ -40,6 +86,31 @@
              }
              '''
              ),
+            ])
+        assert mod.getarg_OLD() is None
+        val, refcnt = mod.getarg_OLD(1)
+        assert val == 1
+        val, refcnt = mod.getarg_OLD(1, 2)
+        assert val == (1, 2)
+        assert refcnt == 1 # see the comments in the test above
+
+    def test_call_METH_KEYWORDS(self):
+        mod = self.import_extension('MyModule', [
+            ('getarg_KW', 'METH_VARARGS | METH_KEYWORDS',
+             '''
+             if (!kwargs) kwargs = Py_None;
+             return Py_BuildValue("OO", args, kwargs);
+             '''
+             ),
+            ])
+        assert mod.getarg_KW(1) == ((1,), None)
+        assert mod.getarg_KW(1, 2) == ((1, 2), None)
+        assert mod.getarg_KW(a=3, b=4) == ((), {'a': 3, 'b': 4})
+        assert mod.getarg_KW(1, 2, a=3, b=4) == ((1, 2), {'a': 3, 'b': 4})
+        assert mod.getarg_KW.__name__ == "getarg_KW"
+
+    def test_func_attributes(self):
+        mod = self.import_extension('MyModule', [
             ('isCFunction', 'METH_O',
              '''
              if(PyCFunction_Check(args)) {
@@ -67,30 +138,17 @@
              '''
              PyCFunction ptr = PyCFunction_GetFunction(args);
              if (!ptr) return NULL;
-             if (ptr == (PyCFunction)MyModule_getarg_O)
+             if (ptr == (PyCFunction)MyModule_getModule)
                  Py_RETURN_TRUE;
              else
                  Py_RETURN_FALSE;
              '''
              ),
             ])
-        assert mod.getarg_O(1) == 1
-        assert mod.getarg_O.__name__ == "getarg_O"
-        raises(TypeError, mod.getarg_O)
-        raises(TypeError, mod.getarg_O, 1, 1)
-
-        assert mod.getarg_NO() is None
-        raises(TypeError, mod.getarg_NO, 1)
-        raises(TypeError, mod.getarg_NO, 1, 1)
-
-        assert mod.getarg_OLD(1) == 1
-        assert mod.getarg_OLD() is None
-        assert mod.getarg_OLD(1, 2) == (1, 2)
-
-        assert mod.isCFunction(mod.getarg_O) == "getarg_O"
-        assert mod.getModule(mod.getarg_O) == 'MyModule'
+        assert mod.isCFunction(mod.getModule) == "getModule"
+        assert mod.getModule(mod.getModule) == 'MyModule'
         if self.runappdirect:  # XXX: fails untranslated
-            assert mod.isSameFunction(mod.getarg_O)
+            assert mod.isSameFunction(mod.getModule)
         raises(SystemError, mod.isSameFunction, 1)
 
     def test_check(self):
diff --git a/pypy/module/cpyext/test/test_tupleobject.py 
b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -6,7 +6,8 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib.debug import FatalError
 from pypy.module.cpyext.tupleobject import (
-    PyTupleObject, PyTuple_Check, PyTuple_SetItem, PyTuple_Size)
+    PyTupleObject, PyTuple_Check, PyTuple_SetItem, PyTuple_Size,
+    tuple_from_args_w)
 from pypy.module.cpyext.state import State
 
 class TestTupleObject(BaseApiTest):
@@ -77,6 +78,16 @@
         assert py_b == py_a
         assert py_b.c_ob_pypy_link == 0
 
+    def test_tuple_from_args_w(self, space, api):
+        args_w = [space.newint(i) for i in (40, 41, 42)]
+        py_tuple = tuple_from_args_w(space, args_w)
+        assert py_tuple.c_ob_refcnt == 1
+        assert api.PyTuple_Size(py_tuple) == 3
+        py_items = [api.PyTuple_GetItem(py_tuple, i) for i in range(3)]
+        items = [api.PyInt_AsLong(py_obj) for py_obj in py_items]
+        assert items == [40, 41, 42]
+        decref(space, py_tuple)
+
     def test_tuple_resize(self, space, api):
         state = space.fromcache(State)
         w_42 = space.wrap(42)
diff --git a/pypy/module/cpyext/tupleobject.py 
b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -113,6 +113,14 @@
     track_reference(space, py_obj, w_obj)
     return w_obj
 
+def tuple_from_args_w(space, args_w):
+    state = space.fromcache(State)
+    n = len(args_w)
+    py_tuple = state.C.PyTuple_New(n) # XXX: check for errors?
+    py_tuple = rffi.cast(PyTupleObject, py_tuple)
+    for i, w_obj in enumerate(args_w):
+        py_tuple.c_ob_item[i] = make_ref(space, w_obj)
+    return rffi.cast(PyObject, py_tuple)
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
 def PyTuple_SetItem(space, ref, index, py_obj):
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -378,7 +378,10 @@
     args_w = space.fixedview(w_args)
     w_subtype = args_w[0]
     w_args = space.newtuple(args_w[1:])
-    if not space.is_true(w_kwds):
+    # CCC: is this check still needed? I think we can safely remove it now
+    # that we manually handle __args__ inside W_PyCFunctionObject.descr_call,
+    # but we need to double check
+    if w_kwds and not space.is_true(w_kwds):
         w_kwds = None
 
     subtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_subtype))
diff --git a/pypy/tool/cpyext/extbuild.py b/pypy/tool/cpyext/extbuild.py
--- a/pypy/tool/cpyext/extbuild.py
+++ b/pypy/tool/cpyext/extbuild.py
@@ -114,14 +114,18 @@
     codes = []
     for funcname, flags, code in functions:
         cfuncname = "%s_%s" % (modname, funcname)
+        if 'METH_KEYWORDS' in flags:
+            signature = '(PyObject *self, PyObject *args, PyObject *kwargs)'
+        else:
+            signature = '(PyObject *self, PyObject *args)'
         methods_table.append(
-            "{\"%s\", %s, %s}," % (funcname, cfuncname, flags))
+            "{\"%s\", (PyCFunction)%s, %s}," % (funcname, cfuncname, flags))
         func_code = """
-        static PyObject* %s(PyObject* self, PyObject* args)
-        {
-        %s
-        }
-        """ % (cfuncname, code)
+        static PyObject* {cfuncname}{signature}
+        {{
+        {code}
+        }}
+        """.format(cfuncname=cfuncname, signature=signature, code=code)
         codes.append(func_code)
 
     body = "\n".join(codes) + """
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to