PageRenderTime 197ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/External.LCA_RESTRICTED/Languages/IronPython/27/Lib/test/test_weakref.py

http://github.com/IronLanguages/main
Python | 1510 lines | 1101 code | 192 blank | 217 comment | 58 complexity | 461d32f5f4e6613854d13eec68829548 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. import gc
  2. import sys
  3. import unittest
  4. import UserList
  5. import weakref
  6. import operator
  7. import contextlib
  8. import copy
  9. from test import test_support
  10. # Used in ReferencesTestCase.test_ref_created_during_del() .
  11. ref_from_del = None
  12. class C:
  13. def method(self):
  14. pass
  15. class Callable:
  16. bar = None
  17. def __call__(self, x):
  18. self.bar = x
  19. def create_function():
  20. def f(): pass
  21. return f
  22. def create_bound_method():
  23. return C().method
  24. def create_unbound_method():
  25. return C.method
  26. class Object:
  27. def __init__(self, arg):
  28. self.arg = arg
  29. def __repr__(self):
  30. return "<Object %r>" % self.arg
  31. def __eq__(self, other):
  32. if isinstance(other, Object):
  33. return self.arg == other.arg
  34. return NotImplemented
  35. def __ne__(self, other):
  36. if isinstance(other, Object):
  37. return self.arg != other.arg
  38. return NotImplemented
  39. def __hash__(self):
  40. return hash(self.arg)
  41. class RefCycle:
  42. def __init__(self):
  43. self.cycle = self
  44. class TestBase(unittest.TestCase):
  45. def setUp(self):
  46. self.cbcalled = 0
  47. def callback(self, ref):
  48. self.cbcalled += 1
  49. class ReferencesTestCase(TestBase):
  50. def test_basic_ref(self):
  51. self.check_basic_ref(C)
  52. self.check_basic_ref(create_function)
  53. self.check_basic_ref(create_bound_method)
  54. self.check_basic_ref(create_unbound_method)
  55. # Just make sure the tp_repr handler doesn't raise an exception.
  56. # Live reference:
  57. o = C()
  58. wr = weakref.ref(o)
  59. repr(wr)
  60. # Dead reference:
  61. del o
  62. repr(wr)
  63. def test_basic_callback(self):
  64. self.check_basic_callback(C)
  65. self.check_basic_callback(create_function)
  66. self.check_basic_callback(create_bound_method)
  67. self.check_basic_callback(create_unbound_method)
  68. def test_multiple_callbacks(self):
  69. o = C()
  70. ref1 = weakref.ref(o, self.callback)
  71. ref2 = weakref.ref(o, self.callback)
  72. del o
  73. self.assertIsNone(ref1(), "expected reference to be invalidated")
  74. self.assertIsNone(ref2(), "expected reference to be invalidated")
  75. self.assertEqual(self.cbcalled, 2,
  76. "callback not called the right number of times")
  77. def test_multiple_selfref_callbacks(self):
  78. # Make sure all references are invalidated before callbacks are called
  79. #
  80. # What's important here is that we're using the first
  81. # reference in the callback invoked on the second reference
  82. # (the most recently created ref is cleaned up first). This
  83. # tests that all references to the object are invalidated
  84. # before any of the callbacks are invoked, so that we only
  85. # have one invocation of _weakref.c:cleanup_helper() active
  86. # for a particular object at a time.
  87. #
  88. def callback(object, self=self):
  89. self.ref()
  90. c = C()
  91. self.ref = weakref.ref(c, callback)
  92. ref1 = weakref.ref(c, callback)
  93. del c
  94. def test_constructor_kwargs(self):
  95. c = C()
  96. self.assertRaises(TypeError, weakref.ref, c, callback=None)
  97. def test_proxy_ref(self):
  98. o = C()
  99. o.bar = 1
  100. ref1 = weakref.proxy(o, self.callback)
  101. ref2 = weakref.proxy(o, self.callback)
  102. del o
  103. def check(proxy):
  104. proxy.bar
  105. self.assertRaises(weakref.ReferenceError, check, ref1)
  106. self.assertRaises(weakref.ReferenceError, check, ref2)
  107. self.assertRaises(weakref.ReferenceError, bool, weakref.proxy(C()))
  108. self.assertEqual(self.cbcalled, 2)
  109. def check_basic_ref(self, factory):
  110. o = factory()
  111. ref = weakref.ref(o)
  112. self.assertIsNotNone(ref(),
  113. "weak reference to live object should be live")
  114. o2 = ref()
  115. self.assertIs(o, o2,
  116. "<ref>() should return original object if live")
  117. def check_basic_callback(self, factory):
  118. self.cbcalled = 0
  119. o = factory()
  120. ref = weakref.ref(o, self.callback)
  121. del o
  122. self.assertEqual(self.cbcalled, 1,
  123. "callback did not properly set 'cbcalled'")
  124. self.assertIsNone(ref(),
  125. "ref2 should be dead after deleting object reference")
  126. def test_ref_reuse(self):
  127. o = C()
  128. ref1 = weakref.ref(o)
  129. # create a proxy to make sure that there's an intervening creation
  130. # between these two; it should make no difference
  131. proxy = weakref.proxy(o)
  132. ref2 = weakref.ref(o)
  133. self.assertIs(ref1, ref2,
  134. "reference object w/out callback should be re-used")
  135. o = C()
  136. proxy = weakref.proxy(o)
  137. ref1 = weakref.ref(o)
  138. ref2 = weakref.ref(o)
  139. self.assertIs(ref1, ref2,
  140. "reference object w/out callback should be re-used")
  141. self.assertEqual(weakref.getweakrefcount(o), 2,
  142. "wrong weak ref count for object")
  143. del proxy
  144. self.assertEqual(weakref.getweakrefcount(o), 1,
  145. "wrong weak ref count for object after deleting proxy")
  146. def test_proxy_reuse(self):
  147. o = C()
  148. proxy1 = weakref.proxy(o)
  149. ref = weakref.ref(o)
  150. proxy2 = weakref.proxy(o)
  151. self.assertIs(proxy1, proxy2,
  152. "proxy object w/out callback should have been re-used")
  153. def test_basic_proxy(self):
  154. o = C()
  155. self.check_proxy(o, weakref.proxy(o))
  156. L = UserList.UserList()
  157. p = weakref.proxy(L)
  158. self.assertFalse(p, "proxy for empty UserList should be false")
  159. p.append(12)
  160. self.assertEqual(len(L), 1)
  161. self.assertTrue(p, "proxy for non-empty UserList should be true")
  162. with test_support.check_py3k_warnings():
  163. p[:] = [2, 3]
  164. self.assertEqual(len(L), 2)
  165. self.assertEqual(len(p), 2)
  166. self.assertIn(3, p, "proxy didn't support __contains__() properly")
  167. p[1] = 5
  168. self.assertEqual(L[1], 5)
  169. self.assertEqual(p[1], 5)
  170. L2 = UserList.UserList(L)
  171. p2 = weakref.proxy(L2)
  172. self.assertEqual(p, p2)
  173. ## self.assertEqual(repr(L2), repr(p2))
  174. L3 = UserList.UserList(range(10))
  175. p3 = weakref.proxy(L3)
  176. with test_support.check_py3k_warnings():
  177. self.assertEqual(L3[:], p3[:])
  178. self.assertEqual(L3[5:], p3[5:])
  179. self.assertEqual(L3[:5], p3[:5])
  180. self.assertEqual(L3[2:5], p3[2:5])
  181. def test_proxy_unicode(self):
  182. # See bug 5037
  183. class C(object):
  184. def __str__(self):
  185. return "string"
  186. def __unicode__(self):
  187. return u"unicode"
  188. instance = C()
  189. self.assertIn("__unicode__", dir(weakref.proxy(instance)))
  190. self.assertEqual(unicode(weakref.proxy(instance)), u"unicode")
  191. def test_proxy_index(self):
  192. class C:
  193. def __index__(self):
  194. return 10
  195. o = C()
  196. p = weakref.proxy(o)
  197. self.assertEqual(operator.index(p), 10)
  198. def test_proxy_div(self):
  199. class C:
  200. def __floordiv__(self, other):
  201. return 42
  202. def __ifloordiv__(self, other):
  203. return 21
  204. o = C()
  205. p = weakref.proxy(o)
  206. self.assertEqual(p // 5, 42)
  207. p //= 5
  208. self.assertEqual(p, 21)
  209. # The PyWeakref_* C API is documented as allowing either NULL or
  210. # None as the value for the callback, where either means "no
  211. # callback". The "no callback" ref and proxy objects are supposed
  212. # to be shared so long as they exist by all callers so long as
  213. # they are active. In Python 2.3.3 and earlier, this guarantee
  214. # was not honored, and was broken in different ways for
  215. # PyWeakref_NewRef() and PyWeakref_NewProxy(). (Two tests.)
  216. def test_shared_ref_without_callback(self):
  217. self.check_shared_without_callback(weakref.ref)
  218. def test_shared_proxy_without_callback(self):
  219. self.check_shared_without_callback(weakref.proxy)
  220. def check_shared_without_callback(self, makeref):
  221. o = Object(1)
  222. p1 = makeref(o, None)
  223. p2 = makeref(o, None)
  224. self.assertIs(p1, p2, "both callbacks were None in the C API")
  225. del p1, p2
  226. p1 = makeref(o)
  227. p2 = makeref(o, None)
  228. self.assertIs(p1, p2, "callbacks were NULL, None in the C API")
  229. del p1, p2
  230. p1 = makeref(o)
  231. p2 = makeref(o)
  232. self.assertIs(p1, p2, "both callbacks were NULL in the C API")
  233. del p1, p2
  234. p1 = makeref(o, None)
  235. p2 = makeref(o)
  236. self.assertIs(p1, p2, "callbacks were None, NULL in the C API")
  237. def test_callable_proxy(self):
  238. o = Callable()
  239. ref1 = weakref.proxy(o)
  240. self.check_proxy(o, ref1)
  241. self.assertIs(type(ref1), weakref.CallableProxyType,
  242. "proxy is not of callable type")
  243. ref1('twinkies!')
  244. self.assertEqual(o.bar, 'twinkies!',
  245. "call through proxy not passed through to original")
  246. ref1(x='Splat.')
  247. self.assertEqual(o.bar, 'Splat.',
  248. "call through proxy not passed through to original")
  249. # expect due to too few args
  250. self.assertRaises(TypeError, ref1)
  251. # expect due to too many args
  252. self.assertRaises(TypeError, ref1, 1, 2, 3)
  253. def check_proxy(self, o, proxy):
  254. o.foo = 1
  255. self.assertEqual(proxy.foo, 1,
  256. "proxy does not reflect attribute addition")
  257. o.foo = 2
  258. self.assertEqual(proxy.foo, 2,
  259. "proxy does not reflect attribute modification")
  260. del o.foo
  261. self.assertFalse(hasattr(proxy, 'foo'),
  262. "proxy does not reflect attribute removal")
  263. proxy.foo = 1
  264. self.assertEqual(o.foo, 1,
  265. "object does not reflect attribute addition via proxy")
  266. proxy.foo = 2
  267. self.assertEqual(o.foo, 2,
  268. "object does not reflect attribute modification via proxy")
  269. del proxy.foo
  270. self.assertFalse(hasattr(o, 'foo'),
  271. "object does not reflect attribute removal via proxy")
  272. def test_proxy_deletion(self):
  273. # Test clearing of SF bug #762891
  274. class Foo:
  275. result = None
  276. def __delitem__(self, accessor):
  277. self.result = accessor
  278. g = Foo()
  279. f = weakref.proxy(g)
  280. del f[0]
  281. self.assertEqual(f.result, 0)
  282. def test_proxy_bool(self):
  283. # Test clearing of SF bug #1170766
  284. class List(list): pass
  285. lyst = List()
  286. self.assertEqual(bool(weakref.proxy(lyst)), bool(lyst))
  287. def test_getweakrefcount(self):
  288. o = C()
  289. ref1 = weakref.ref(o)
  290. ref2 = weakref.ref(o, self.callback)
  291. self.assertEqual(weakref.getweakrefcount(o), 2,
  292. "got wrong number of weak reference objects")
  293. proxy1 = weakref.proxy(o)
  294. proxy2 = weakref.proxy(o, self.callback)
  295. self.assertEqual(weakref.getweakrefcount(o), 4,
  296. "got wrong number of weak reference objects")
  297. del ref1, ref2, proxy1, proxy2
  298. self.assertEqual(weakref.getweakrefcount(o), 0,
  299. "weak reference objects not unlinked from"
  300. " referent when discarded.")
  301. # assumes ints do not support weakrefs
  302. self.assertEqual(weakref.getweakrefcount(1), 0,
  303. "got wrong number of weak reference objects for int")
  304. def test_getweakrefs(self):
  305. o = C()
  306. ref1 = weakref.ref(o, self.callback)
  307. ref2 = weakref.ref(o, self.callback)
  308. del ref1
  309. self.assertEqual(weakref.getweakrefs(o), [ref2],
  310. "list of refs does not match")
  311. o = C()
  312. ref1 = weakref.ref(o, self.callback)
  313. ref2 = weakref.ref(o, self.callback)
  314. del ref2
  315. self.assertEqual(weakref.getweakrefs(o), [ref1],
  316. "list of refs does not match")
  317. del ref1
  318. self.assertEqual(weakref.getweakrefs(o), [],
  319. "list of refs not cleared")
  320. # assumes ints do not support weakrefs
  321. self.assertEqual(weakref.getweakrefs(1), [],
  322. "list of refs does not match for int")
  323. def test_newstyle_number_ops(self):
  324. class F(float):
  325. pass
  326. f = F(2.0)
  327. p = weakref.proxy(f)
  328. self.assertEqual(p + 1.0, 3.0)
  329. self.assertEqual(1.0 + p, 3.0) # this used to SEGV
  330. def test_callbacks_protected(self):
  331. # Callbacks protected from already-set exceptions?
  332. # Regression test for SF bug #478534.
  333. class BogusError(Exception):
  334. pass
  335. data = {}
  336. def remove(k):
  337. del data[k]
  338. def encapsulate():
  339. f = lambda : ()
  340. data[weakref.ref(f, remove)] = None
  341. raise BogusError
  342. try:
  343. encapsulate()
  344. except BogusError:
  345. pass
  346. else:
  347. self.fail("exception not properly restored")
  348. try:
  349. encapsulate()
  350. except BogusError:
  351. pass
  352. else:
  353. self.fail("exception not properly restored")
  354. def test_sf_bug_840829(self):
  355. # "weakref callbacks and gc corrupt memory"
  356. # subtype_dealloc erroneously exposed a new-style instance
  357. # already in the process of getting deallocated to gc,
  358. # causing double-deallocation if the instance had a weakref
  359. # callback that triggered gc.
  360. # If the bug exists, there probably won't be an obvious symptom
  361. # in a release build. In a debug build, a segfault will occur
  362. # when the second attempt to remove the instance from the "list
  363. # of all objects" occurs.
  364. import gc
  365. class C(object):
  366. pass
  367. c = C()
  368. wr = weakref.ref(c, lambda ignore: gc.collect())
  369. del c
  370. # There endeth the first part. It gets worse.
  371. del wr
  372. c1 = C()
  373. c1.i = C()
  374. wr = weakref.ref(c1.i, lambda ignore: gc.collect())
  375. c2 = C()
  376. c2.c1 = c1
  377. del c1 # still alive because c2 points to it
  378. # Now when subtype_dealloc gets called on c2, it's not enough just
  379. # that c2 is immune from gc while the weakref callbacks associated
  380. # with c2 execute (there are none in this 2nd half of the test, btw).
  381. # subtype_dealloc goes on to call the base classes' deallocs too,
  382. # so any gc triggered by weakref callbacks associated with anything
  383. # torn down by a base class dealloc can also trigger double
  384. # deallocation of c2.
  385. del c2
  386. def test_callback_in_cycle_1(self):
  387. import gc
  388. class J(object):
  389. pass
  390. class II(object):
  391. def acallback(self, ignore):
  392. self.J
  393. I = II()
  394. I.J = J
  395. I.wr = weakref.ref(J, I.acallback)
  396. # Now J and II are each in a self-cycle (as all new-style class
  397. # objects are, since their __mro__ points back to them). I holds
  398. # both a weak reference (I.wr) and a strong reference (I.J) to class
  399. # J. I is also in a cycle (I.wr points to a weakref that references
  400. # I.acallback). When we del these three, they all become trash, but
  401. # the cycles prevent any of them from getting cleaned up immediately.
  402. # Instead they have to wait for cyclic gc to deduce that they're
  403. # trash.
  404. #
  405. # gc used to call tp_clear on all of them, and the order in which
  406. # it does that is pretty accidental. The exact order in which we
  407. # built up these things manages to provoke gc into running tp_clear
  408. # in just the right order (I last). Calling tp_clear on II leaves
  409. # behind an insane class object (its __mro__ becomes NULL). Calling
  410. # tp_clear on J breaks its self-cycle, but J doesn't get deleted
  411. # just then because of the strong reference from I.J. Calling
  412. # tp_clear on I starts to clear I's __dict__, and just happens to
  413. # clear I.J first -- I.wr is still intact. That removes the last
  414. # reference to J, which triggers the weakref callback. The callback
  415. # tries to do "self.J", and instances of new-style classes look up
  416. # attributes ("J") in the class dict first. The class (II) wants to
  417. # search II.__mro__, but that's NULL. The result was a segfault in
  418. # a release build, and an assert failure in a debug build.
  419. del I, J, II
  420. gc.collect()
  421. def test_callback_in_cycle_2(self):
  422. import gc
  423. # This is just like test_callback_in_cycle_1, except that II is an
  424. # old-style class. The symptom is different then: an instance of an
  425. # old-style class looks in its own __dict__ first. 'J' happens to
  426. # get cleared from I.__dict__ before 'wr', and 'J' was never in II's
  427. # __dict__, so the attribute isn't found. The difference is that
  428. # the old-style II doesn't have a NULL __mro__ (it doesn't have any
  429. # __mro__), so no segfault occurs. Instead it got:
  430. # test_callback_in_cycle_2 (__main__.ReferencesTestCase) ...
  431. # Exception exceptions.AttributeError:
  432. # "II instance has no attribute 'J'" in <bound method II.acallback
  433. # of <?.II instance at 0x00B9B4B8>> ignored
  434. class J(object):
  435. pass
  436. class II:
  437. def acallback(self, ignore):
  438. self.J
  439. I = II()
  440. I.J = J
  441. I.wr = weakref.ref(J, I.acallback)
  442. del I, J, II
  443. gc.collect()
  444. def test_callback_in_cycle_3(self):
  445. import gc
  446. # This one broke the first patch that fixed the last two. In this
  447. # case, the objects reachable from the callback aren't also reachable
  448. # from the object (c1) *triggering* the callback: you can get to
  449. # c1 from c2, but not vice-versa. The result was that c2's __dict__
  450. # got tp_clear'ed by the time the c2.cb callback got invoked.
  451. class C:
  452. def cb(self, ignore):
  453. self.me
  454. self.c1
  455. self.wr
  456. c1, c2 = C(), C()
  457. c2.me = c2
  458. c2.c1 = c1
  459. c2.wr = weakref.ref(c1, c2.cb)
  460. del c1, c2
  461. gc.collect()
  462. def test_callback_in_cycle_4(self):
  463. import gc
  464. # Like test_callback_in_cycle_3, except c2 and c1 have different
  465. # classes. c2's class (C) isn't reachable from c1 then, so protecting
  466. # objects reachable from the dying object (c1) isn't enough to stop
  467. # c2's class (C) from getting tp_clear'ed before c2.cb is invoked.
  468. # The result was a segfault (C.__mro__ was NULL when the callback
  469. # tried to look up self.me).
  470. class C(object):
  471. def cb(self, ignore):
  472. self.me
  473. self.c1
  474. self.wr
  475. class D:
  476. pass
  477. c1, c2 = D(), C()
  478. c2.me = c2
  479. c2.c1 = c1
  480. c2.wr = weakref.ref(c1, c2.cb)
  481. del c1, c2, C, D
  482. gc.collect()
  483. def test_callback_in_cycle_resurrection(self):
  484. import gc
  485. # Do something nasty in a weakref callback: resurrect objects
  486. # from dead cycles. For this to be attempted, the weakref and
  487. # its callback must also be part of the cyclic trash (else the
  488. # objects reachable via the callback couldn't be in cyclic trash
  489. # to begin with -- the callback would act like an external root).
  490. # But gc clears trash weakrefs with callbacks early now, which
  491. # disables the callbacks, so the callbacks shouldn't get called
  492. # at all (and so nothing actually gets resurrected).
  493. alist = []
  494. class C(object):
  495. def __init__(self, value):
  496. self.attribute = value
  497. def acallback(self, ignore):
  498. alist.append(self.c)
  499. c1, c2 = C(1), C(2)
  500. c1.c = c2
  501. c2.c = c1
  502. c1.wr = weakref.ref(c2, c1.acallback)
  503. c2.wr = weakref.ref(c1, c2.acallback)
  504. def C_went_away(ignore):
  505. alist.append("C went away")
  506. wr = weakref.ref(C, C_went_away)
  507. del c1, c2, C # make them all trash
  508. self.assertEqual(alist, []) # del isn't enough to reclaim anything
  509. gc.collect()
  510. # c1.wr and c2.wr were part of the cyclic trash, so should have
  511. # been cleared without their callbacks executing. OTOH, the weakref
  512. # to C is bound to a function local (wr), and wasn't trash, so that
  513. # callback should have been invoked when C went away.
  514. self.assertEqual(alist, ["C went away"])
  515. # The remaining weakref should be dead now (its callback ran).
  516. self.assertEqual(wr(), None)
  517. del alist[:]
  518. gc.collect()
  519. self.assertEqual(alist, [])
  520. def test_callbacks_on_callback(self):
  521. import gc
  522. # Set up weakref callbacks *on* weakref callbacks.
  523. alist = []
  524. def safe_callback(ignore):
  525. alist.append("safe_callback called")
  526. class C(object):
  527. def cb(self, ignore):
  528. alist.append("cb called")
  529. c, d = C(), C()
  530. c.other = d
  531. d.other = c
  532. callback = c.cb
  533. c.wr = weakref.ref(d, callback) # this won't trigger
  534. d.wr = weakref.ref(callback, d.cb) # ditto
  535. external_wr = weakref.ref(callback, safe_callback) # but this will
  536. self.assertIs(external_wr(), callback)
  537. # The weakrefs attached to c and d should get cleared, so that
  538. # C.cb is never called. But external_wr isn't part of the cyclic
  539. # trash, and no cyclic trash is reachable from it, so safe_callback
  540. # should get invoked when the bound method object callback (c.cb)
  541. # -- which is itself a callback, and also part of the cyclic trash --
  542. # gets reclaimed at the end of gc.
  543. del callback, c, d, C
  544. self.assertEqual(alist, []) # del isn't enough to clean up cycles
  545. gc.collect()
  546. self.assertEqual(alist, ["safe_callback called"])
  547. self.assertEqual(external_wr(), None)
  548. del alist[:]
  549. gc.collect()
  550. self.assertEqual(alist, [])
  551. def test_gc_during_ref_creation(self):
  552. self.check_gc_during_creation(weakref.ref)
  553. def test_gc_during_proxy_creation(self):
  554. self.check_gc_during_creation(weakref.proxy)
  555. def check_gc_during_creation(self, makeref):
  556. thresholds = gc.get_threshold()
  557. gc.set_threshold(1, 1, 1)
  558. gc.collect()
  559. class A:
  560. pass
  561. def callback(*args):
  562. pass
  563. referenced = A()
  564. a = A()
  565. a.a = a
  566. a.wr = makeref(referenced)
  567. try:
  568. # now make sure the object and the ref get labeled as
  569. # cyclic trash:
  570. a = A()
  571. weakref.ref(referenced, callback)
  572. finally:
  573. gc.set_threshold(*thresholds)
  574. def test_ref_created_during_del(self):
  575. # Bug #1377858
  576. # A weakref created in an object's __del__() would crash the
  577. # interpreter when the weakref was cleaned up since it would refer to
  578. # non-existent memory. This test should not segfault the interpreter.
  579. class Target(object):
  580. def __del__(self):
  581. global ref_from_del
  582. ref_from_del = weakref.ref(self)
  583. w = Target()
  584. def test_init(self):
  585. # Issue 3634
  586. # <weakref to class>.__init__() doesn't check errors correctly
  587. r = weakref.ref(Exception)
  588. self.assertRaises(TypeError, r.__init__, 0, 0, 0, 0, 0)
  589. # No exception should be raised here
  590. gc.collect()
  591. def test_classes(self):
  592. # Check that both old-style classes and new-style classes
  593. # are weakrefable.
  594. class A(object):
  595. pass
  596. class B:
  597. pass
  598. l = []
  599. weakref.ref(int)
  600. a = weakref.ref(A, l.append)
  601. A = None
  602. gc.collect()
  603. self.assertEqual(a(), None)
  604. self.assertEqual(l, [a])
  605. b = weakref.ref(B, l.append)
  606. B = None
  607. gc.collect()
  608. self.assertEqual(b(), None)
  609. self.assertEqual(l, [a, b])
  610. def test_equality(self):
  611. # Alive weakrefs defer equality testing to their underlying object.
  612. x = Object(1)
  613. y = Object(1)
  614. z = Object(2)
  615. a = weakref.ref(x)
  616. b = weakref.ref(y)
  617. c = weakref.ref(z)
  618. d = weakref.ref(x)
  619. # Note how we directly test the operators here, to stress both
  620. # __eq__ and __ne__.
  621. self.assertTrue(a == b)
  622. self.assertFalse(a != b)
  623. self.assertFalse(a == c)
  624. self.assertTrue(a != c)
  625. self.assertTrue(a == d)
  626. self.assertFalse(a != d)
  627. del x, y, z
  628. gc.collect()
  629. for r in a, b, c:
  630. # Sanity check
  631. self.assertIs(r(), None)
  632. # Dead weakrefs compare by identity: whether `a` and `d` are the
  633. # same weakref object is an implementation detail, since they pointed
  634. # to the same original object and didn't have a callback.
  635. # (see issue #16453).
  636. self.assertFalse(a == b)
  637. self.assertTrue(a != b)
  638. self.assertFalse(a == c)
  639. self.assertTrue(a != c)
  640. self.assertEqual(a == d, a is d)
  641. self.assertEqual(a != d, a is not d)
  642. def test_hashing(self):
  643. # Alive weakrefs hash the same as the underlying object
  644. x = Object(42)
  645. y = Object(42)
  646. a = weakref.ref(x)
  647. b = weakref.ref(y)
  648. self.assertEqual(hash(a), hash(42))
  649. del x, y
  650. gc.collect()
  651. # Dead weakrefs:
  652. # - retain their hash is they were hashed when alive;
  653. # - otherwise, cannot be hashed.
  654. self.assertEqual(hash(a), hash(42))
  655. self.assertRaises(TypeError, hash, b)
  656. def test_trashcan_16602(self):
  657. # Issue #16602: when a weakref's target was part of a long
  658. # deallocation chain, the trashcan mechanism could delay clearing
  659. # of the weakref and make the target object visible from outside
  660. # code even though its refcount had dropped to 0. A crash ensued.
  661. class C(object):
  662. def __init__(self, parent):
  663. if not parent:
  664. return
  665. wself = weakref.ref(self)
  666. def cb(wparent):
  667. o = wself()
  668. self.wparent = weakref.ref(parent, cb)
  669. d = weakref.WeakKeyDictionary()
  670. root = c = C(None)
  671. for n in range(100):
  672. d[c] = c = C(c)
  673. del root
  674. gc.collect()
  675. class SubclassableWeakrefTestCase(TestBase):
  676. def test_subclass_refs(self):
  677. class MyRef(weakref.ref):
  678. def __init__(self, ob, callback=None, value=42):
  679. self.value = value
  680. super(MyRef, self).__init__(ob, callback)
  681. def __call__(self):
  682. self.called = True
  683. return super(MyRef, self).__call__()
  684. o = Object("foo")
  685. mr = MyRef(o, value=24)
  686. self.assertIs(mr(), o)
  687. self.assertTrue(mr.called)
  688. self.assertEqual(mr.value, 24)
  689. del o
  690. self.assertIsNone(mr())
  691. self.assertTrue(mr.called)
  692. def test_subclass_refs_dont_replace_standard_refs(self):
  693. class MyRef(weakref.ref):
  694. pass
  695. o = Object(42)
  696. r1 = MyRef(o)
  697. r2 = weakref.ref(o)
  698. self.assertIsNot(r1, r2)
  699. self.assertEqual(weakref.getweakrefs(o), [r2, r1])
  700. self.assertEqual(weakref.getweakrefcount(o), 2)
  701. r3 = MyRef(o)
  702. self.assertEqual(weakref.getweakrefcount(o), 3)
  703. refs = weakref.getweakrefs(o)
  704. self.assertEqual(len(refs), 3)
  705. self.assertIs(r2, refs[0])
  706. self.assertIn(r1, refs[1:])
  707. self.assertIn(r3, refs[1:])
  708. def test_subclass_refs_dont_conflate_callbacks(self):
  709. class MyRef(weakref.ref):
  710. pass
  711. o = Object(42)
  712. r1 = MyRef(o, id)
  713. r2 = MyRef(o, str)
  714. self.assertIsNot(r1, r2)
  715. refs = weakref.getweakrefs(o)
  716. self.assertIn(r1, refs)
  717. self.assertIn(r2, refs)
  718. def test_subclass_refs_with_slots(self):
  719. class MyRef(weakref.ref):
  720. __slots__ = "slot1", "slot2"
  721. def __new__(type, ob, callback, slot1, slot2):
  722. return weakref.ref.__new__(type, ob, callback)
  723. def __init__(self, ob, callback, slot1, slot2):
  724. self.slot1 = slot1
  725. self.slot2 = slot2
  726. def meth(self):
  727. return self.slot1 + self.slot2
  728. o = Object(42)
  729. r = MyRef(o, None, "abc", "def")
  730. self.assertEqual(r.slot1, "abc")
  731. self.assertEqual(r.slot2, "def")
  732. self.assertEqual(r.meth(), "abcdef")
  733. self.assertFalse(hasattr(r, "__dict__"))
  734. def test_subclass_refs_with_cycle(self):
  735. # Bug #3110
  736. # An instance of a weakref subclass can have attributes.
  737. # If such a weakref holds the only strong reference to the object,
  738. # deleting the weakref will delete the object. In this case,
  739. # the callback must not be called, because the ref object is
  740. # being deleted.
  741. class MyRef(weakref.ref):
  742. pass
  743. # Use a local callback, for "regrtest -R::"
  744. # to detect refcounting problems
  745. def callback(w):
  746. self.cbcalled += 1
  747. o = C()
  748. r1 = MyRef(o, callback)
  749. r1.o = o
  750. del o
  751. del r1 # Used to crash here
  752. self.assertEqual(self.cbcalled, 0)
  753. # Same test, with two weakrefs to the same object
  754. # (since code paths are different)
  755. o = C()
  756. r1 = MyRef(o, callback)
  757. r2 = MyRef(o, callback)
  758. r1.r = r2
  759. r2.o = o
  760. del o
  761. del r2
  762. del r1 # Used to crash here
  763. self.assertEqual(self.cbcalled, 0)
  764. class MappingTestCase(TestBase):
  765. COUNT = 10
  766. def check_len_cycles(self, dict_type, cons):
  767. N = 20
  768. items = [RefCycle() for i in range(N)]
  769. dct = dict_type(cons(i, o) for i, o in enumerate(items))
  770. # Keep an iterator alive
  771. it = dct.iteritems()
  772. try:
  773. next(it)
  774. except StopIteration:
  775. pass
  776. del items
  777. gc.collect()
  778. n1 = len(dct)
  779. list(it)
  780. del it
  781. gc.collect()
  782. n2 = len(dct)
  783. # iteration should prevent garbage collection here
  784. # Note that this is a test on an implementation detail. The requirement
  785. # is only to provide stable iteration, not that the size of the container
  786. # stay fixed.
  787. self.assertEqual(n1, 20)
  788. #self.assertIn(n1, (0, 1))
  789. self.assertEqual(n2, 0)
  790. def test_weak_keyed_len_cycles(self):
  791. self.check_len_cycles(weakref.WeakKeyDictionary, lambda n, k: (k, n))
  792. def test_weak_valued_len_cycles(self):
  793. self.check_len_cycles(weakref.WeakValueDictionary, lambda n, k: (n, k))
  794. def check_len_race(self, dict_type, cons):
  795. # Extended sanity checks for len() in the face of cyclic collection
  796. self.addCleanup(gc.set_threshold, *gc.get_threshold())
  797. for th in range(1, 100):
  798. N = 20
  799. gc.collect(0)
  800. gc.set_threshold(th, th, th)
  801. items = [RefCycle() for i in range(N)]
  802. dct = dict_type(cons(o) for o in items)
  803. del items
  804. # All items will be collected at next garbage collection pass
  805. it = dct.iteritems()
  806. try:
  807. next(it)
  808. except StopIteration:
  809. pass
  810. n1 = len(dct)
  811. del it
  812. n2 = len(dct)
  813. self.assertGreaterEqual(n1, 0)
  814. self.assertLessEqual(n1, N)
  815. self.assertGreaterEqual(n2, 0)
  816. self.assertLessEqual(n2, n1)
  817. def test_weak_keyed_len_race(self):
  818. self.check_len_race(weakref.WeakKeyDictionary, lambda k: (k, 1))
  819. def test_weak_valued_len_race(self):
  820. self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k))
  821. def test_weak_values(self):
  822. #
  823. # This exercises d.copy(), d.items(), d[], del d[], len(d).
  824. #
  825. dict, objects = self.make_weak_valued_dict()
  826. for o in objects:
  827. self.assertEqual(weakref.getweakrefcount(o), 1,
  828. "wrong number of weak references to %r!" % o)
  829. self.assertIs(o, dict[o.arg],
  830. "wrong object returned by weak dict!")
  831. items1 = dict.items()
  832. items2 = dict.copy().items()
  833. items1.sort()
  834. items2.sort()
  835. self.assertEqual(items1, items2,
  836. "cloning of weak-valued dictionary did not work!")
  837. del items1, items2
  838. self.assertEqual(len(dict), self.COUNT)
  839. del objects[0]
  840. self.assertEqual(len(dict), (self.COUNT - 1),
  841. "deleting object did not cause dictionary update")
  842. del objects, o
  843. self.assertEqual(len(dict), 0,
  844. "deleting the values did not clear the dictionary")
  845. # regression on SF bug #447152:
  846. dict = weakref.WeakValueDictionary()
  847. self.assertRaises(KeyError, dict.__getitem__, 1)
  848. dict[2] = C()
  849. self.assertRaises(KeyError, dict.__getitem__, 2)
  850. def test_weak_keys(self):
  851. #
  852. # This exercises d.copy(), d.items(), d[] = v, d[], del d[],
  853. # len(d), in d.
  854. #
  855. dict, objects = self.make_weak_keyed_dict()
  856. for o in objects:
  857. self.assertEqual(weakref.getweakrefcount(o), 1,
  858. "wrong number of weak references to %r!" % o)
  859. self.assertIs(o.arg, dict[o],
  860. "wrong object returned by weak dict!")
  861. items1 = dict.items()
  862. items2 = dict.copy().items()
  863. self.assertEqual(set(items1), set(items2),
  864. "cloning of weak-keyed dictionary did not work!")
  865. del items1, items2
  866. self.assertEqual(len(dict), self.COUNT)
  867. del objects[0]
  868. self.assertEqual(len(dict), (self.COUNT - 1),
  869. "deleting object did not cause dictionary update")
  870. del objects, o
  871. self.assertEqual(len(dict), 0,
  872. "deleting the keys did not clear the dictionary")
  873. o = Object(42)
  874. dict[o] = "What is the meaning of the universe?"
  875. self.assertIn(o, dict)
  876. self.assertNotIn(34, dict)
  877. def test_weak_keyed_iters(self):
  878. dict, objects = self.make_weak_keyed_dict()
  879. self.check_iters(dict)
  880. # Test keyrefs()
  881. refs = dict.keyrefs()
  882. self.assertEqual(len(refs), len(objects))
  883. objects2 = list(objects)
  884. for wr in refs:
  885. ob = wr()
  886. self.assertIn(ob, dict)
  887. self.assertEqual(ob.arg, dict[ob])
  888. objects2.remove(ob)
  889. self.assertEqual(len(objects2), 0)
  890. # Test iterkeyrefs()
  891. objects2 = list(objects)
  892. self.assertEqual(len(list(dict.iterkeyrefs())), len(objects))
  893. for wr in dict.iterkeyrefs():
  894. ob = wr()
  895. self.assertIn(ob, dict)
  896. self.assertEqual(ob.arg, dict[ob])
  897. objects2.remove(ob)
  898. self.assertEqual(len(objects2), 0)
  899. def test_weak_valued_iters(self):
  900. dict, objects = self.make_weak_valued_dict()
  901. self.check_iters(dict)
  902. # Test valuerefs()
  903. refs = dict.valuerefs()
  904. self.assertEqual(len(refs), len(objects))
  905. objects2 = list(objects)
  906. for wr in refs:
  907. ob = wr()
  908. self.assertEqual(ob, dict[ob.arg])
  909. self.assertEqual(ob.arg, dict[ob.arg].arg)
  910. objects2.remove(ob)
  911. self.assertEqual(len(objects2), 0)
  912. # Test itervaluerefs()
  913. objects2 = list(objects)
  914. self.assertEqual(len(list(dict.itervaluerefs())), len(objects))
  915. for wr in dict.itervaluerefs():
  916. ob = wr()
  917. self.assertEqual(ob, dict[ob.arg])
  918. self.assertEqual(ob.arg, dict[ob.arg].arg)
  919. objects2.remove(ob)
  920. self.assertEqual(len(objects2), 0)
  921. def check_iters(self, dict):
  922. # item iterator:
  923. items = dict.items()
  924. for item in dict.iteritems():
  925. items.remove(item)
  926. self.assertEqual(len(items), 0, "iteritems() did not touch all items")
  927. # key iterator, via __iter__():
  928. keys = dict.keys()
  929. for k in dict:
  930. keys.remove(k)
  931. self.assertEqual(len(keys), 0, "__iter__() did not touch all keys")
  932. # key iterator, via iterkeys():
  933. keys = dict.keys()
  934. for k in dict.iterkeys():
  935. keys.remove(k)
  936. self.assertEqual(len(keys), 0, "iterkeys() did not touch all keys")
  937. # value iterator:
  938. values = dict.values()
  939. for v in dict.itervalues():
  940. values.remove(v)
  941. self.assertEqual(len(values), 0,
  942. "itervalues() did not touch all values")
  943. def check_weak_destroy_while_iterating(self, dict, objects, iter_name):
  944. n = len(dict)
  945. it = iter(getattr(dict, iter_name)())
  946. next(it) # Trigger internal iteration
  947. # Destroy an object
  948. del objects[-1]
  949. gc.collect() # just in case
  950. # We have removed either the first consumed object, or another one
  951. self.assertIn(len(list(it)), [len(objects), len(objects) - 1])
  952. del it
  953. # The removal has been committed
  954. self.assertEqual(len(dict), n - 1)
  955. def check_weak_destroy_and_mutate_while_iterating(self, dict, testcontext):
  956. # Check that we can explicitly mutate the weak dict without
  957. # interfering with delayed removal.
  958. # `testcontext` should create an iterator, destroy one of the
  959. # weakref'ed objects and then return a new key/value pair corresponding
  960. # to the destroyed object.
  961. with testcontext() as (k, v):
  962. self.assertFalse(k in dict)
  963. with testcontext() as (k, v):
  964. self.assertRaises(KeyError, dict.__delitem__, k)
  965. self.assertFalse(k in dict)
  966. with testcontext() as (k, v):
  967. self.assertRaises(KeyError, dict.pop, k)
  968. self.assertFalse(k in dict)
  969. with testcontext() as (k, v):
  970. dict[k] = v
  971. self.assertEqual(dict[k], v)
  972. ddict = copy.copy(dict)
  973. with testcontext() as (k, v):
  974. dict.update(ddict)
  975. self.assertEqual(dict, ddict)
  976. with testcontext() as (k, v):
  977. dict.clear()
  978. self.assertEqual(len(dict), 0)
  979. def test_weak_keys_destroy_while_iterating(self):
  980. # Issue #7105: iterators shouldn't crash when a key is implicitly removed
  981. dict, objects = self.make_weak_keyed_dict()
  982. self.check_weak_destroy_while_iterating(dict, objects, 'iterkeys')
  983. self.check_weak_destroy_while_iterating(dict, objects, 'iteritems')
  984. self.check_weak_destroy_while_iterating(dict, objects, 'itervalues')
  985. self.check_weak_destroy_while_iterating(dict, objects, 'iterkeyrefs')
  986. dict, objects = self.make_weak_keyed_dict()
  987. @contextlib.contextmanager
  988. def testcontext():
  989. try:
  990. it = iter(dict.iteritems())
  991. next(it)
  992. # Schedule a key/value for removal and recreate it
  993. v = objects.pop().arg
  994. gc.collect() # just in case
  995. yield Object(v), v
  996. finally:
  997. it = None # should commit all removals
  998. gc.collect()
  999. self.check_weak_destroy_and_mutate_while_iterating(dict, testcontext)
  1000. def test_weak_values_destroy_while_iterating(self):
  1001. # Issue #7105: iterators shouldn't crash when a key is implicitly removed
  1002. dict, objects = self.make_weak_valued_dict()
  1003. self.check_weak_destroy_while_iterating(dict, objects, 'iterkeys')
  1004. self.check_weak_destroy_while_iterating(dict, objects, 'iteritems')
  1005. self.check_weak_destroy_while_iterating(dict, objects, 'itervalues')
  1006. self.check_weak_destroy_while_iterating(dict, objects, 'itervaluerefs')
  1007. dict, objects = self.make_weak_valued_dict()
  1008. @contextlib.contextmanager
  1009. def testcontext():
  1010. try:
  1011. it = iter(dict.iteritems())
  1012. next(it)
  1013. # Schedule a key/value for removal and recreate it
  1014. k = objects.pop().arg
  1015. gc.collect() # just in case
  1016. yield k, Object(k)
  1017. finally:
  1018. it = None # should commit all removals
  1019. gc.collect()
  1020. self.check_weak_destroy_and_mutate_while_iterating(dict, testcontext)
  1021. def test_make_weak_keyed_dict_from_dict(self):
  1022. o = Object(3)
  1023. dict = weakref.WeakKeyDictionary({o:364})
  1024. self.assertEqual(dict[o], 364)
  1025. def test_make_weak_keyed_dict_from_weak_keyed_dict(self):
  1026. o = Object(3)
  1027. dict = weakref.WeakKeyDictionary({o:364})
  1028. dict2 = weakref.WeakKeyDictionary(dict)
  1029. self.assertEqual(dict[o], 364)
  1030. def make_weak_keyed_dict(self):
  1031. dict = weakref.WeakKeyDictionary()
  1032. objects = map(Object, range(self.COUNT))
  1033. for o in objects:
  1034. dict[o] = o.arg
  1035. return dict, objects
  1036. def test_make_weak_valued_dict_misc(self):
  1037. # errors
  1038. self.assertRaises(TypeError, weakref.WeakValueDictionary.__init__)
  1039. self.assertRaises(TypeError, weakref.WeakValueDictionary, {}, {})
  1040. self.assertRaises(TypeError, weakref.WeakValueDictionary, (), ())
  1041. # special keyword arguments
  1042. o = Object(3)
  1043. for kw in 'self', 'other', 'iterable':
  1044. d = weakref.WeakValueDictionary(**{kw: o})
  1045. self.assertEqual(list(d.keys()), [kw])
  1046. self.assertEqual(d[kw], o)
  1047. def make_weak_valued_dict(self):
  1048. dict = weakref.WeakValueDictionary()
  1049. objects = map(Object, range(self.COUNT))
  1050. for o in objects:
  1051. dict[o.arg] = o
  1052. return dict, objects
  1053. def check_popitem(self, klass, key1, value1, key2, value2):
  1054. weakdict = klass()
  1055. weakdict[key1] = value1
  1056. weakdict[key2] = value2
  1057. self.assertEqual(len(weakdict), 2)
  1058. k, v = weakdict.popitem()
  1059. self.assertEqual(len(weakdict), 1)
  1060. if k is key1:
  1061. self.assertIs(v, value1)
  1062. else:
  1063. self.assertIs(v, value2)
  1064. k, v = weakdict.popitem()
  1065. self.assertEqual(len(weakdict), 0)
  1066. if k is key1:
  1067. self.assertIs(v, value1)
  1068. else:
  1069. self.assertIs(v, value2)
  1070. def test_weak_valued_dict_popitem(self):
  1071. self.check_popitem(weakref.WeakValueDictionary,
  1072. "key1", C(), "key2", C())
  1073. def test_weak_keyed_dict_popitem(self):
  1074. self.check_popitem(weakref.WeakKeyDictionary,
  1075. C(), "value 1", C(), "value 2")
  1076. def check_setdefault(self, klass, key, value1, value2):
  1077. self.assertIsNot(value1, value2,
  1078. "invalid test"
  1079. " -- value parameters must be distinct objects")
  1080. weakdict = klass()
  1081. o = weakdict.setdefault(key, value1)
  1082. self.assertIs(o, value1)
  1083. self.assertIn(key, weakdict)
  1084. self.assertIs(weakdict.get(key), value1)
  1085. self.assertIs(weakdict[key], value1)
  1086. o = weakdict.setdefault(key, value2)
  1087. self.assertIs(o, value1)
  1088. self.assertIn(key, weakdict)
  1089. self.assertIs(weakdict.get(key), value1)
  1090. self.assertIs(weakdict[key], value1)
  1091. def test_weak_valued_dict_setdefault(self):
  1092. self.check_setdefault(weakref.WeakValueDictionary,
  1093. "key", C(), C())
  1094. def test_weak_keyed_dict_setdefault(self):
  1095. self.check_setdefault(weakref.WeakKeyDictionary,
  1096. C(), "value 1", "value 2")
  1097. def check_update(self, klass, dict):
  1098. #
  1099. # This exercises d.update(), len(d), d.keys(), in d,
  1100. # d.get(), d[].
  1101. #
  1102. weakdict = klass()
  1103. weakdict.update(dict)
  1104. self.assertEqual(len(weakdict), len(dict))
  1105. for k in weakdict.keys():
  1106. self.assertIn(k, dict,
  1107. "mysterious new key appeared in weak dict")
  1108. v = dict.get(k)
  1109. self.assertIs(v, weakdict[k])
  1110. self.assertIs(v, weakdict.get(k))
  1111. for k in dict.keys():
  1112. self.assertIn(k, weakdict,
  1113. "original key disappeared in weak dict")
  1114. v = dict[k]
  1115. self.assertIs(v, weakdict[k])
  1116. self.assertIs(v, weakdict.get(k))
  1117. def test_weak_valued_dict_update(self):
  1118. self.check_update(weakref.WeakValueDictionary,
  1119. {1: C(), 'a': C(), C(): C()})
  1120. # errors
  1121. self.assertRaises(TypeError, weakref.WeakValueDictionary.update)
  1122. d = weakref.WeakValueDictionary()
  1123. self.assertRaises(TypeError, d.update, {}, {})
  1124. self.assertRaises(TypeError, d.update, (), ())
  1125. self.assertEqual(list(d.keys()), [])
  1126. # special keyword arguments
  1127. o = Object(3)
  1128. for kw in 'self', 'dict', 'other', 'iterable':
  1129. d = weakref.WeakValueDictionary()
  1130. d.update(**{kw: o})
  1131. self.assertEqual(list(d.keys()), [kw])
  1132. self.assertEqual(d[kw], o)
  1133. def test_weak_keyed_dict_update(self):
  1134. self.check_update(weakref.WeakKeyDictionary,
  1135. {C(): 1, C(): 2, C(): 3})
  1136. def test_weak_keyed_delitem(self):
  1137. d = weakref.WeakKeyDictionary()
  1138. o1 = Object('1')
  1139. o2 = Object('2')
  1140. d[o1] = 'something'
  1141. d[o2] = 'something'
  1142. self.assertEqual(len(d), 2)
  1143. del d[o1]
  1144. self.assertEqual(len(d), 1)
  1145. self.assertEqual(d.keys(), [o2])
  1146. def test_weak_valued_delitem(self):
  1147. d = weakref.WeakValueDictionary()
  1148. o1 = Object('1')
  1149. o2 = Object('2')
  1150. d['something'] = o1
  1151. d['something else'] = o2
  1152. self.assertEqual(len(d), 2)
  1153. del d['something']
  1154. self.assertEqual(len(d), 1)
  1155. self.assertEqual(d.items(), [('something else', o2)])
  1156. def test_weak_keyed_bad_delitem(self):
  1157. d = weakref.WeakKeyDictionary()
  1158. o = Object('1')
  1159. # An attempt to delete an object that isn't there should raise
  1160. # KeyError. It didn't before 2.3.
  1161. self.assertRaises(KeyError, d.__delitem__, o)
  1162. self.assertRaises(KeyError, d.__getitem__, o)
  1163. # If a key isn't of a weakly referencable type, __getitem__ and
  1164. # __setitem__ raise TypeError. __delitem__ should too.
  1165. self.assertRaises(TypeError, d.__delitem__, 13)
  1166. self.assertRaises(TypeError, d.__getitem__, 13)
  1167. self.assertRaises(TypeError, d.__setitem__, 13, 13)
  1168. def test_weak_keyed_cascading_deletes(self):
  1169. # SF bug 742860. For some reason, before 2.3 __delitem__ iterated
  1170. # over the keys via self.data.iterkeys(). If things vanished from
  1171. # the dict during this (or got added), that caused a RuntimeError.
  1172. d = weakref.WeakKeyDictionary()
  1173. mutate = False
  1174. class C(object):
  1175. def __init__(self, i):
  1176. self.value = i
  1177. def __hash__(self):
  1178. return hash(self.value)
  1179. def __eq__(self, other):
  1180. if mutate:
  1181. # Side effect that mutates the dict, by removing the
  1182. # last strong reference to a key.
  1183. del objs[-1]
  1184. return self.value == other.value
  1185. objs = [C(i) for i in range(4)]
  1186. for o in objs:
  1187. d[o] = o.value
  1188. del o # now the only strong references to keys are in objs
  1189. # Find the order in which iterkeys sees the keys.
  1190. objs = d.keys()
  1191. # Reverse it, so that the iteration implementation of __delitem__
  1192. # has to keep looping to find the first object we delete.
  1193. objs.reverse()
  1194. # Turn on mutation in C.__eq__. The first time thru the loop,
  1195. # under the iterkeys() business the first comparison will delete
  1196. # the last item iterkeys() would see, and that causes a
  1197. # RuntimeError: dictionary changed size during iteration
  1198. # when the iterkeys() loop goes around to try comparing the next
  1199. # key. After this was fixed, it just deletes the last object *our*
  1200. # "for o in obj" loop would have gotten to.
  1201. mutate = True
  1202. count = 0
  1203. for o in objs:
  1204. count += 1
  1205. del d[o]
  1206. self.assertEqual(len(d), 0)
  1207. self.assertEqual(count, 2)
  1208. from test import mapping_tests
  1209. class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
  1210. """Check that WeakValueDictionary conforms to the mapping protocol"""
  1211. __ref = {"key1":Object(1), "key2":Object(2), "key3":Object(3)}
  1212. type2test = weakref.WeakValueDictionary
  1213. def _reference(self):
  1214. return self.__ref.copy()
  1215. class WeakKeyDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
  1216. """Check that WeakKeyDictionary conforms to the mapping protocol"""
  1217. __ref = {Object("key1"):1, Object("key2"):2, Object("key3"):3}
  1218. type2test = weakref.WeakKeyDictionary
  1219. def _reference(self):
  1220. return self.__ref.copy()
  1221. libreftest = """ Doctest for examples in the library reference: weakref.rst
  1222. >>> import weakref
  1223. >>> class Dict(dict):
  1224. ... pass
  1225. ...
  1226. >>> obj = Dict(red=1, green=2, blue=3) # this object is weak referencable
  1227. >>> r = weakref.ref(obj)
  1228. >>> print r() is obj
  1229. True
  1230. >>> import weakref
  1231. >>> class Object:
  1232. ... pass
  1233. ...
  1234. >>> o = Object()
  1235. >>> r = weakref.ref(o)
  1236. >>> o2 = r()
  1237. >>> o is o2
  1238. True
  1239. >>> del o, o2
  1240. >>> print r()
  1241. None
  1242. >>> import weakref
  1243. >>> class ExtendedRef(weakref.ref):
  1244. ... def __init__(self, ob, callback=None, **annotations):
  1245. ... super(ExtendedRef, self).__init__(ob, callback)
  1246. ... self.__counter = 0
  1247. ... for k, v in annotations.iteritems():
  1248. ... setattr(self, k, v)
  1249. ... def __call__(self):
  1250. ... '''Return a pair containing the referent and the number of
  1251. ... times the reference has been called.
  1252. ... '''
  1253. ... ob = super(ExtendedRef, self).__call__()
  1254. ... if ob is not None:
  1255. ... self.__counter += 1
  1256. ... ob = (ob, self.__counter)
  1257. ... return ob
  1258. ...
  1259. >>> class A: # not in docs from here, just testing the ExtendedRef
  1260. ... pass
  1261. ...
  1262. >>> a = A()
  1263. >>> r = ExtendedRef(a, foo=1, bar="baz")
  1264. >>> r.foo
  1265. 1
  1266. >>> r.bar
  1267. 'baz'
  1268. >>> r()[1]
  1269. 1
  1270. >>> r()[1]
  1271. 2
  1272. >>> r()[0] is a
  1273. True
  1274. >>> import weakref
  1275. >>> _id2obj_dict = weakref.WeakValueDictionary()
  1276. >>> def remember(obj):
  1277. ... oid = id(obj)
  1278. ... _id2obj_dict[oid] = obj
  1279. ... return oid
  1280. ...
  1281. >>> def id2obj(oid):
  1282. ... return _id2obj_dict[oid]
  1283. ...
  1284. >>> a = A() # from here, just testing
  1285. >>> a_id = remember(a)
  1286. >>> id2obj(a_id) is a
  1287. True
  1288. >>> del a
  1289. >>> try:
  1290. ... id2obj(a_id)
  1291. ... except KeyError:
  1292. ... print 'OK'
  1293. ... else:
  1294. ... print 'WeakValueDictionary error'
  1295. OK
  1296. """
  1297. __test__ = {'libreftest' : libreftest}
  1298. def test_main():
  1299. test_support.run_unittest(
  1300. ReferencesTestCase,
  1301. MappingTestCase,
  1302. WeakValueDictionaryTestCase,
  1303. WeakKeyDictionaryTestCase,
  1304. SubclassableWeakrefTestCase,
  1305. )
  1306. test_support.run_doctest(sys.modules[__name__])
  1307. if __name__ == "__main__":
  1308. test_main()