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