diff options
Diffstat (limited to 'lang/python23-pth/patches/patch-ag')
-rw-r--r-- | lang/python23-pth/patches/patch-ag | 223 |
1 files changed, 213 insertions, 10 deletions
diff --git a/lang/python23-pth/patches/patch-ag b/lang/python23-pth/patches/patch-ag index ab803d54fa9..6bca9ddf6d1 100644 --- a/lang/python23-pth/patches/patch-ag +++ b/lang/python23-pth/patches/patch-ag @@ -1,13 +1,216 @@ -$NetBSD: patch-ag,v 1.1.1.1 2003/08/04 08:29:32 drochner Exp $ +$NetBSD: patch-ag,v 1.2 2003/12/08 21:13:56 recht Exp $ ---- configure.orig 2003-08-03 13:46:44.000000000 +0200 -+++ configure 2003-08-03 13:47:19.000000000 +0200 -@@ -1243,7 +1243,7 @@ - mv confdefs.h.new confdefs.h +--- Lib/test/test_weakref.py.orig 2003-07-14 23:37:17.000000000 +0200 ++++ Lib/test/test_weakref.py +@@ -299,6 +299,211 @@ class ReferencesTestCase(TestBase): + self.fail("exception not properly restored") --VERSION=2.3 -+VERSION=2p3 - - - SOVERSION=1.0 ++ def test_callback_in_cycle_1(self): ++ import gc ++ ++ class J(object): ++ pass ++ ++ class II(object): ++ def acallback(self, ignore): ++ self.J ++ ++ I = II() ++ I.J = J ++ I.wr = weakref.ref(J, I.acallback) ++ ++ # Now J and II are each in a self-cycle (as all new-style class ++ # objects are, since their __mro__ points back to them). I holds ++ # both a weak reference (I.wr) and a strong reference (I.J) to class ++ # J. I is also in a cycle (I.wr points to a weakref that references ++ # I.acallback). When we del these three, they all become trash, but ++ # the cycles prevent any of them from getting cleaned up immediately. ++ # Instead they have to wait for cyclic gc to deduce that they're ++ # trash. ++ # ++ # gc used to call tp_clear on all of them, and the order in which ++ # it does that is pretty accidental. The exact order in which we ++ # built up these things manages to provoke gc into running tp_clear ++ # in just the right order (I last). Calling tp_clear on II leaves ++ # behind an insane class object (its __mro__ becomes NULL). Calling ++ # tp_clear on J breaks its self-cycle, but J doesn't get deleted ++ # just then because of the strong reference from I.J. Calling ++ # tp_clear on I starts to clear I's __dict__, and just happens to ++ # clear I.J first -- I.wr is still intact. That removes the last ++ # reference to J, which triggers the weakref callback. The callback ++ # tries to do "self.J", and instances of new-style classes look up ++ # attributes ("J") in the class dict first. The class (II) wants to ++ # search II.__mro__, but that's NULL. The result was a segfault in ++ # a release build, and an assert failure in a debug build. ++ del I, J, II ++ gc.collect() ++ ++ def test_callback_in_cycle_2(self): ++ import gc ++ ++ # This is just like test_callback_in_cycle_1, except that II is an ++ # old-style class. The symptom is different then: an instance of an ++ # old-style class looks in its own __dict__ first. 'J' happens to ++ # get cleared from I.__dict__ before 'wr', and 'J' was never in II's ++ # __dict__, so the attribute isn't found. The difference is that ++ # the old-style II doesn't have a NULL __mro__ (it doesn't have any ++ # __mro__), so no segfault occurs. Instead it got: ++ # test_callback_in_cycle_2 (__main__.ReferencesTestCase) ... ++ # Exception exceptions.AttributeError: ++ # "II instance has no attribute 'J'" in <bound method II.acallback ++ # of <?.II instance at 0x00B9B4B8>> ignored ++ ++ class J(object): ++ pass ++ ++ class II: ++ def acallback(self, ignore): ++ self.J ++ ++ I = II() ++ I.J = J ++ I.wr = weakref.ref(J, I.acallback) ++ ++ del I, J, II ++ gc.collect() ++ ++ def test_callback_in_cycle_3(self): ++ import gc ++ ++ # This one broke the first patch that fixed the last two. In this ++ # case, the objects reachable from the callback aren't also reachable ++ # from the object (c1) *triggering* the callback: you can get to ++ # c1 from c2, but not vice-versa. The result was that c2's __dict__ ++ # got tp_clear'ed by the time the c2.cb callback got invoked. ++ ++ class C: ++ def cb(self, ignore): ++ self.me ++ self.c1 ++ self.wr ++ ++ c1, c2 = C(), C() ++ ++ c2.me = c2 ++ c2.c1 = c1 ++ c2.wr = weakref.ref(c1, c2.cb) ++ ++ del c1, c2 ++ gc.collect() ++ ++ def test_callback_in_cycle_4(self): ++ import gc ++ ++ # Like test_callback_in_cycle_3, except c2 and c1 have different ++ # classes. c2's class (C) isn't reachable from c1 then, so protecting ++ # objects reachable from the dying object (c1) isn't enough to stop ++ # c2's class (C) from getting tp_clear'ed before c2.cb is invoked. ++ # The result was a segfault (C.__mro__ was NULL when the callback ++ # tried to look up self.me). ++ ++ class C(object): ++ def cb(self, ignore): ++ self.me ++ self.c1 ++ self.wr ++ ++ class D: ++ pass ++ ++ c1, c2 = D(), C() ++ ++ c2.me = c2 ++ c2.c1 = c1 ++ c2.wr = weakref.ref(c1, c2.cb) ++ ++ del c1, c2, C, D ++ gc.collect() ++ ++ def test_callback_in_cycle_resurrection(self): ++ import gc ++ ++ # Do something nasty in a weakref callback: resurrect objects ++ # from dead cycles. For this to be attempted, the weakref and ++ # its callback must also be part of the cyclic trash (else the ++ # objects reachable via the callback couldn't be in cyclic trash ++ # to begin with -- the callback would act like an external root). ++ # But gc clears trash weakrefs with callbacks early now, which ++ # disables the callbacks, so the callbacks shouldn't get called ++ # at all (and so nothing actually gets resurrected). ++ ++ alist = [] ++ class C(object): ++ def __init__(self, value): ++ self.attribute = value ++ ++ def acallback(self, ignore): ++ alist.append(self.c) ++ ++ c1, c2 = C(1), C(2) ++ c1.c = c2 ++ c2.c = c1 ++ c1.wr = weakref.ref(c2, c1.acallback) ++ c2.wr = weakref.ref(c1, c2.acallback) ++ ++ def C_went_away(ignore): ++ alist.append("C went away") ++ wr = weakref.ref(C, C_went_away) ++ ++ del c1, c2, C # make them all trash ++ self.assertEqual(alist, []) # del isn't enough to reclaim anything ++ ++ gc.collect() ++ # c1.wr and c2.wr were part of the cyclic trash, so should have ++ # been cleared without their callbacks executing. OTOH, the weakref ++ # to C is bound to a function local (wr), and wasn't trash, so that ++ # callback should have been invoked when C went away. ++ self.assertEqual(alist, ["C went away"]) ++ # The remaining weakref should be dead now (its callback ran). ++ self.assertEqual(wr(), None) ++ ++ del alist[:] ++ gc.collect() ++ self.assertEqual(alist, []) ++ ++ def test_callbacks_on_callback(self): ++ import gc ++ ++ # Set up weakref callbacks *on* weakref callbacks. ++ alist = [] ++ def safe_callback(ignore): ++ alist.append("safe_callback called") ++ ++ class C(object): ++ def cb(self, ignore): ++ alist.append("cb called") ++ ++ c, d = C(), C() ++ c.other = d ++ d.other = c ++ callback = c.cb ++ c.wr = weakref.ref(d, callback) # this won't trigger ++ d.wr = weakref.ref(callback, d.cb) # ditto ++ external_wr = weakref.ref(callback, safe_callback) # but this will ++ self.assert_(external_wr() is callback) ++ ++ # The weakrefs attached to c and d should get cleared, so that ++ # C.cb is never called. But external_wr isn't part of the cyclic ++ # trash, and no cyclic trash is reachable from it, so safe_callback ++ # should get invoked when the bound method object callback (c.cb) ++ # -- which is itself a callback, and also part of the cyclic trash -- ++ # gets reclaimed at the end of gc. ++ ++ del callback, c, d, C ++ self.assertEqual(alist, []) # del isn't enough to clean up cycles ++ gc.collect() ++ self.assertEqual(alist, ["safe_callback called"]) ++ self.assertEqual(external_wr(), None) ++ ++ del alist[:] ++ gc.collect() ++ self.assertEqual(alist, []) ++ + class Object: + def __init__(self, arg): + self.arg = arg |