PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/objspace/std/stdtypedef.py

https://bitbucket.org/sonwell/pypy
Python | 297 lines | 265 code | 20 blank | 12 comment | 28 complexity | 68cb339d77a444465420150203f46cec MD5 | raw file
  1. from pypy.interpreter import gateway, baseobjspace, argument
  2. from pypy.interpreter.error import OperationError, operationerrfmt
  3. from pypy.interpreter.typedef import TypeDef, GetSetProperty, Member
  4. from pypy.interpreter.typedef import descr_get_dict, descr_set_dict
  5. from pypy.interpreter.typedef import descr_del_dict
  6. from pypy.interpreter.baseobjspace import SpaceCache
  7. from pypy.objspace.std import model
  8. from pypy.objspace.std.model import StdObjSpaceMultiMethod
  9. from pypy.objspace.std.multimethod import FailedToImplement
  10. from pypy.rlib import jit
  11. from pypy.tool.sourcetools import compile2
  12. __all__ = ['StdTypeDef', 'SMM']
  13. SMM = StdObjSpaceMultiMethod
  14. class StdTypeDef(TypeDef):
  15. def __init__(self, __name, __base=None, **rawdict):
  16. "NOT_RPYTHON: initialization-time only."
  17. TypeDef.__init__(self, __name, __base, **rawdict)
  18. self.any = type("W_Any"+__name.title(), (baseobjspace.W_Root,), {'typedef': self})
  19. self.local_multimethods = []
  20. def registermethods(self, namespace):
  21. "NOT_RPYTHON: initialization-time only."
  22. self.local_multimethods += hack_out_multimethods(namespace)
  23. @jit.unroll_safe
  24. def issubtypedef(a, b):
  25. from pypy.objspace.std.objecttype import object_typedef
  26. if b is object_typedef:
  27. return True
  28. if a is None:
  29. return False
  30. if a is b:
  31. return True
  32. for a1 in a.bases:
  33. if issubtypedef(a1, b):
  34. return True
  35. return False
  36. std_dict_descr = GetSetProperty(descr_get_dict, descr_set_dict, descr_del_dict,
  37. doc="dictionary for instance variables (if defined)")
  38. std_dict_descr.name = '__dict__'
  39. # ____________________________________________________________
  40. #
  41. # All the code below fishes from the multimethod registration tables
  42. # the descriptors to put into the W_TypeObjects.
  43. #
  44. class TypeCache(SpaceCache):
  45. def build(cache, typedef):
  46. "NOT_RPYTHON: initialization-time only."
  47. # build a W_TypeObject from this StdTypeDef
  48. from pypy.objspace.std.typeobject import W_TypeObject
  49. from pypy.objspace.std.objecttype import object_typedef
  50. space = cache.space
  51. w = space.wrap
  52. rawdict = typedef.rawdict
  53. lazyloaders = {}
  54. if isinstance(typedef, StdTypeDef):
  55. # get all the sliced multimethods
  56. multimethods = slicemultimethods(space, typedef)
  57. for name, loader in multimethods.items():
  58. if name in rawdict:
  59. # the name specified in the rawdict has priority
  60. continue
  61. assert name not in lazyloaders, (
  62. 'name clash: %s in %s.lazyloaders' % (name, typedef.name))
  63. lazyloaders[name] = loader
  64. # compute the bases
  65. if typedef is object_typedef:
  66. bases_w = []
  67. else:
  68. bases = typedef.bases or [object_typedef]
  69. bases_w = [space.gettypeobject(base) for base in bases]
  70. # wrap everything
  71. dict_w = {}
  72. for descrname, descrvalue in rawdict.items():
  73. dict_w[descrname] = w(descrvalue)
  74. if typedef.applevel_subclasses_base is not None:
  75. overridetypedef = typedef.applevel_subclasses_base.typedef
  76. else:
  77. overridetypedef = typedef
  78. w_type = W_TypeObject(space, typedef.name, bases_w, dict_w,
  79. overridetypedef=overridetypedef)
  80. if typedef is not overridetypedef:
  81. w_type.w_doc = space.wrap(typedef.doc)
  82. w_type.lazyloaders = lazyloaders
  83. return w_type
  84. def ready(self, w_type):
  85. w_type.ready()
  86. def hack_out_multimethods(ns):
  87. "NOT_RPYTHON: initialization-time only."
  88. result = []
  89. seen = {}
  90. for value in ns.itervalues():
  91. if isinstance(value, StdObjSpaceMultiMethod):
  92. if value.name in seen:
  93. raise Exception("duplicate multimethod name %r" %
  94. (value.name,))
  95. seen[value.name] = True
  96. result.append(value)
  97. return result
  98. def is_relevant_for_slice(target_type, typedef):
  99. targettypedef = getattr(target_type, 'typedef', None)
  100. if targettypedef == typedef:
  101. return True
  102. method = getattr(target_type, "is_implementation_for", lambda t: False)
  103. return method(typedef)
  104. def sliced_typeorders(typeorder, multimethod, typedef, i, local=False):
  105. """NOT_RPYTHON"""
  106. list_of_typeorders = [typeorder] * multimethod.arity
  107. prefix = '_mm_' + multimethod.name
  108. if not local:
  109. # slice
  110. sliced_typeorder = {}
  111. for type, order in typeorder.items():
  112. thistypedef = getattr(type, 'typedef', None)
  113. if issubtypedef(thistypedef, typedef):
  114. lst = []
  115. for target_type, conversion in order:
  116. if is_relevant_for_slice(target_type, typedef):
  117. lst.append((target_type, conversion))
  118. sliced_typeorder[type] = lst
  119. list_of_typeorders[i] = sliced_typeorder
  120. prefix += '_%sS%d' % (typedef.name, i)
  121. else:
  122. prefix = typedef.name +'_mth'+prefix
  123. return prefix, list_of_typeorders
  124. def _gettypeerrormsg(nbargs):
  125. if nbargs > 1:
  126. plural = 's'
  127. else:
  128. plural = ''
  129. return "unsupported operand type%s for %%s: %s" % (
  130. plural, ', '.join(["'%s'"] * nbargs))
  131. _gettypeerrormsg._annspecialcase_ = 'specialize:memo'
  132. def _gettypenames(space, *args_w):
  133. if args_w:
  134. typename = space.type(args_w[-1]).getname(space)
  135. return _gettypenames(space, *args_w[:-1]) + (typename,)
  136. return ()
  137. _gettypenames._always_inline_ = True
  138. def gettypeerror(space, operatorsymbol, *args_w):
  139. msg = _gettypeerrormsg(len(args_w))
  140. type_names = _gettypenames(space, *args_w)
  141. return operationerrfmt(space.w_TypeError, msg,
  142. operatorsymbol, *type_names)
  143. def make_perform_trampoline(prefix, exprargs, expr, miniglobals, multimethod, selfindex=0,
  144. allow_NotImplemented_results=False):
  145. """NOT_RPYTHON"""
  146. # mess to figure out how to put a gateway around executing expr
  147. argnames = ['_%d'%(i+1) for i in range(multimethod.arity)]
  148. explicit_argnames = multimethod.extras.get('argnames', [])
  149. argnames[len(argnames)-len(explicit_argnames):] = explicit_argnames
  150. solid_arglist = ['w_'+name for name in argnames]
  151. wrapper_arglist = solid_arglist[:]
  152. if multimethod.extras.get('varargs_w', False):
  153. wrapper_arglist.append('args_w')
  154. if multimethod.extras.get('keywords', False):
  155. raise Exception, "no longer supported, use __args__"
  156. if multimethod.extras.get('general__args__', False):
  157. wrapper_arglist.append('__args__')
  158. wrapper_arglist += multimethod.extras.get('extra_args', ())
  159. miniglobals.update({ 'OperationError': OperationError,
  160. 'gettypeerror': gettypeerror})
  161. app_defaults = multimethod.extras.get('defaults', ())
  162. i = len(argnames) - len(app_defaults)
  163. wrapper_signature = wrapper_arglist[:]
  164. unwrap_spec_kwds = {}
  165. for app_default in app_defaults:
  166. name = wrapper_signature[i]
  167. unwrap_spec_kwds[name] = gateway.WrappedDefault(app_default)
  168. i += 1
  169. wrapper_signature.insert(0, wrapper_signature.pop(selfindex))
  170. wrapper_sig = ', '.join(wrapper_signature)
  171. src = []
  172. dest = []
  173. for wrapper_arg,expr_arg in zip(['space']+wrapper_arglist, exprargs):
  174. if wrapper_arg != expr_arg:
  175. src.append(wrapper_arg)
  176. dest.append(expr_arg)
  177. renaming = ', '.join(dest) +" = "+', '.join(src)
  178. if allow_NotImplemented_results and (len(multimethod.specialnames) > 1 or
  179. multimethod.name.startswith('inplace_')):
  180. # turn FailedToImplement into NotImplemented
  181. code = """def %s_perform_call(space, %s):
  182. %s
  183. try:
  184. return %s
  185. except FailedToImplement, e:
  186. if e.get_w_type(space) is not None:
  187. raise OperationError(e.w_type, e.get_w_value(space))
  188. else:
  189. return space.w_NotImplemented
  190. """ % (prefix, wrapper_sig, renaming, expr)
  191. else:
  192. # turn FailedToImplement into nice TypeErrors
  193. code = """def %s_perform_call(space, %s):
  194. %s
  195. try:
  196. w_res = %s
  197. except FailedToImplement, e:
  198. if e.get_w_type(space) is not None:
  199. raise OperationError(e.w_type, e.get_w_value(space))
  200. else:
  201. raise gettypeerror(space, %r, %s)
  202. if w_res is None:
  203. w_res = space.w_None
  204. return w_res
  205. """ % (prefix, wrapper_sig, renaming, expr,
  206. multimethod.operatorsymbol, ', '.join(solid_arglist))
  207. exec compile2(code, '', 'exec') in miniglobals
  208. func = miniglobals["%s_perform_call" % prefix]
  209. if unwrap_spec_kwds:
  210. func = gateway.unwrap_spec(**unwrap_spec_kwds)(func)
  211. return func
  212. def wrap_trampoline_in_gateway(func, methname, multimethod):
  213. """NOT_RPYTHON"""
  214. if 'doc' in multimethod.extras:
  215. func.__doc__ = multimethod.extras['doc']
  216. return gateway.interp2app(func, app_name=methname)
  217. def slicemultimethod(space, multimethod, typedef, result, local=False):
  218. """NOT_RPYTHON"""
  219. for i in range(len(multimethod.specialnames)):
  220. methname = multimethod.specialnames[i]
  221. if methname in result:
  222. # conflict between e.g. __lt__ and
  223. # __lt__-as-reversed-version-of-__gt__
  224. loader = result[methname]
  225. if loader.bound_position < i:
  226. continue
  227. def multimethod_loader(i=i, methname=methname):
  228. """NOT_RPYTHON"""
  229. prefix, list_of_typeorders = sliced_typeorders(
  230. space.model.typeorder, multimethod, typedef, i, local=local)
  231. exprargs, expr, miniglobals, fallback = multimethod.install(prefix, list_of_typeorders,
  232. baked_perform_call=False,
  233. base_typeorder=space.model.typeorder)
  234. if fallback:
  235. return None # skip empty multimethods
  236. trampoline = make_perform_trampoline(prefix, exprargs, expr, miniglobals,
  237. multimethod, i,
  238. allow_NotImplemented_results=True)
  239. gw = wrap_trampoline_in_gateway(trampoline, methname, multimethod)
  240. return space.wrap(gw)
  241. multimethod_loader.bound_position = i # for the check above
  242. result[methname] = multimethod_loader
  243. def slicemultimethods(space, typedef):
  244. """NOT_RPYTHON"""
  245. result = {}
  246. # import and slice all multimethods of the MM container
  247. for multimethod in hack_out_multimethods(model.MM.__dict__):
  248. slicemultimethod(space, multimethod, typedef, result)
  249. # import all multimethods defined directly on the type without slicing
  250. for multimethod in typedef.local_multimethods:
  251. slicemultimethod(space, multimethod, typedef, result, local=True)
  252. return result
  253. def multimethods_defined_on(cls):
  254. """NOT_RPYTHON: enumerate the (multimethod, local_flag) for all the
  255. multimethods that have an implementation whose first typed argument
  256. is 'cls'.
  257. """
  258. typedef = cls.typedef
  259. for multimethod in hack_out_multimethods(model.MM.__dict__):
  260. if cls in multimethod.dispatch_tree:
  261. yield multimethod, False
  262. for multimethod in typedef.local_multimethods:
  263. yield multimethod, True