PageRenderTime 37ms CodeModel.GetById 11ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/cpyext/methodobject.py

https://bitbucket.org/rokujyouhitoma/pypy/
Python | 281 lines | 235 code | 38 blank | 8 comment | 28 complexity | 68bc219e6945d035e0df8fe72d9b3c25 MD5 | raw file
  1from pypy.interpreter.baseobjspace import Wrappable
  2from pypy.interpreter.typedef import TypeDef, GetSetProperty
  3from pypy.interpreter.argument import Arguments
  4from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
  5from pypy.interpreter.gateway import interp2app
  6from pypy.interpreter.error import OperationError, operationerrfmt
  7from pypy.interpreter.function import BuiltinFunction, Method, StaticMethod
  8from pypy.rpython.lltypesystem import rffi, lltype
  9from pypy.module.cpyext.pyobject import (PyObject, from_ref, make_ref,
 10                                         make_typedescr, Py_DecRef)
 11from pypy.module.cpyext.api import (
 12    generic_cpy_call, cpython_api, PyObject, cpython_struct, METH_KEYWORDS,
 13    METH_O, CONST_STRING, METH_CLASS, METH_STATIC, METH_COEXIST, METH_NOARGS,
 14    METH_VARARGS, build_type_checkers, PyObjectFields, bootstrap_function)
 15from pypy.module.cpyext.pyerrors import PyErr_Occurred
 16from pypy.rlib.objectmodel import we_are_translated
 17
 18PyCFunction_typedef = rffi.COpaquePtr(typedef='PyCFunction')
 19PyCFunction = lltype.Ptr(lltype.FuncType([PyObject, PyObject], PyObject))
 20PyCFunctionKwArgs = lltype.Ptr(lltype.FuncType([PyObject, PyObject, PyObject], PyObject))
 21
 22PyMethodDef = cpython_struct(
 23    'PyMethodDef',
 24    [('ml_name', rffi.CCHARP),
 25     ('ml_meth', PyCFunction_typedef),
 26     ('ml_flags', rffi.INT_real),
 27     ('ml_doc', rffi.CCHARP),
 28     ])
 29
 30PyCFunctionObjectStruct = cpython_struct(
 31    'PyCFunctionObject',
 32    PyObjectFields + (
 33     ('m_ml', lltype.Ptr(PyMethodDef)),
 34     ('m_self', PyObject),
 35     ('m_module', PyObject),
 36     ))
 37PyCFunctionObject = lltype.Ptr(PyCFunctionObjectStruct)
 38
 39@bootstrap_function
 40def init_methodobject(space):
 41    make_typedescr(W_PyCFunctionObject.typedef,
 42                   basestruct=PyCFunctionObject.TO,
 43                   attach=cfunction_attach,
 44                   dealloc=cfunction_dealloc)
 45
 46def cfunction_attach(space, py_obj, w_obj):
 47    py_func = rffi.cast(PyCFunctionObject, py_obj)
 48    assert isinstance(w_obj, W_PyCFunctionObject)
 49    py_func.c_m_ml = w_obj.ml
 50    py_func.c_m_self = make_ref(space, w_obj.w_self)
 51    py_func.c_m_module = make_ref(space, w_obj.w_module)
 52
 53@cpython_api([PyObject], lltype.Void, external=False)
 54def cfunction_dealloc(space, py_obj):
 55    py_func = rffi.cast(PyCFunctionObject, py_obj)
 56    Py_DecRef(space, py_func.c_m_self)
 57    Py_DecRef(space, py_func.c_m_module)
 58    from pypy.module.cpyext.object import PyObject_dealloc
 59    PyObject_dealloc(space, py_obj)
 60
 61class W_PyCFunctionObject(Wrappable):
 62    def __init__(self, space, ml, w_self, w_module=None):
 63        self.ml = ml
 64        self.name = rffi.charp2str(self.ml.c_ml_name)
 65        self.w_self = w_self
 66        self.w_module = w_module
 67
 68    def call(self, space, w_self, w_args, w_kw):
 69        # Call the C function
 70        if w_self is None:
 71            w_self = self.w_self
 72        flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
 73        flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
 74        if space.is_true(w_kw) and not flags & METH_KEYWORDS:
 75            raise OperationError(space.w_TypeError, space.wrap(
 76                self.name + "() takes no keyword arguments"))
 77
 78        func = rffi.cast(PyCFunction, self.ml.c_ml_meth)
 79        length = space.int_w(space.len(w_args))
 80        if flags & METH_KEYWORDS:
 81            func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
 82            return generic_cpy_call(space, func, w_self, w_args, w_kw)
 83        elif flags & METH_NOARGS:
 84            if length == 0:
 85                return generic_cpy_call(space, func, w_self, None)
 86            raise OperationError(space.w_TypeError, space.wrap(
 87                self.name + "() takes no arguments"))
 88        elif flags & METH_O:
 89            if length != 1:
 90                raise OperationError(space.w_TypeError,
 91                        space.wrap("%s() takes exactly one argument (%d given)" %  (
 92                        self.name, length)))
 93            w_arg = space.getitem(w_args, space.wrap(0))
 94            return generic_cpy_call(space, func, w_self, w_arg)
 95        elif flags & METH_VARARGS:
 96            return generic_cpy_call(space, func, w_self, w_args)
 97        else: # METH_OLDARGS, the really old style
 98            size = length
 99            if size == 1:
