PageRenderTime 40ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_weakref/interp__weakref.py

https://bitbucket.org/pypy/pypy/
Python | 375 lines | 323 code | 36 blank | 16 comment | 46 complexity | c85d8ad73c6905ef34e3fee106b8270e MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from pypy.interpreter.baseobjspace import W_Root
  3. from pypy.interpreter.error import oefmt
  4. from pypy.interpreter.gateway import interp2app, ObjSpace
  5. from pypy.interpreter.typedef import TypeDef
  6. from pypy.interpreter.executioncontext import AsyncAction, report_error
  7. from rpython.rlib import jit, rgc
  8. from rpython.rlib.rshrinklist import AbstractShrinkList
  9. from rpython.rlib.objectmodel import specialize
  10. from rpython.rlib.rweakref import dead_ref
  11. import weakref
  12. class WRefShrinkList(AbstractShrinkList):
  13. def must_keep(self, wref):
  14. return wref() is not None
  15. class WeakrefLifeline(W_Root):
  16. typedef = None
  17. cached_weakref = None
  18. cached_proxy = None
  19. other_refs_weak = None
  20. has_callbacks = False
  21. def __init__(self, space):
  22. self.space = space
  23. def append_wref_to(self, w_ref):
  24. if self.other_refs_weak is None:
  25. self.other_refs_weak = WRefShrinkList()
  26. self.other_refs_weak.append(weakref.ref(w_ref))
  27. @specialize.arg(1)
  28. def traverse(self, callback, arg=None):
  29. if self.cached_weakref is not None:
  30. arg = callback(self, self.cached_weakref, arg)
  31. if self.cached_proxy is not None:
  32. arg = callback(self, self.cached_proxy, arg)
  33. if self.other_refs_weak is not None:
  34. for ref_w_ref in self.other_refs_weak.items():
  35. arg = callback(self, ref_w_ref, arg)
  36. return arg
  37. def _clear_wref(self, wref, _):
  38. w_ref = wref()
  39. if w_ref is not None:
  40. w_ref.clear()
  41. def clear_all_weakrefs(self):
  42. """Clear all weakrefs. This is called when an app-level object has
  43. a __del__, just before the app-level __del__ method is called.
  44. """
  45. self.traverse(WeakrefLifeline._clear_wref)
  46. # Note that for no particular reason other than convenience,
  47. # weakref callbacks are not invoked eagerly here. They are
  48. # invoked by self.__del__() anyway.
  49. @jit.dont_look_inside
  50. def get_or_make_weakref(self, w_subtype, w_obj):
  51. space = self.space
  52. w_weakreftype = space.gettypeobject(W_Weakref.typedef)
  53. #
  54. if space.is_w(w_weakreftype, w_subtype):
  55. if self.cached_weakref is not None:
  56. w_cached = self.cached_weakref()
  57. if w_cached is not None:
  58. return w_cached
  59. w_ref = W_Weakref(space, w_obj, None)
  60. self.cached_weakref = weakref.ref(w_ref)
  61. else:
  62. # subclass: cannot cache
  63. w_ref = space.allocate_instance(W_Weakref, w_subtype)
  64. W_Weakref.__init__(w_ref, space, w_obj, None)
  65. self.append_wref_to(w_ref)
  66. return w_ref
  67. @jit.dont_look_inside
  68. def get_or_make_proxy(self, w_obj):
  69. space = self.space
  70. if self.cached_proxy is not None:
  71. w_cached = self.cached_proxy()
  72. if w_cached is not None:
  73. return w_cached
  74. if space.is_true(space.callable(w_obj)):
  75. w_proxy = W_CallableProxy(space, w_obj, None)
  76. else:
  77. w_proxy = W_Proxy(space, w_obj, None)
  78. self.cached_proxy = weakref.ref(w_proxy)
  79. return w_proxy
  80. def get_any_weakref(self, space):
  81. if self.cached_weakref is not None:
  82. w_ref = self.cached_weakref()
  83. if w_ref is not None:
  84. return w_ref
  85. if self.other_refs_weak is not None:
  86. w_weakreftype = space.gettypeobject(W_Weakref.typedef)
  87. for wref in self.other_refs_weak.items():
  88. w_ref = wref()
  89. if (w_ref is not None and space.isinstance_w(w_ref, w_weakreftype)):
  90. return w_ref
  91. return space.w_None
  92. def enable_callbacks(self):
  93. if not self.has_callbacks:
  94. self.space.finalizer_queue.register_finalizer(self)
  95. self.has_callbacks = True
  96. @jit.dont_look_inside
  97. def make_weakref_with_callback(self, w_subtype, w_obj, w_callable):
  98. space = self.space
  99. w_ref = space.allocate_instance(W_Weakref, w_subtype)
  100. W_Weakref.__init__(w_ref, space, w_obj, w_callable)
  101. self.append_wref_to(w_ref)
  102. self.enable_callbacks()
  103. return w_ref
  104. @jit.dont_look_inside
  105. def make_proxy_with_callback(self, w_obj, w_callable):
  106. space = self.space
  107. if space.is_true(space.callable(w_obj)):
  108. w_proxy = W_CallableProxy(space, w_obj, w_callable)
  109. else:
  110. w_proxy = W_Proxy(space, w_obj, w_callable)
  111. self.append_wref_to(w_proxy)
  112. self.enable_callbacks()
  113. return w_proxy
  114. def _finalize_(self):
  115. """This is called at the end, if enable_callbacks() was invoked.
  116. It activates the callbacks.
  117. """
  118. if self.other_refs_weak is None:
  119. return
  120. #
  121. # If this is set, then we're in the 'gc.disable()' mode. In that
  122. # case, don't invoke the callbacks now.
  123. if self.space.user_del_action.gc_disabled(self):
  124. return
  125. #
  126. items = self.other_refs_weak.items()
  127. self.other_refs_weak = None
  128. for i in range(len(items)-1, -1, -1):
  129. w_ref = items[i]()
  130. if w_ref is not None and w_ref.w_callable is not None:
  131. try:
  132. w_ref.activate_callback()
  133. except Exception as e:
  134. report_error(self.space, e,
  135. "weakref callback ", w_ref.w_callable)
  136. # ____________________________________________________________
  137. class W_WeakrefBase(W_Root):
  138. def __init__(self, space, w_obj, w_callable):
  139. assert w_callable is not space.w_None # should be really None
  140. self.space = space
  141. assert w_obj is not None
  142. self.w_obj_weak = weakref.ref(w_obj)
  143. self.w_callable = w_callable
  144. @jit.dont_look_inside
  145. def dereference(self):
  146. w_obj = self.w_obj_weak()
  147. return w_obj
  148. def clear(self):
  149. self.w_obj_weak = dead_ref
  150. def activate_callback(self):
  151. self.space.call_function(self.w_callable, self)
  152. def descr__repr__(self, space):
  153. w_obj = self.dereference()
  154. if w_obj is None:
  155. state = '; dead'
  156. else:
  157. typename = space.type(w_obj).getname(space)
  158. objname = w_obj.getname(space)
  159. if objname and objname != '?':
  160. state = "; to '%s' (%s)" % (typename, objname)
  161. else:
  162. state = "; to '%s'" % (typename,)
  163. return self.getrepr(space, self.typedef.name, state)
  164. class W_Weakref(W_WeakrefBase):
  165. def __init__(self, space, w_obj, w_callable):
  166. W_WeakrefBase.__init__(self, space, w_obj, w_callable)
  167. self.w_hash = None
  168. def descr__init__weakref(self, space, w_obj, w_callable=None,
  169. __args__=None):
  170. if __args__.arguments_w:
  171. raise oefmt(space.w_TypeError,
  172. "__init__ expected at most 2 arguments")
  173. def descr_hash(self):
  174. if self.w_hash is not None:
  175. return self.w_hash
  176. w_obj = self.dereference()
  177. if w_obj is None:
  178. raise oefmt(self.space.w_TypeError, "weak object has gone away")
  179. self.w_hash = self.space.hash(w_obj)
  180. return self.w_hash
  181. def descr_call(self):
  182. w_obj = self.dereference()
  183. if w_obj is None:
  184. return self.space.w_None
  185. return w_obj
  186. def descr__eq__(self, space, w_ref2):
  187. if not isinstance(w_ref2, W_Weakref):
  188. return space.w_NotImplemented
  189. ref1 = self
  190. ref2 = w_ref2
  191. w_obj1 = ref1.dereference()
  192. w_obj2 = ref2.dereference()
  193. if w_obj1 is None or w_obj2 is None:
  194. return space.is_(ref1, ref2)
  195. return space.eq(w_obj1, w_obj2)
  196. def descr__ne__(self, space, w_ref2):
  197. return space.not_(space.eq(self, w_ref2))
  198. def getlifeline(space, w_obj):
  199. lifeline = w_obj.getweakref()
  200. if lifeline is None:
  201. lifeline = WeakrefLifeline(space)
  202. w_obj.setweakref(space, lifeline)
  203. return lifeline
  204. def descr__new__weakref(space, w_subtype, w_obj, w_callable=None,
  205. __args__=None):
  206. if __args__.arguments_w:
  207. raise oefmt(space.w_TypeError, "__new__ expected at most 2 arguments")
  208. lifeline = getlifeline(space, w_obj)
  209. if space.is_none(w_callable):
  210. return lifeline.get_or_make_weakref(w_subtype, w_obj)
  211. else:
  212. return lifeline.make_weakref_with_callback(w_subtype, w_obj, w_callable)
  213. W_Weakref.typedef = TypeDef("weakref",
  214. __doc__ = """A weak reference to an object 'obj'. A 'callback' can be given,
  215. which is called with 'obj' as an argument when it is about to be finalized.""",
  216. __new__ = interp2app(descr__new__weakref),
  217. __init__ = interp2app(W_Weakref.descr__init__weakref),
  218. __eq__ = interp2app(W_Weakref.descr__eq__),
  219. __ne__ = interp2app(W_Weakref.descr__ne__),
  220. __hash__ = interp2app(W_Weakref.descr_hash),
  221. __call__ = interp2app(W_Weakref.descr_call),
  222. __repr__ = interp2app(W_WeakrefBase.descr__repr__),
  223. )
  224. def _weakref_count(lifeline, wref, count):
  225. if wref() is not None:
  226. count += 1
  227. return count
  228. def getweakrefcount(space, w_obj):
  229. """Return the number of weak references to 'obj'."""
  230. lifeline = w_obj.getweakref()
  231. if lifeline is None:
  232. return space.wrap(0)
  233. else:
  234. result = lifeline.traverse(_weakref_count, 0)
  235. return space.wrap(result)
  236. def _get_weakrefs(lifeline, wref, result):
  237. w_ref = wref()
  238. if w_ref is not None:
  239. result.append(w_ref)
  240. return result
  241. def getweakrefs(space, w_obj):
  242. """Return a list of all weak reference objects that point to 'obj'."""
  243. result = []
  244. lifeline = w_obj.getweakref()
  245. if lifeline is not None:
  246. lifeline.traverse(_get_weakrefs, result)
  247. return space.newlist(result)
  248. #_________________________________________________________________
  249. # Proxy
  250. class W_Proxy(W_WeakrefBase):
  251. def descr__hash__(self, space):
  252. raise oefmt(space.w_TypeError, "unhashable type")
  253. class W_CallableProxy(W_Proxy):
  254. def descr__call__(self, space, __args__):
  255. w_obj = force(space, self)
  256. return space.call_args(w_obj, __args__)
  257. def proxy(space, w_obj, w_callable=None):
  258. """Create a proxy object that weakly references 'obj'.
  259. 'callback', if given, is called with the proxy as an argument when 'obj'
  260. is about to be finalized."""
  261. lifeline = getlifeline(space, w_obj)
  262. if space.is_none(w_callable):
  263. return lifeline.get_or_make_proxy(w_obj)
  264. else:
  265. return lifeline.make_proxy_with_callback(w_obj, w_callable)
  266. def descr__new__proxy(space, w_subtype, w_obj, w_callable=None):
  267. raise oefmt(space.w_TypeError, "cannot create 'weakproxy' instances")
  268. def descr__new__callableproxy(space, w_subtype, w_obj, w_callable=None):
  269. raise oefmt(space.w_TypeError,
  270. "cannot create 'weakcallableproxy' instances")
  271. def force(space, proxy):
  272. if not isinstance(proxy, W_Proxy):
  273. return proxy
  274. w_obj = proxy.dereference()
  275. if w_obj is None:
  276. raise oefmt(space.w_ReferenceError,
  277. "weakly referenced object no longer exists")
  278. return w_obj
  279. proxy_typedef_dict = {}
  280. callable_proxy_typedef_dict = {}
  281. special_ops = {'repr': True, 'hash': True}
  282. for opname, _, arity, special_methods in ObjSpace.MethodTable:
  283. if opname in special_ops or not special_methods:
  284. continue
  285. nonspaceargs = ", ".join(["w_obj%s" % i for i in range(arity)])
  286. code = "def func(space, %s):\n '''%s'''\n" % (nonspaceargs, opname)
  287. assert arity >= len(special_methods)
  288. forcing_count = len(special_methods)
  289. if opname.startswith('inplace_'):
  290. assert arity == 2
  291. forcing_count = arity
  292. for i in range(forcing_count):
  293. code += " w_obj%s = force(space, w_obj%s)\n" % (i, i)
  294. code += " return space.%s(%s)" % (opname, nonspaceargs)
  295. exec py.code.Source(code).compile()
  296. func.func_name = opname
  297. for special_method in special_methods:
  298. proxy_typedef_dict[special_method] = interp2app(func)
  299. callable_proxy_typedef_dict[special_method] = interp2app(func)
  300. # __unicode__ is not yet a space operation
  301. def proxy_unicode(space, w_obj):
  302. w_obj = force(space, w_obj)
  303. return space.call_method(w_obj, '__unicode__')
  304. proxy_typedef_dict['__unicode__'] = interp2app(proxy_unicode)
  305. callable_proxy_typedef_dict['__unicode__'] = interp2app(proxy_unicode)
  306. W_Proxy.typedef = TypeDef("weakproxy",
  307. __new__ = interp2app(descr__new__proxy),
  308. __hash__ = interp2app(W_Proxy.descr__hash__),
  309. __repr__ = interp2app(W_WeakrefBase.descr__repr__),
  310. **proxy_typedef_dict)
  311. W_Proxy.typedef.acceptable_as_base_class = False
  312. W_CallableProxy.typedef = TypeDef("weakcallableproxy",
  313. __new__ = interp2app(descr__new__callableproxy),
  314. __hash__ = interp2app(W_Proxy.descr__hash__),
  315. __repr__ = interp2app(W_WeakrefBase.descr__repr__),
  316. __call__ = interp2app(W_CallableProxy.descr__call__),
  317. **callable_proxy_typedef_dict)
  318. W_CallableProxy.typedef.acceptable_as_base_class = False