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