100                w_arg = space.getitem(w_args, space.wrap(0))
101            elif size == 0:
102                w_arg = None
103            else:
104                w_arg = w_args
105            return generic_cpy_call(space, func, w_self, w_arg)
106
107    def get_doc(self, space):
108        doc = self.ml.c_ml_doc
109        if doc:
110            return space.wrap(rffi.charp2str(doc))
111        else:
112            return space.w_None
113
114
115class W_PyCMethodObject(W_PyCFunctionObject):
116    w_self = None
117    def __init__(self, space, ml, w_type):
118        self.space = space
119        self.ml = ml
120        self.name = rffi.charp2str(ml.c_ml_name)
121        self.w_objclass = w_type
122
123    def __repr__(self):
124        return self.space.unwrap(self.descr_method_repr())
125
126    def descr_method_repr(self):
127        return self.getrepr(self.space, "built-in method '%s' of '%s' object" % (self.name, self.w_objclass.getname(self.space)))
128
129PyCFunction_Check, PyCFunction_CheckExact = build_type_checkers("CFunction", W_PyCFunctionObject)
130
131class W_PyCWrapperObject(Wrappable):
132    def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds,
133            doc, func):
134        self.space = space
135        self.method_name = method_name
136        self.wrapper_func = wrapper_func
137        self.wrapper_func_kwds = wrapper_func_kwds
138        self.doc = doc
139        self.func = func
140        pyo = rffi.cast(PyObject, pto)
141        self.w_objclass = from_ref(space, pyo)
142
143    def call(self, space, w_self, w_args, w_kw):
144        if self.wrapper_func is None:
145            assert self.wrapper_func_kwds is not None
146            return self.wrapper_func_kwds(space, w_self, w_args, self.func, w_kw)
147        if space.is_true(w_kw):
148            raise operationerrfmt(
149                space.w_TypeError,
150                "wrapper %s doesn't take any keyword arguments",
151                self.method_name)
152        return self.wrapper_func(space, w_self, w_args, self.func)
153
154    def descr_method_repr(self):
155        return self.space.wrap("<slot wrapper '%s' of '%s' objects>" % (self.method_name,
156            self.w_objclass.getname(self.space)))
157
158def cwrapper_descr_call(space, w_self, __args__):
159    self = space.interp_w(W_PyCWrapperObject, w_self)
160    args_w, kw_w = __args__.unpack()
161    w_args = space.newtuple(args_w[1:])
162    w_self = args_w[0]
163    w_kw = space.newdict()
164    for key, w_obj in kw_w.items():
165        space.setitem(w_kw, space.wrap(key), w_obj)
166    return self.call(space, w_self, w_args, w_kw)
167
168
169def cfunction_descr_call(space, w_self, __args__):
170    self = space.interp_w(W_PyCFunctionObject, w_self)
171    args_w, kw_w = __args__.unpack()
172    w_args = space.newtuple(args_w)
173    w_kw = space.newdict()
174    for key, w_obj in kw_w.items():
175        space.setitem(w_kw, space.wrap(key), w_obj)
176    ret = self.call(space, None, w_args, w_kw)
177    return ret
178
179def cmethod_descr_call(space, w_self, __args__):
180    self = space.interp_w(W_PyCFunctionObject, w_self)
181    args_w, kw_w = __args__.unpack()
182    w_instance = args_w[0] # XXX typecheck missing
183    w_args = space.newtuple(args_w[1:])
184    w_kw = space.newdict()
185    for key, w_obj in kw_w.items():
186        space.setitem(w_kw, space.wrap(key), w_obj)
187    ret = self.call(space, w_instance, w_args, w_kw)
188    return ret
189
190def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
191    asking_for_bound = (space.is_w(w_cls, space.w_None) or
192                        not space.is_w(w_obj, space.w_None) or
193                        space.is_w(w_cls, space.type(space.w_None)))
194    if asking_for_bound:
195        return space.wrap(Method(space, w_function, w_obj, w_cls))
196    else:
197        return w_function
198
199
200W_PyCFunctionObject.typedef = TypeDef(
201    'builtin_function_or_method',
202    __call__ = interp2app(cfunction_descr_call),
203    __doc__ = GetSetProperty(W_PyCFunctionObject.get_doc),
204    __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObject),
205    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObject),
206    )
207W_PyCFunctionObject.typedef.acceptable_as_base_class = False
208
209W_PyCMethodObject.typedef = TypeDef(
210    'method',
211    __get__ = interp2app(cmethod_descr_get),
212    __call__ = interp2app(cmethod_descr_call),
213    __name__ = interp_attrproperty('name', cls=W_PyCMethodObject),
214    __objclass__ = interp_attrproperty_w('w_objclass', cls=W_PyCMethodObject),
215    __repr__ = interp2app(W_PyCMethodObject.descr_method_repr),
216    )
217W_PyCMethodObject.typedef.acceptable_as_base_class = False
218
219
220W_PyCWrapperObject.typedef = TypeDef(
221    'wrapper_descriptor',
222    __call__ = interp2app(cwrapper_descr_call),
223    __get__ = interp2app(cmethod_descr_get),
224    __name__ = interp_attrproperty('method_name', cls=W_PyCWrapperObject),
225    __doc__ = interp_attrproperty('doc', cls=W_PyCWrapperObject),
226    __objclass__ = interp_attrproperty_w('w_objclass', cls=W_PyCWrapperObject),
227    __repr__ = interp2app(W_PyCWrapperObject.descr_method_repr),
228    # XXX missing: __getattribute__
229    )
230W_PyCWrapperObject.typedef.acceptable_as_base_class = False
231
232
233@cpython_api([lltype.Ptr(PyMethodDef), PyObject, PyObject], PyObject)
234def PyCFunction_NewEx(space, ml, w_self, w_name):
235    return space.wrap(W_PyCFunctionObject(space, ml, w_self, w_name))
236
237@cpython_api([PyObject], PyCFunction_typedef)
238def PyCFunction_GetFunction(space, w_obj):
239    cfunction = space.interp_w(W_PyCFunctionObject, w_obj)
240    return cfunction.ml.c_ml_meth
241
242@cpython_api([PyObject], PyObject)
243def PyStaticMethod_New(space, w_func):
244    return space.wrap(StaticMethod(w_func))
245
246@cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject)
247def PyDescr_NewMethod(space, w_type, method):
248    return space.wrap(W_PyCMethodObject(space, method, w_type))
249
250def PyDescr_NewWrapper(space, pto, method_name, wrapper_func, wrapper_func_kwds,
251                       doc, func):
252    # not exactly the API sig
253    return space.wrap(W_PyCWrapperObject(space, pto, method_name,
254        wrapper_func, wrapper_func_kwds, doc, func))
255
256@cpython_api([lltype.Ptr(PyMethodDef), PyObject, CONST_STRING], PyObject)
257def Py_FindMethod(space, table, w_obj, name_ptr):
258    """Return a bound method object for an extension type implemented in C.  This
259    can be useful in the implementation of a tp_getattro or
260    tp_getattr handler that does not use the
261    PyObject_GenericGetAttr() function."""
262    # XXX handle __doc__
263
264    name = rffi.charp2str(name_ptr)
265    methods = rffi.cast(rffi.CArrayPtr(PyMethodDef), table)
266    method_list_w = []
267
268    if methods:
269        i = -1
270        while True:
271            i = i + 1
272            method = methods[i]
273            if not method.c_ml_name: break
274            if name == "__methods__":
275                method_list_w.append(space.wrap(rffi.charp2str(method.c_ml_name)))
276            elif rffi.charp2str(method.c_ml_name) == name: # XXX expensive copying
277                return space.wrap(W_PyCFunctionObject(space, method, w_obj))
278    if name == "__methods__":
279        return space.newlist(method_list_w)
280    raise OperationError(space.w_AttributeError, space.wrap(name))
281