def test_garbage(self): with restore_gc_state(): gc.disable() a = [] b = [] c = [] a.append(a) b.append(c) c.append(b) del a, b, c graph = garbage() self.assertEqual(len(gc.garbage), 0) self.assertEqual(len(graph), 3) # A second call to garbage should # produce nothing new. graph2 = garbage() self.assertEqual(gc.garbage, []) self.assertEqual(len(graph2), 0) # But if we delete graph then # a, b and c become collectable again. del graph graph = garbage() self.assertEqual(gc.garbage, []) self.assertEqual(len(graph), 3) # Get rid of everything. del graph, graph2 gc.collect() graph = garbage() self.assertEqual(gc.garbage, []) self.assertEqual(len(graph), 0)
def cycles_created_by(callable): """ Return graph of cyclic garbage created by the given callable. Return an :class:`~refcycle.object_graph.ObjectGraph` representing those objects generated by the given callable that can't be collected by Python's usual reference-count based garbage collection. This includes objects that will eventually be collected by the cyclic garbage collector, as well as genuinely unreachable objects that will never be collected. `callable` should be a callable that takes no arguments; its return value (if any) will be ignored. """ with restore_gc_state(): gc.disable() gc.collect() gc.set_debug(gc.DEBUG_SAVEALL) callable() new_object_count = gc.collect() if new_object_count: objects = gc.garbage[-new_object_count:] del gc.garbage[-new_object_count:] else: objects = [] return ObjectGraph(objects)
def test_snapshot(self): with restore_gc_state(): gc.disable() original_objects = snapshot() create_cycles() new_objects = snapshot() diff = new_objects - original_objects - ObjectGraph( original_objects.owned_objects()) self.assertEqual(len(diff), 4)
def test_key_cycles(self): with restore_gc_state(): gc.disable() a = ['a'] b = ['b'] c = ['c'] d = ['d'] a.append(b) b.append(a) c.append(d) d.append(c) b.append(d) del a, b, c, d sccs = key_cycles() self.assertEqual(len(sccs), 1) self.assertEqual(len(sccs[0]), 2) # Make sure to remove the sccs for good. del sccs gc.collect() # Same again, but with no connections between {a, b} and {c, d}. with restore_gc_state(): gc.disable() a = ['a'] b = ['b'] c = ['c'] d = ['d'] a.append(b) b.append(a) c.append(d) d.append(c) del a, b, c, d sccs = key_cycles() self.assertEqual(len(sccs), 2) self.assertEqual(len(sccs[0]), 2) self.assertEqual(len(sccs[1]), 2) # Make sure to remove the sccs for good. del sccs gc.collect()
def garbage(): """ Collect garbage and return an :class:`~refcycle.object_graph.ObjectGraph` based on collected garbage. The collected elements are removed from ``gc.garbage``, but are still kept alive by the references in the graph. Deleting the :class:`~refcycle.object_graph.ObjectGraph` instance and doing another ``gc.collect`` will remove those objects for good. """ with restore_gc_state(): gc.disable() gc.set_debug(gc.DEBUG_SAVEALL) collected_count = gc.collect() if collected_count: objects = gc.garbage[-collected_count:] del gc.garbage[-collected_count:] else: objects = [] return ObjectGraph(objects)