Author: Richard Plangger <planri...@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) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit