import gc import xx import weakref class CF(list): __slots__ = [] def __del__(self): print('running __del__ for CF', self) def callback(self, wr): # if this runs, it can do bad things: cause crashes, revive 'self' print('oops, should not run', repr(self)) class A: def __del__(self): print('running __del__ for A') ct = xx.new() # does not implement tp_traverse ct.a = A() cf = CF() cf.append(ct) cf.append(cf) # create cycle # second trash cycle containing weakref, created so it outlives cf cycle wr = [weakref.ref(ct.a, cf.callback)] wr.append(wr) del ct, A gc.collect() del cf, wr # At this point the following is true: # - cf and the weakref are cyclic trash # - ct is not yet trash (it actually is but the GC doesn't know because of # the missing tp_traverse method). It will be freed with cf is cleared # - callback for 'a' will run when cf is cleared because ct drops the ref to # 'a' gc.set_debug(gc.DEBUG_COLLECTABLE) print('running collect') print(gc.collect()) gc.set_debug(0)