/pypy/module/cpyext/methodobject.py
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