def _test_close_open_io(self, io_func, nb_workers=5): def worker(): funcs = itertools.cycle(( lambda: io_func(), lambda: self._close_and_reopen_file(), )) for f in funcs: if not self.do_continue: break try: f() except (IOError, ValueError): pass self._create_file() self._run_workers(worker, nb_workers) # make sure that all files can be closed now del self.all_files gc_collect() if test_support.verbose: # Useful verbose statistics when tuning this test to take # less time to run but still ensuring that its still useful. # # the percent of close calls that raised an error percent = 100. - 100.*self.close_success_count/self.close_count print self.close_count, ('%.4f ' % percent),
def test_weak_keys(self): # # This exercises d.copy(), d.items(), d[] = v, d[], del d[], # len(d), in d. # dict, objects = self.make_weak_keyed_dict() for o in objects: self.assertTrue(weakref.getweakrefcount(o) == 1, "wrong number of weak references to %r!" % o) self.assertTrue(o.arg is dict[o], "wrong object returned by weak dict!") items1 = dict.items() items2 = dict.copy().items() self.assertTrue(set(items1) == set(items2), "cloning of weak-keyed dictionary did not work!") del items1, items2 self.assertTrue(len(dict) == self.COUNT) del objects[0] gc_collect() self.assertTrue(len(dict) == (self.COUNT - 1), "deleting object did not cause dictionary update") del objects, o gc_collect() self.assertTrue(len(dict) == 0, "deleting the keys did not clear the dictionary") o = Object(42) dict[o] = "What is the meaning of the universe?" self.assertIn(o, dict) self.assertNotIn(34, dict)
def tearDown(self): test_support.gc_collect() for name in self.files: os.unlink(name) for i in xrange(2): if os.path.exists(test_support.TESTFN): os.rmdir(test_support.TESTFN)
def cleanup_test_droppings(testname, verbose): import shutil # Try to clean up junk commonly left behind. While tests shouldn't leave # any files or directories behind, when a test fails that can be tedious # for it to arrange. The consequences can be especially nasty on Windows, # since if a test leaves a file open, it cannot be deleted by name (while # there's nothing we can do about that here either, we can display the # name of the offending test, which is a real help). for name in (test_support.TESTFN, "db_home", ): if not os.path.exists(name): continue # work around tests depending on refcounting files, # but this doesn't work with respect to Windows test_support.gc_collect() if os.path.isdir(name): kind, nuker = "directory", shutil.rmtree elif os.path.isfile(name): kind, nuker = "file", os.unlink else: raise SystemError("os.path says %r exists but is neither " "directory nor file" % name) if verbose: print "%r left behind %s %r" % (testname, kind, name) try: nuker(name) except Exception, msg: print >> sys.stderr, ("%r left behind %s %r and it couldn't be " "removed: %s" % (testname, kind, name, msg))
def check_gc_during_creation(self, makeref): if test_support.check_impl_detail(): import gc thresholds = gc.get_threshold() gc.set_threshold(1, 1, 1) gc_collect() class A: pass def callback(*args): pass referenced = A() a = A() a.a = a a.wr = makeref(referenced) try: # now make sure the object and the ref get labeled as # cyclic trash: a = A() weakref.ref(referenced, callback) finally: if test_support.check_impl_detail(): gc.set_threshold(*thresholds)
def test_init(self): # Issue 3634 # <weakref to class>.__init__() doesn't check errors correctly r = weakref.ref(Exception) self.assertRaises(TypeError, r.__init__, 0, 0, 0, 0, 0) # No exception should be raised here gc_collect()
def test_leak_fast_process_del_killed(self): # Issue #12650: on Unix, if Popen.__del__() was called before the # process exited, and the process got killed by a signal, it would never # be removed from subprocess._active, which triggered a FD and memory # leak. # spawn a Popen, delete its reference and kill it p = subprocess.Popen([sys.executable, "-c", 'import time;' 'time.sleep(3)'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.addCleanup(p.stdout.close) self.addCleanup(p.stderr.close) ident = id(p) pid = p.pid del p test_support.gc_collect() os.kill(pid, signal.SIGKILL) # check that p is in the active processes list self.assertIn(ident, [id(o) for o in subprocess._active]) # let some time for the process to exit, and create a new Popen: this # should trigger the wait() of p time.sleep(0.2) with self.assertRaises(EnvironmentError) as c: with subprocess.Popen(['nonexisting_i_hope'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: pass # p should have been wait()ed on, and removed from the _active list self.assertRaises(OSError, os.waitpid, pid, 0) self.assertNotIn(ident, [id(o) for o in subprocess._active])
def test_weakref(self): s = array.array(self.typecode, self.example) p = proxy(s) self.assertEqual(p.tostring(), s.tostring()) s = None test_support.gc_collect() self.assertRaises(ReferenceError, len, p)
def testTruncateOnWindows(self): def bug801631(): # SF bug <http://www.python.org/sf/801631> # "file.truncate fault on windows" f = _FileIO(TESTFN, 'w') f.write(bytes(range(11))) f.close() f = _FileIO(TESTFN,'r+') data = f.read(5) if data != bytes(range(5)): self.fail("Read on file opened for update failed %r" % data) if f.tell() != 5: self.fail("File pos after read wrong %d" % f.tell()) f.truncate() if f.tell() != 5: self.fail("File pos after ftruncate wrong %d" % f.tell()) f.close() size = os.path.getsize(TESTFN) if size != 5: self.fail("File size after ftruncate wrong %d" % size) try: bug801631() finally: gc_collect() os.unlink(TESTFN)
def test_many(self): # mktemp can choose many usable file names (stochastic) extant = range(TEST_FILES) for i in extant: extant[i] = self.do_create(pre="aa") del extant test_support.gc_collect()
def test_weakref(self): f = self.thetype(int, base=16) p = proxy(f) self.assertEqual(f.func, p.func) f = None test_support.gc_collect() self.assertRaises(ReferenceError, getattr, p, 'func')
def test_callback_in_cycle_2(self): # 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_weakref(self): d = deque('gallahad') p = weakref.proxy(d) self.assertEqual(str(p), str(d)) d = None test_support.gc_collect() self.assertRaises(ReferenceError, str, p)
def test_class_to_test_weakness(self): # regrtest for bug 1522, adapted from test code submitted by Matt Brinkley # We wish to demonstrate that the proxy created by a Python class and # its PyType are GC'd when no longer in use, and therefore that the # Jython type system does not keep a PyType alive gratuitously. We do # this by holding weak references, then checking they're dead. # Get to steady state by creating >1 Dog and then GC-ing homeless ones. battersea = [] for i in range(2): battersea.extend(run_script(DOG, ('Dog', 'dog', 'breed'))) test_support.gc_collect() # This is the steady-state, GC number of Dog objects alive after GC: start_size = len(survivors(battersea)) # probably = 1 # Add more Dogs and GC the homeless ones again. for i in range(5): battersea.extend(run_script(DOG, ('Dog', 'dog', 'breed'))) test_support.gc_collect() #print "\nDogs home =", battersea #print "\nDogs alive =", survivors(battersea) # Post-GC number of Dogs should be as before self.assertEqual(start_size, len(survivors(battersea)))
def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [SomeClass(c) for c in string.ascii_letters] s = WeakSet(items) @contextlib.contextmanager def testcontext(): try: it = iter(s) next(it) # Schedule an item for removal and recreate it u = SomeClass(str(items.pop())) test_support.gc_collect() # just in case yield u finally: it = None # should commit all removals test_support.gc_collect() with testcontext() as u: self.assertNotIn(u, s) with testcontext() as u: self.assertRaises(KeyError, s.remove, u) self.assertNotIn(u, s) with testcontext() as u: s.add(u) self.assertIn(u, s) t = s.copy() with testcontext() as u: s.update(t) self.assertEqual(len(s), len(t)) with testcontext() as u: s.clear() self.assertEqual(len(s), 0)
def test___del__(self): self.assertFalse(self.info_exists("varname")) v = Variable(self.root, "sample string", "varname") self.assertTrue(self.info_exists("varname")) del v gc_collect() self.assertFalse(self.info_exists("varname"))
def test__count(self): # Test the _count() function. orig = thread._count() mut = thread.allocate_lock() mut.acquire() started = [] def task(): started.append(None) mut.acquire() mut.release() thread.start_new_thread(task, ()) while not started: time.sleep(0.01) self.assertEqual(thread._count(), orig + 1) # Allow the task to finish. mut.release() # The only reliable way to be sure that the thread ended from the # interpreter's point of view is to wait for the function object to be # destroyed. done = [] wr = weakref.ref(task, lambda _: done.append(None)) del task while not done: time.sleep(0.01) test_support.gc_collect() self.assertEqual(thread._count(), orig)
def test_callback_in_cycle_4(self): # 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_callbacks_on_callback(self): # 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.assertTrue(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, [])
def tearDown(self): # wait on the server thread to terminate test_support.gc_collect() # to close the active connections self.evt.wait(10) # disable traceback reporting SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False
def testOpenDel(self): # "Test opening and deleting a file many times" self.createTempFile() for i in xrange(10000): o = BZ2File(self.filename) del o if i % 100 == 0: test_support.gc_collect()
def test_basic(self): # mktemp can choose usable file names self.do_create() self.do_create(pre="a") self.do_create(suf="b") self.do_create(pre="a", suf="b") self.do_create(pre="aa", suf=".txt") test_support.gc_collect()
def test_cycle(self): class myobj(object): pass o = myobj() o.s = slice(o) w = weakref.ref(o) o = None test_support.gc_collect() self.assertIsNone(w())
def test_choose_directory(self): # _mkstemp_inner can create files in a user-selected directory dir = tempfile.mkdtemp() try: self.do_create(dir=dir).write("blat") test_support.gc_collect() finally: os.rmdir(dir)
def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) self.assertRaises(TypeError, self.s.__contains__, [[]]) self.assertIn(self.obj, self.fs) del self.obj test_support.gc_collect() self.assertNotIn(SomeClass('F'), self.fs)
def tearDown(self): test_support.gc_collect() if self.dir: try: os.rmdir(self.dir) except OSError, e: raise e self.dir = None
def check_basic_callback(self, factory): self.cbcalled = 0 o = factory() ref = weakref.ref(o, self.callback) del o gc_collect() self.assertTrue(self.cbcalled == 1, "callback did not properly set 'cbcalled'") self.assertTrue(ref() is None, "ref2 should be dead after deleting object reference")
def test_dont_clear_dict(self): # See issue 7140. def f(): foo = ModuleType("foo") foo.bar = 4 return foo gc_collect() self.assertEqual(f().__dict__["bar"], 4)
def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) # 1 is not weakref'able, but that TypeError is caught by __contains__ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj test_support.gc_collect() self.assertNotIn(SomeClass('F'), self.fs)
def testWeakRefs(self): # verify weak references p = proxy(self.f) p.write(bytes(range(10))) self.assertEqual(self.f.tell(), p.tell()) self.f.close() self.f = None gc_collect() self.assertRaises(ReferenceError, getattr, p, 'tell')
def test_len_cycles(self): N = 20 items = [RefCycle() for i in range(N)] s = WeakSet(items) del items it = iter(s) try: next(it) except StopIteration: pass gc.collect() n1 = len(s) del it test_support.gc_collect() n2 = len(s) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0)
def test_zombie_fast_process_del(self): # Issue #12650: on Unix, if Popen.__del__() was called before the # process exited, it wouldn't be added to subprocess._active, and would # remain a zombie. # spawn a Popen, and delete its reference before it exits p = subprocess.Popen( [sys.executable, "-c", 'import sys, time;' 'time.sleep(0.2)'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.addCleanup(p.stdout.close) self.addCleanup(p.stderr.close) ident = id(p) pid = p.pid del p test_support.gc_collect() # check that p is in the active processes list self.assertIn(ident, [id(o) for o in subprocess._active])
def test_classes(self): # Check that both old-style classes and new-style classes # are weakrefable. class A(object): pass class B: pass l = [] weakref.ref(int) a = weakref.ref(A, l.append) A = None gc_collect() self.assertEqual(a(), None) self.assertEqual(l, [a]) b = weakref.ref(B, l.append) B = None gc_collect() self.assertEqual(b(), None) self.assertEqual(l, [a, b])
def test_callback_in_cycle_resurrection(self): # 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_subclass_refs(self): class MyRef(weakref.ref): def __init__(self, ob, callback=None, value=42): self.value = value super(MyRef, self).__init__(ob, callback) def __call__(self): self.called = True return super(MyRef, self).__call__() o = Object("foo") mr = MyRef(o, value=24) self.assertTrue(mr() is o) self.assertTrue(mr.called) self.assertEqual(mr.value, 24) del o gc_collect() self.assertTrue(mr() is None) self.assertTrue(mr.called)
def test_cache_leak(self): # See issue #2521. class A(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def f(self): pass class C(A): def f(self): A.f(self) r = weakref.ref(C) # Trigger cache. C().f() del C test_support.gc_collect() self.assertEqual(r(), None)
def check_len_cycles(self, dict_type, cons): N = 20 items = [RefCycle() for i in range(N)] dct = dict_type(cons(o) for o in items) # Keep an iterator alive it = dct.iteritems() try: next(it) except StopIteration: pass del items gc.collect() n1 = len(dct) del it test_support.gc_collect() n2 = len(dct) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) self.assertEqual(n2, 0)
def test_13_genexp(self): if self.using_gc: gc.enable() test_support.gc_collect() try: self.run_test(generator_example) # issue1265: if the trace function contains a generator, # and if the traced function contains another generator # that is not completely exhausted, the trace stopped. # Worse: the 'finally' clause was not invoked. tracer = Tracer() sys.settrace(tracer.traceWithGenexp) generator_example() sys.settrace(None) self.compare_events(generator_example.__code__.co_firstlineno, tracer.events, generator_example.events) finally: if self.using_gc: gc.disable()
def test_deepcopy_weakkeydict(self): class C(object): def __init__(self, i): self.i = i a, b, c, d = [C(i) for i in xrange(4)] u = weakref.WeakKeyDictionary() u[a] = b u[c] = d # Keys aren't copied, values are v = copy.deepcopy(u) self.assertNotEqual(v, u) self.assertEqual(len(v), 2) self.assertFalse(v[a] is b) self.assertFalse(v[c] is d) self.assertEqual(v[a].i, b.i) self.assertEqual(v[c].i, d.i) del c test_support.gc_collect() self.assertEqual(len(v), 1)
def _check_copy_weakdict(self, _dicttype): class C(object): pass a, b, c, d = [C() for i in xrange(4)] u = _dicttype() u[a] = b u[c] = d v = copy.copy(u) self.assertFalse(v is u) self.assertEqual(v, u) self.assertEqual(v[a], b) self.assertEqual(v[c], d) self.assertEqual(len(v), 2) del c, d test_support.gc_collect() self.assertEqual(len(v), 1) x, y = C(), C() # The underlying containers are decoupled v[x] = y self.assertNotIn(x, u)
def test_callback_in_cycle_3(self): # 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 testRawFileGarbageCollection(self): # FileIO objects are collected, and collecting them flushes # all data to disk. # WARNING - cyclic garbage collection can't work with python objects having a __del__ method ! import weakref f = io.open(TESTFN, 'wb', buffering=0) f.write(b"abcxxx") wr = weakref.ref(f) del f test_support.gc_collect() # print (gc.garbage) - in case of trouble self.assertTrue(wr() is None, wr) with io.open(TESTFN, "rb") as f: self.assertEqual(f.read(), b"abcxxx")
def test_getweakrefcount(self): o = C() ref1 = weakref.ref(o) ref2 = weakref.ref(o, self.callback) self.assertTrue(weakref.getweakrefcount(o) == 2, "got wrong number of weak reference objects") proxy1 = weakref.proxy(o) proxy2 = weakref.proxy(o, self.callback) self.assertTrue(weakref.getweakrefcount(o) == 4, "got wrong number of weak reference objects") del ref1, ref2, proxy1, proxy2 gc_collect() self.assertTrue(weakref.getweakrefcount(o) == 0, "weak reference objects not unlinked from" " referent when discarded.") # assumes ints do not support weakrefs self.assertTrue(weakref.getweakrefcount(1) == 0, "got wrong number of weak reference objects for int")
def test_weak_values(self): # # This exercises d.copy(), d.items(), d[], del d[], len(d). # dict, objects = self.make_weak_valued_dict() for o in objects: self.assertTrue( weakref.getweakrefcount(o) == 1, "wrong number of weak references to %r!" % o) self.assertTrue(o is dict[o.arg], "wrong object returned by weak dict!") items1 = dict.items() items2 = dict.copy().items() items1.sort() items2.sort() self.assertTrue(items1 == items2, "cloning of weak-valued dictionary did not work!") del items1, items2 self.assertTrue(len(dict) == self.COUNT) del objects[0] gc_collect() self.assertTrue( len(dict) == (self.COUNT - 1), "deleting object did not cause dictionary update") del objects, o gc_collect() self.assertTrue( len(dict) == 0, "deleting the values did not clear the dictionary") # regression on SF bug #447152: dict = weakref.WeakValueDictionary() self.assertRaises(KeyError, dict.__getitem__, 1) dict[2] = C() gc_collect() self.assertRaises(KeyError, dict.__getitem__, 2)
def test_weak_keys(self): # # This exercises d.copy(), d.items(), d[] = v, d[], del d[], # len(d), d.has_key(). # import gc dict, objects = self.make_weak_keyed_dict() for o in objects: self.assert_( weakref.getweakrefcount(o) == 1, "wrong number of weak references to %r!" % o) self.assert_(o.arg is dict[o], "wrong object returned by weak dict!") items1 = dict.items() items2 = dict.copy().items() self.assert_( set(items1) == set(items2), "cloning of weak-keyed dictionary did not work!") del items1, items2 test_support.gc_collect() self.assert_(len(dict) == self.COUNT) del objects[0] test_support.gc_collect() self.assert_( len(dict) == (self.COUNT - 1), "deleting object did not cause dictionary update") del objects, o test_support.gc_collect() self.assert_( len(dict) == 0, "deleting the keys did not clear the dictionary") o = Object(42) dict[o] = "What is the meaning of the universe?" self.assert_(dict.has_key(o)) self.assert_(not dict.has_key(34))
def test_getweakrefs(self): o = C() ref1 = weakref.ref(o, self.callback) ref2 = weakref.ref(o, self.callback) del ref1 gc_collect() self.assertTrue( weakref.getweakrefs(o) == [ref2], "list of refs does not match") o = C() ref1 = weakref.ref(o, self.callback) ref2 = weakref.ref(o, self.callback) del ref2 gc_collect() self.assertTrue( weakref.getweakrefs(o) == [ref1], "list of refs does not match") del ref1 gc_collect() self.assertTrue( weakref.getweakrefs(o) == [], "list of refs not cleared") # assumes ints do not support weakrefs self.assertTrue( weakref.getweakrefs(1) == [], "list of refs does not match for int")
def test_close_with_cleared_frame(self): # See issue #17669. # # Create a stack of generators: outer() delegating to inner() # delegating to innermost(). The key point is that the instance of # inner is created first: this ensures that its frame appears before # the instance of outer in the GC linked list. # # At the gc.collect call: # - frame_clear is called on the inner_gen frame. # - gen_dealloc is called on the outer_gen generator (the only # reference is in the frame's locals). # - gen_close is called on the outer_gen generator. # - gen_close_iter is called to close the inner_gen generator, which # in turn calls gen_close, and gen_yf. # # Previously, gen_yf would crash since inner_gen's frame had been # cleared (and in particular f_stacktop was NULL). def innermost(): yield def inner(): outer_gen = yield yield from innermost() def outer(): inner_gen = yield yield from inner_gen with disable_gc(): inner_gen = inner() outer_gen = outer() outer_gen.send(None) outer_gen.send(inner_gen) outer_gen.send(outer_gen) del outer_gen del inner_gen gc_collect()
def test_basic(self): # Create a code object in a clean environment so that we know we have # the only reference to it left. namespace = {} exec "def f(): pass" in globals(), namespace f = namespace["f"] del namespace self.called = False def callback(code): self.called = True # f is now the last reference to the function, and through it, the code # object. While we hold it, check that we can create a weakref and # deref it. Then delete it, and check that the callback gets called and # the reference dies. coderef = weakref.ref(f.__code__, callback) self.assertTrue(bool(coderef())) del f gc_collect() self.assertFalse(bool(coderef())) self.assertTrue(self.called)
def test_ref_reuse(self): o = C() ref1 = weakref.ref(o) # create a proxy to make sure that there's an intervening creation # between these two; it should make no difference proxy = weakref.proxy(o) ref2 = weakref.ref(o) self.assertTrue(ref1 is ref2, "reference object w/out callback should be re-used") o = C() proxy = weakref.proxy(o) ref1 = weakref.ref(o) ref2 = weakref.ref(o) self.assertTrue(ref1 is ref2, "reference object w/out callback should be re-used") self.assertTrue(weakref.getweakrefcount(o) == 2, "wrong weak ref count for object") del proxy gc_collect() self.assertTrue(weakref.getweakrefcount(o) == 1, "wrong weak ref count for object after deleting proxy")
def test_deepcopy_weakvaluedict(self): class C(object): def __init__(self, i): self.i = i a, b, c, d = [C(i) for i in xrange(4)] u = weakref.WeakValueDictionary() u[a] = b u[c] = d # Keys are copied, values aren't v = copy.deepcopy(u) self.assertNotEqual(v, u) self.assertEqual(len(v), 2) (x, y), (z, t) = sorted(v.items(), key=lambda pair: pair[0].i) self.assertFalse(x is a) self.assertEqual(x.i, a.i) self.assertTrue(y is b) self.assertFalse(z is c) self.assertEqual(z.i, c.i) self.assertTrue(t is d) del x, y, z, t del d test_support.gc_collect() self.assertEqual(len(v), 1)
def testLeaks(self): class Foo: count = 0 def __init__(self): Foo.count += 1 def __del__(self): Foo.count -= 1 def f1(): x = Foo() def f2(): return x f2() for i in range(100): f1() gc_collect() self.assertEqual(Foo.count, 0)
def test_len_race(self): # Extended sanity checks for len() in the face of cyclic collection #self.addCleanup(gc.set_threshold, *gc.get_threshold()) for th in range(1, 100): N = 20 test_support.gc_collect() #gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] s = WeakSet(items) del items # All items will be collected at next garbage collection pass it = iter(s) try: next(it) except StopIteration: pass n1 = len(s) del it n2 = len(s) self.assertGreaterEqual(n1, 0) self.assertLessEqual(n1, N) self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1)
def test_sf_bug_840829(self): # "weakref callbacks and gc corrupt memory" # subtype_dealloc erroneously exposed a new-style instance # already in the process of getting deallocated to gc, # causing double-deallocation if the instance had a weakref # callback that triggered gc. # If the bug exists, there probably won't be an obvious symptom # in a release build. In a debug build, a segfault will occur # when the second attempt to remove the instance from the "list # of all objects" occurs. class C(object): pass c = C() wr = weakref.ref(c, lambda ignore: gc_collect()) del c # There endeth the first part. It gets worse. del wr c1 = C() c1.i = C() wr = weakref.ref(c1.i, lambda ignore: gc_collect()) c2 = C() c2.c1 = c1 del c1 # still alive because c2 points to it # Now when subtype_dealloc gets called on c2, it's not enough just # that c2 is immune from gc while the weakref callbacks associated # with c2 execute (there are none in this 2nd half of the test, btw). # subtype_dealloc goes on to call the base classes' deallocs too, # so any gc triggered by weakref callbacks associated with anything # torn down by a base class dealloc can also trigger double # deallocation of c2. del c2
def test_callback_in_cycle_1(self): 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_unraisable(self): # Issue #22836: PyErr_WriteUnraisable() should give sensible reports class BrokenDel: def __del__(self): exc = ValueError("del is broken") # In Python 3, the following line would be in the report: raise exc class BrokenRepr(BrokenDel): def __repr__(self): raise AttributeError("repr() is broken") class BrokenExceptionDel: def __del__(self): exc = BrokenStrException() # In Python 3, the following line would be in the report: raise exc for test_class in (BrokenDel, BrokenRepr, BrokenExceptionDel): obj = test_class() with captured_stderr() as stderr: del obj gc_collect() report = stderr.getvalue() self.assertRegexpMatches(report, "Exception.* ignored") if test_class is BrokenRepr: self.assertIn("<object repr() failed>", report) else: self.assertIn("__del__", report) if test_class is BrokenExceptionDel: self.assertIn("BrokenStrException", report) self.assertIn("<exception repr() failed>", report) else: self.assertIn("ValueError", report) self.assertIn("del is broken", report) self.assertTrue(report.endswith("\n"))
def test_class_to_test_weakness(self): # regrtest for bug 1522, adapted from test code submitted by Matt Brinkley # work around the fact that we can't look at PyType directly # by using this helper function that reflects on PyType (and # demonstrates here that it's the same as the builtin function # `type`!) class_to_type_map = invokePyTypeMethod(type, 'getClassToType') def create_proxies(): pi = PythonInterpreter() pi.exec(""" from java.lang import Comparable class Dog(Comparable): def compareTo(self, o): return 0 def bark(self): return 'woof woof' Dog().bark() """) # get to steady state first, then verify we don't create new proxies for i in xrange(2): create_proxies() # Ensure the reaper thread can run and clear out weak refs, so # use this supporting function test_support.gc_collect() # Given that taking the len (or size()) of Guava weak maps is # eventually consistent, we should instead take a len of its # keys. start_size = len(list(class_to_type_map)) for i in xrange(5): create_proxies() test_support.gc_collect() self.assertEqual(start_size, len(list(class_to_type_map)))
def test_loading_classes_weakness(self): # Show that classes loaded via a class loader are collectible once the # class loader is not strongly reachable. # Reference all species of object on a weak list: zoo = [] def activity(): # Look for the Callbacker class ONLY in the special JAR jar = os.path.join(sys.prefix, "callbacker_test.jar") cldr = test_support.make_jar_classloader(jar, None) CB = cldr.loadClass("org.python.tests.Callbacker") cb = CB() # Save locals as weak references (that we hope will go dead) for obj in (cb, type(cb), cldr): zoo.append(weakref.ref(obj)) # Load and use a class: objects created in zoo. activity() #print "\nzoo =", zoo test_support.gc_collect() #print "\nsurvivors =", survivors(zoo) self.assertEqual(0, len(survivors(zoo)))
def test_widget_destroy(self): # automatically created variable x = ttk.LabeledScale(self.root) var = x._variable._name x.destroy() gc_collect() self.assertRaises(tkinter.TclError, x.tk.globalgetvar, var) # manually created variable myvar = tkinter.DoubleVar(self.root) name = myvar._name x = ttk.LabeledScale(self.root, variable=myvar) x.destroy() gc_collect() if self.wantobjects: self.assertEqual(x.tk.globalgetvar(name), myvar.get()) else: self.assertEqual(float(x.tk.globalgetvar(name)), myvar.get()) del myvar gc_collect() self.assertRaises(tkinter.TclError, x.tk.globalgetvar, name) # checking that the tracing callback is properly removed myvar = tkinter.IntVar(self.root) # LabeledScale will start tracing myvar x = ttk.LabeledScale(self.root, variable=myvar) x.destroy() gc_collect() # Unless the tracing callback was removed, creating a new # LabeledScale with the same var will cause an error now. This # happens because the variable will be set to (possibly) a new # value which causes the tracing callback to be called and then # it tries calling instance attributes not yet defined. ttk.LabeledScale(self.root, variable=myvar) if hasattr(sys, 'last_type'): self.assertNotEqual(sys.last_type, tkinter.TclError)
def tearDown(self): test_support.gc_collect() if os.path.isfile(self.filename): os.unlink(self.filename)