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