17db96d56Sopenharmony_ciimport unittest
27db96d56Sopenharmony_ciimport unittest.mock
37db96d56Sopenharmony_cifrom test.support import (verbose, refcount_test,
47db96d56Sopenharmony_ci                          cpython_only, requires_subprocess)
57db96d56Sopenharmony_cifrom test.support.import_helper import import_module
67db96d56Sopenharmony_cifrom test.support.os_helper import temp_dir, TESTFN, unlink
77db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_ok, make_script
87db96d56Sopenharmony_cifrom test.support import threading_helper
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciimport gc
117db96d56Sopenharmony_ciimport sys
127db96d56Sopenharmony_ciimport sysconfig
137db96d56Sopenharmony_ciimport textwrap
147db96d56Sopenharmony_ciimport threading
157db96d56Sopenharmony_ciimport time
167db96d56Sopenharmony_ciimport weakref
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_citry:
197db96d56Sopenharmony_ci    from _testcapi import with_tp_del
207db96d56Sopenharmony_ciexcept ImportError:
217db96d56Sopenharmony_ci    def with_tp_del(cls):
227db96d56Sopenharmony_ci        class C(object):
237db96d56Sopenharmony_ci            def __new__(cls, *args, **kwargs):
247db96d56Sopenharmony_ci                raise TypeError('requires _testcapi.with_tp_del')
257db96d56Sopenharmony_ci        return C
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_citry:
287db96d56Sopenharmony_ci    from _testcapi import ContainerNoGC
297db96d56Sopenharmony_ciexcept ImportError:
307db96d56Sopenharmony_ci    ContainerNoGC = None
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci### Support code
337db96d56Sopenharmony_ci###############################################################################
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_ci# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
367db96d56Sopenharmony_ci# cyclic gc.
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ci# An instance of C1055820 has a self-loop, so becomes cyclic trash when
397db96d56Sopenharmony_ci# unreachable.
407db96d56Sopenharmony_ciclass C1055820(object):
417db96d56Sopenharmony_ci    def __init__(self, i):
427db96d56Sopenharmony_ci        self.i = i
437db96d56Sopenharmony_ci        self.loop = self
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ciclass GC_Detector(object):
467db96d56Sopenharmony_ci    # Create an instance I.  Then gc hasn't happened again so long as
477db96d56Sopenharmony_ci    # I.gc_happened is false.
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_ci    def __init__(self):
507db96d56Sopenharmony_ci        self.gc_happened = False
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci        def it_happened(ignored):
537db96d56Sopenharmony_ci            self.gc_happened = True
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci        # Create a piece of cyclic trash that triggers it_happened when
567db96d56Sopenharmony_ci        # gc collects it.
577db96d56Sopenharmony_ci        self.wr = weakref.ref(C1055820(666), it_happened)
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci@with_tp_del
607db96d56Sopenharmony_ciclass Uncollectable(object):
617db96d56Sopenharmony_ci    """Create a reference cycle with multiple __del__ methods.
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci    An object in a reference cycle will never have zero references,
647db96d56Sopenharmony_ci    and so must be garbage collected.  If one or more objects in the
657db96d56Sopenharmony_ci    cycle have __del__ methods, the gc refuses to guess an order,
667db96d56Sopenharmony_ci    and leaves the cycle uncollected."""
677db96d56Sopenharmony_ci    def __init__(self, partner=None):
687db96d56Sopenharmony_ci        if partner is None:
697db96d56Sopenharmony_ci            self.partner = Uncollectable(partner=self)
707db96d56Sopenharmony_ci        else:
717db96d56Sopenharmony_ci            self.partner = partner
727db96d56Sopenharmony_ci    def __tp_del__(self):
737db96d56Sopenharmony_ci        pass
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ciif sysconfig.get_config_vars().get('PY_CFLAGS', ''):
767db96d56Sopenharmony_ci    BUILD_WITH_NDEBUG = ('-DNDEBUG' in sysconfig.get_config_vars()['PY_CFLAGS'])
777db96d56Sopenharmony_cielse:
787db96d56Sopenharmony_ci    # Usually, sys.gettotalrefcount() is only present if Python has been
797db96d56Sopenharmony_ci    # compiled in debug mode. If it's missing, expect that Python has
807db96d56Sopenharmony_ci    # been released in release mode: with NDEBUG defined.
817db96d56Sopenharmony_ci    BUILD_WITH_NDEBUG = (not hasattr(sys, 'gettotalrefcount'))
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ci### Tests
847db96d56Sopenharmony_ci###############################################################################
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ciclass GCTests(unittest.TestCase):
877db96d56Sopenharmony_ci    def test_list(self):
887db96d56Sopenharmony_ci        l = []
897db96d56Sopenharmony_ci        l.append(l)
907db96d56Sopenharmony_ci        gc.collect()
917db96d56Sopenharmony_ci        del l
927db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 1)
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_ci    def test_dict(self):
957db96d56Sopenharmony_ci        d = {}
967db96d56Sopenharmony_ci        d[1] = d
977db96d56Sopenharmony_ci        gc.collect()
987db96d56Sopenharmony_ci        del d
997db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 1)
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ci    def test_tuple(self):
1027db96d56Sopenharmony_ci        # since tuples are immutable we close the loop with a list
1037db96d56Sopenharmony_ci        l = []
1047db96d56Sopenharmony_ci        t = (l,)
1057db96d56Sopenharmony_ci        l.append(t)
1067db96d56Sopenharmony_ci        gc.collect()
1077db96d56Sopenharmony_ci        del t
1087db96d56Sopenharmony_ci        del l
1097db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ci    def test_class(self):
1127db96d56Sopenharmony_ci        class A:
1137db96d56Sopenharmony_ci            pass
1147db96d56Sopenharmony_ci        A.a = A
1157db96d56Sopenharmony_ci        gc.collect()
1167db96d56Sopenharmony_ci        del A
1177db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1187db96d56Sopenharmony_ci
1197db96d56Sopenharmony_ci    def test_newstyleclass(self):
1207db96d56Sopenharmony_ci        class A(object):
1217db96d56Sopenharmony_ci            pass
1227db96d56Sopenharmony_ci        gc.collect()
1237db96d56Sopenharmony_ci        del A
1247db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ci    def test_instance(self):
1277db96d56Sopenharmony_ci        class A:
1287db96d56Sopenharmony_ci            pass
1297db96d56Sopenharmony_ci        a = A()
1307db96d56Sopenharmony_ci        a.a = a
1317db96d56Sopenharmony_ci        gc.collect()
1327db96d56Sopenharmony_ci        del a
1337db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ci    def test_newinstance(self):
1367db96d56Sopenharmony_ci        class A(object):
1377db96d56Sopenharmony_ci            pass
1387db96d56Sopenharmony_ci        a = A()
1397db96d56Sopenharmony_ci        a.a = a
1407db96d56Sopenharmony_ci        gc.collect()
1417db96d56Sopenharmony_ci        del a
1427db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1437db96d56Sopenharmony_ci        class B(list):
1447db96d56Sopenharmony_ci            pass
1457db96d56Sopenharmony_ci        class C(B, A):
1467db96d56Sopenharmony_ci            pass
1477db96d56Sopenharmony_ci        a = C()
1487db96d56Sopenharmony_ci        a.a = a
1497db96d56Sopenharmony_ci        gc.collect()
1507db96d56Sopenharmony_ci        del a
1517db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1527db96d56Sopenharmony_ci        del B, C
1537db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1547db96d56Sopenharmony_ci        A.a = A()
1557db96d56Sopenharmony_ci        del A
1567db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1577db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 0)
1587db96d56Sopenharmony_ci
1597db96d56Sopenharmony_ci    def test_method(self):
1607db96d56Sopenharmony_ci        # Tricky: self.__init__ is a bound method, it references the instance.
1617db96d56Sopenharmony_ci        class A:
1627db96d56Sopenharmony_ci            def __init__(self):
1637db96d56Sopenharmony_ci                self.init = self.__init__
1647db96d56Sopenharmony_ci        a = A()
1657db96d56Sopenharmony_ci        gc.collect()
1667db96d56Sopenharmony_ci        del a
1677db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1687db96d56Sopenharmony_ci
1697db96d56Sopenharmony_ci    @cpython_only
1707db96d56Sopenharmony_ci    def test_legacy_finalizer(self):
1717db96d56Sopenharmony_ci        # A() is uncollectable if it is part of a cycle, make sure it shows up
1727db96d56Sopenharmony_ci        # in gc.garbage.
1737db96d56Sopenharmony_ci        @with_tp_del
1747db96d56Sopenharmony_ci        class A:
1757db96d56Sopenharmony_ci            def __tp_del__(self): pass
1767db96d56Sopenharmony_ci        class B:
1777db96d56Sopenharmony_ci            pass
1787db96d56Sopenharmony_ci        a = A()
1797db96d56Sopenharmony_ci        a.a = a
1807db96d56Sopenharmony_ci        id_a = id(a)
1817db96d56Sopenharmony_ci        b = B()
1827db96d56Sopenharmony_ci        b.b = b
1837db96d56Sopenharmony_ci        gc.collect()
1847db96d56Sopenharmony_ci        del a
1857db96d56Sopenharmony_ci        del b
1867db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
1877db96d56Sopenharmony_ci        for obj in gc.garbage:
1887db96d56Sopenharmony_ci            if id(obj) == id_a:
1897db96d56Sopenharmony_ci                del obj.a
1907db96d56Sopenharmony_ci                break
1917db96d56Sopenharmony_ci        else:
1927db96d56Sopenharmony_ci            self.fail("didn't find obj in garbage (finalizer)")
1937db96d56Sopenharmony_ci        gc.garbage.remove(obj)
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ci    @cpython_only
1967db96d56Sopenharmony_ci    def test_legacy_finalizer_newclass(self):
1977db96d56Sopenharmony_ci        # A() is uncollectable if it is part of a cycle, make sure it shows up
1987db96d56Sopenharmony_ci        # in gc.garbage.
1997db96d56Sopenharmony_ci        @with_tp_del
2007db96d56Sopenharmony_ci        class A(object):
2017db96d56Sopenharmony_ci            def __tp_del__(self): pass
2027db96d56Sopenharmony_ci        class B(object):
2037db96d56Sopenharmony_ci            pass
2047db96d56Sopenharmony_ci        a = A()
2057db96d56Sopenharmony_ci        a.a = a
2067db96d56Sopenharmony_ci        id_a = id(a)
2077db96d56Sopenharmony_ci        b = B()
2087db96d56Sopenharmony_ci        b.b = b
2097db96d56Sopenharmony_ci        gc.collect()
2107db96d56Sopenharmony_ci        del a
2117db96d56Sopenharmony_ci        del b
2127db96d56Sopenharmony_ci        self.assertNotEqual(gc.collect(), 0)
2137db96d56Sopenharmony_ci        for obj in gc.garbage:
2147db96d56Sopenharmony_ci            if id(obj) == id_a:
2157db96d56Sopenharmony_ci                del obj.a
2167db96d56Sopenharmony_ci                break
2177db96d56Sopenharmony_ci        else:
2187db96d56Sopenharmony_ci            self.fail("didn't find obj in garbage (finalizer)")
2197db96d56Sopenharmony_ci        gc.garbage.remove(obj)
2207db96d56Sopenharmony_ci
2217db96d56Sopenharmony_ci    def test_function(self):
2227db96d56Sopenharmony_ci        # Tricky: f -> d -> f, code should call d.clear() after the exec to
2237db96d56Sopenharmony_ci        # break the cycle.
2247db96d56Sopenharmony_ci        d = {}
2257db96d56Sopenharmony_ci        exec("def f(): pass\n", d)
2267db96d56Sopenharmony_ci        gc.collect()
2277db96d56Sopenharmony_ci        del d
2287db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
2297db96d56Sopenharmony_ci
2307db96d56Sopenharmony_ci    def test_function_tp_clear_leaves_consistent_state(self):
2317db96d56Sopenharmony_ci        # https://github.com/python/cpython/issues/91636
2327db96d56Sopenharmony_ci        code = """if 1:
2337db96d56Sopenharmony_ci
2347db96d56Sopenharmony_ci        import gc
2357db96d56Sopenharmony_ci        import weakref
2367db96d56Sopenharmony_ci
2377db96d56Sopenharmony_ci        class LateFin:
2387db96d56Sopenharmony_ci            __slots__ = ('ref',)
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_ci            def __del__(self):
2417db96d56Sopenharmony_ci
2427db96d56Sopenharmony_ci                # 8. Now `latefin`'s finalizer is called. Here we
2437db96d56Sopenharmony_ci                #    obtain a reference to `func`, which is currently
2447db96d56Sopenharmony_ci                #    undergoing `tp_clear`.
2457db96d56Sopenharmony_ci                global func
2467db96d56Sopenharmony_ci                func = self.ref()
2477db96d56Sopenharmony_ci
2487db96d56Sopenharmony_ci        class Cyclic(tuple):
2497db96d56Sopenharmony_ci            __slots__ = ()
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ci            # 4. The finalizers of all garbage objects are called. In
2527db96d56Sopenharmony_ci            #    this case this is only us as `func` doesn't have a
2537db96d56Sopenharmony_ci            #    finalizer.
2547db96d56Sopenharmony_ci            def __del__(self):
2557db96d56Sopenharmony_ci
2567db96d56Sopenharmony_ci                # 5. Create a weakref to `func` now. If we had created
2577db96d56Sopenharmony_ci                #    it earlier, it would have been cleared by the
2587db96d56Sopenharmony_ci                #    garbage collector before calling the finalizers.
2597db96d56Sopenharmony_ci                self[1].ref = weakref.ref(self[0])
2607db96d56Sopenharmony_ci
2617db96d56Sopenharmony_ci                # 6. Drop the global reference to `latefin`. The only
2627db96d56Sopenharmony_ci                #    remaining reference is the one we have.
2637db96d56Sopenharmony_ci                global latefin
2647db96d56Sopenharmony_ci                del latefin
2657db96d56Sopenharmony_ci
2667db96d56Sopenharmony_ci            # 7. Now `func` is `tp_clear`-ed. This drops the last
2677db96d56Sopenharmony_ci            #    reference to `Cyclic`, which gets `tp_dealloc`-ed.
2687db96d56Sopenharmony_ci            #    This drops the last reference to `latefin`.
2697db96d56Sopenharmony_ci
2707db96d56Sopenharmony_ci        latefin = LateFin()
2717db96d56Sopenharmony_ci        def func():
2727db96d56Sopenharmony_ci            pass
2737db96d56Sopenharmony_ci        cyc = tuple.__new__(Cyclic, (func, latefin))
2747db96d56Sopenharmony_ci
2757db96d56Sopenharmony_ci        # 1. Create a reference cycle of `cyc` and `func`.
2767db96d56Sopenharmony_ci        func.__module__ = cyc
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci        # 2. Make the cycle unreachable, but keep the global reference
2797db96d56Sopenharmony_ci        #    to `latefin` so that it isn't detected as garbage. This
2807db96d56Sopenharmony_ci        #    way its finalizer will not be called immediately.
2817db96d56Sopenharmony_ci        del func, cyc
2827db96d56Sopenharmony_ci
2837db96d56Sopenharmony_ci        # 3. Invoke garbage collection,
2847db96d56Sopenharmony_ci        #    which will find `cyc` and `func` as garbage.
2857db96d56Sopenharmony_ci        gc.collect()
2867db96d56Sopenharmony_ci
2877db96d56Sopenharmony_ci        # 9. Previously, this would crash because `func_qualname`
2887db96d56Sopenharmony_ci        #    had been NULL-ed out by func_clear().
2897db96d56Sopenharmony_ci        print(f"{func=}")
2907db96d56Sopenharmony_ci        """
2917db96d56Sopenharmony_ci        # We're mostly just checking that this doesn't crash.
2927db96d56Sopenharmony_ci        rc, stdout, stderr = assert_python_ok("-c", code)
2937db96d56Sopenharmony_ci        self.assertEqual(rc, 0)
2947db96d56Sopenharmony_ci        self.assertRegex(stdout, rb"""\A\s*func=<function  at \S+>\s*\Z""")
2957db96d56Sopenharmony_ci        self.assertFalse(stderr)
2967db96d56Sopenharmony_ci
2977db96d56Sopenharmony_ci    @refcount_test
2987db96d56Sopenharmony_ci    def test_frame(self):
2997db96d56Sopenharmony_ci        def f():
3007db96d56Sopenharmony_ci            frame = sys._getframe()
3017db96d56Sopenharmony_ci        gc.collect()
3027db96d56Sopenharmony_ci        f()
3037db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 1)
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_ci    def test_saveall(self):
3067db96d56Sopenharmony_ci        # Verify that cyclic garbage like lists show up in gc.garbage if the
3077db96d56Sopenharmony_ci        # SAVEALL option is enabled.
3087db96d56Sopenharmony_ci
3097db96d56Sopenharmony_ci        # First make sure we don't save away other stuff that just happens to
3107db96d56Sopenharmony_ci        # be waiting for collection.
3117db96d56Sopenharmony_ci        gc.collect()
3127db96d56Sopenharmony_ci        # if this fails, someone else created immortal trash
3137db96d56Sopenharmony_ci        self.assertEqual(gc.garbage, [])
3147db96d56Sopenharmony_ci
3157db96d56Sopenharmony_ci        L = []
3167db96d56Sopenharmony_ci        L.append(L)
3177db96d56Sopenharmony_ci        id_L = id(L)
3187db96d56Sopenharmony_ci
3197db96d56Sopenharmony_ci        debug = gc.get_debug()
3207db96d56Sopenharmony_ci        gc.set_debug(debug | gc.DEBUG_SAVEALL)
3217db96d56Sopenharmony_ci        del L
3227db96d56Sopenharmony_ci        gc.collect()
3237db96d56Sopenharmony_ci        gc.set_debug(debug)
3247db96d56Sopenharmony_ci
3257db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), 1)
3267db96d56Sopenharmony_ci        obj = gc.garbage.pop()
3277db96d56Sopenharmony_ci        self.assertEqual(id(obj), id_L)
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci    def test_del(self):
3307db96d56Sopenharmony_ci        # __del__ methods can trigger collection, make this to happen
3317db96d56Sopenharmony_ci        thresholds = gc.get_threshold()
3327db96d56Sopenharmony_ci        gc.enable()
3337db96d56Sopenharmony_ci        gc.set_threshold(1)
3347db96d56Sopenharmony_ci
3357db96d56Sopenharmony_ci        class A:
3367db96d56Sopenharmony_ci            def __del__(self):
3377db96d56Sopenharmony_ci                dir(self)
3387db96d56Sopenharmony_ci        a = A()
3397db96d56Sopenharmony_ci        del a
3407db96d56Sopenharmony_ci
3417db96d56Sopenharmony_ci        gc.disable()
3427db96d56Sopenharmony_ci        gc.set_threshold(*thresholds)
3437db96d56Sopenharmony_ci
3447db96d56Sopenharmony_ci    def test_del_newclass(self):
3457db96d56Sopenharmony_ci        # __del__ methods can trigger collection, make this to happen
3467db96d56Sopenharmony_ci        thresholds = gc.get_threshold()
3477db96d56Sopenharmony_ci        gc.enable()
3487db96d56Sopenharmony_ci        gc.set_threshold(1)
3497db96d56Sopenharmony_ci
3507db96d56Sopenharmony_ci        class A(object):
3517db96d56Sopenharmony_ci            def __del__(self):
3527db96d56Sopenharmony_ci                dir(self)
3537db96d56Sopenharmony_ci        a = A()
3547db96d56Sopenharmony_ci        del a
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci        gc.disable()
3577db96d56Sopenharmony_ci        gc.set_threshold(*thresholds)
3587db96d56Sopenharmony_ci
3597db96d56Sopenharmony_ci    # The following two tests are fragile:
3607db96d56Sopenharmony_ci    # They precisely count the number of allocations,
3617db96d56Sopenharmony_ci    # which is highly implementation-dependent.
3627db96d56Sopenharmony_ci    # For example, disposed tuples are not freed, but reused.
3637db96d56Sopenharmony_ci    # To minimize variations, though, we first store the get_count() results
3647db96d56Sopenharmony_ci    # and check them at the end.
3657db96d56Sopenharmony_ci    @refcount_test
3667db96d56Sopenharmony_ci    def test_get_count(self):
3677db96d56Sopenharmony_ci        gc.collect()
3687db96d56Sopenharmony_ci        a, b, c = gc.get_count()
3697db96d56Sopenharmony_ci        x = []
3707db96d56Sopenharmony_ci        d, e, f = gc.get_count()
3717db96d56Sopenharmony_ci        self.assertEqual((b, c), (0, 0))
3727db96d56Sopenharmony_ci        self.assertEqual((e, f), (0, 0))
3737db96d56Sopenharmony_ci        # This is less fragile than asserting that a equals 0.
3747db96d56Sopenharmony_ci        self.assertLess(a, 5)
3757db96d56Sopenharmony_ci        # Between the two calls to get_count(), at least one object was
3767db96d56Sopenharmony_ci        # created (the list).
3777db96d56Sopenharmony_ci        self.assertGreater(d, a)
3787db96d56Sopenharmony_ci
3797db96d56Sopenharmony_ci    @refcount_test
3807db96d56Sopenharmony_ci    def test_collect_generations(self):
3817db96d56Sopenharmony_ci        gc.collect()
3827db96d56Sopenharmony_ci        # This object will "trickle" into generation N + 1 after
3837db96d56Sopenharmony_ci        # each call to collect(N)
3847db96d56Sopenharmony_ci        x = []
3857db96d56Sopenharmony_ci        gc.collect(0)
3867db96d56Sopenharmony_ci        # x is now in gen 1
3877db96d56Sopenharmony_ci        a, b, c = gc.get_count()
3887db96d56Sopenharmony_ci        gc.collect(1)
3897db96d56Sopenharmony_ci        # x is now in gen 2
3907db96d56Sopenharmony_ci        d, e, f = gc.get_count()
3917db96d56Sopenharmony_ci        gc.collect(2)
3927db96d56Sopenharmony_ci        # x is now in gen 3
3937db96d56Sopenharmony_ci        g, h, i = gc.get_count()
3947db96d56Sopenharmony_ci        # We don't check a, d, g since their exact values depends on
3957db96d56Sopenharmony_ci        # internal implementation details of the interpreter.
3967db96d56Sopenharmony_ci        self.assertEqual((b, c), (1, 0))
3977db96d56Sopenharmony_ci        self.assertEqual((e, f), (0, 1))
3987db96d56Sopenharmony_ci        self.assertEqual((h, i), (0, 0))
3997db96d56Sopenharmony_ci
4007db96d56Sopenharmony_ci    def test_trashcan(self):
4017db96d56Sopenharmony_ci        class Ouch:
4027db96d56Sopenharmony_ci            n = 0
4037db96d56Sopenharmony_ci            def __del__(self):
4047db96d56Sopenharmony_ci                Ouch.n = Ouch.n + 1
4057db96d56Sopenharmony_ci                if Ouch.n % 17 == 0:
4067db96d56Sopenharmony_ci                    gc.collect()
4077db96d56Sopenharmony_ci
4087db96d56Sopenharmony_ci        # "trashcan" is a hack to prevent stack overflow when deallocating
4097db96d56Sopenharmony_ci        # very deeply nested tuples etc.  It works in part by abusing the
4107db96d56Sopenharmony_ci        # type pointer and refcount fields, and that can yield horrible
4117db96d56Sopenharmony_ci        # problems when gc tries to traverse the structures.
4127db96d56Sopenharmony_ci        # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
4137db96d56Sopenharmony_ci        # most likely die via segfault.
4147db96d56Sopenharmony_ci
4157db96d56Sopenharmony_ci        # Note:  In 2.3 the possibility for compiling without cyclic gc was
4167db96d56Sopenharmony_ci        # removed, and that in turn allows the trashcan mechanism to work
4177db96d56Sopenharmony_ci        # via much simpler means (e.g., it never abuses the type pointer or
4187db96d56Sopenharmony_ci        # refcount fields anymore).  Since it's much less likely to cause a
4197db96d56Sopenharmony_ci        # problem now, the various constants in this expensive (we force a lot
4207db96d56Sopenharmony_ci        # of full collections) test are cut back from the 2.2 version.
4217db96d56Sopenharmony_ci        gc.enable()
4227db96d56Sopenharmony_ci        N = 150
4237db96d56Sopenharmony_ci        for count in range(2):
4247db96d56Sopenharmony_ci            t = []
4257db96d56Sopenharmony_ci            for i in range(N):
4267db96d56Sopenharmony_ci                t = [t, Ouch()]
4277db96d56Sopenharmony_ci            u = []
4287db96d56Sopenharmony_ci            for i in range(N):
4297db96d56Sopenharmony_ci                u = [u, Ouch()]
4307db96d56Sopenharmony_ci            v = {}
4317db96d56Sopenharmony_ci            for i in range(N):
4327db96d56Sopenharmony_ci                v = {1: v, 2: Ouch()}
4337db96d56Sopenharmony_ci        gc.disable()
4347db96d56Sopenharmony_ci
4357db96d56Sopenharmony_ci    @threading_helper.requires_working_threading()
4367db96d56Sopenharmony_ci    def test_trashcan_threads(self):
4377db96d56Sopenharmony_ci        # Issue #13992: trashcan mechanism should be thread-safe
4387db96d56Sopenharmony_ci        NESTING = 60
4397db96d56Sopenharmony_ci        N_THREADS = 2
4407db96d56Sopenharmony_ci
4417db96d56Sopenharmony_ci        def sleeper_gen():
4427db96d56Sopenharmony_ci            """A generator that releases the GIL when closed or dealloc'ed."""
4437db96d56Sopenharmony_ci            try:
4447db96d56Sopenharmony_ci                yield
4457db96d56Sopenharmony_ci            finally:
4467db96d56Sopenharmony_ci                time.sleep(0.000001)
4477db96d56Sopenharmony_ci
4487db96d56Sopenharmony_ci        class C(list):
4497db96d56Sopenharmony_ci            # Appending to a list is atomic, which avoids the use of a lock.
4507db96d56Sopenharmony_ci            inits = []
4517db96d56Sopenharmony_ci            dels = []
4527db96d56Sopenharmony_ci            def __init__(self, alist):
4537db96d56Sopenharmony_ci                self[:] = alist
4547db96d56Sopenharmony_ci                C.inits.append(None)
4557db96d56Sopenharmony_ci            def __del__(self):
4567db96d56Sopenharmony_ci                # This __del__ is called by subtype_dealloc().
4577db96d56Sopenharmony_ci                C.dels.append(None)
4587db96d56Sopenharmony_ci                # `g` will release the GIL when garbage-collected.  This
4597db96d56Sopenharmony_ci                # helps assert subtype_dealloc's behaviour when threads
4607db96d56Sopenharmony_ci                # switch in the middle of it.
4617db96d56Sopenharmony_ci                g = sleeper_gen()
4627db96d56Sopenharmony_ci                next(g)
4637db96d56Sopenharmony_ci                # Now that __del__ is finished, subtype_dealloc will proceed
4647db96d56Sopenharmony_ci                # to call list_dealloc, which also uses the trashcan mechanism.
4657db96d56Sopenharmony_ci
4667db96d56Sopenharmony_ci        def make_nested():
4677db96d56Sopenharmony_ci            """Create a sufficiently nested container object so that the
4687db96d56Sopenharmony_ci            trashcan mechanism is invoked when deallocating it."""
4697db96d56Sopenharmony_ci            x = C([])
4707db96d56Sopenharmony_ci            for i in range(NESTING):
4717db96d56Sopenharmony_ci                x = [C([x])]
4727db96d56Sopenharmony_ci            del x
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ci        def run_thread():
4757db96d56Sopenharmony_ci            """Exercise make_nested() in a loop."""
4767db96d56Sopenharmony_ci            while not exit:
4777db96d56Sopenharmony_ci                make_nested()
4787db96d56Sopenharmony_ci
4797db96d56Sopenharmony_ci        old_switchinterval = sys.getswitchinterval()
4807db96d56Sopenharmony_ci        sys.setswitchinterval(1e-5)
4817db96d56Sopenharmony_ci        try:
4827db96d56Sopenharmony_ci            exit = []
4837db96d56Sopenharmony_ci            threads = []
4847db96d56Sopenharmony_ci            for i in range(N_THREADS):
4857db96d56Sopenharmony_ci                t = threading.Thread(target=run_thread)
4867db96d56Sopenharmony_ci                threads.append(t)
4877db96d56Sopenharmony_ci            with threading_helper.start_threads(threads, lambda: exit.append(1)):
4887db96d56Sopenharmony_ci                time.sleep(1.0)
4897db96d56Sopenharmony_ci        finally:
4907db96d56Sopenharmony_ci            sys.setswitchinterval(old_switchinterval)
4917db96d56Sopenharmony_ci        gc.collect()
4927db96d56Sopenharmony_ci        self.assertEqual(len(C.inits), len(C.dels))
4937db96d56Sopenharmony_ci
4947db96d56Sopenharmony_ci    def test_boom(self):
4957db96d56Sopenharmony_ci        class Boom:
4967db96d56Sopenharmony_ci            def __getattr__(self, someattribute):
4977db96d56Sopenharmony_ci                del self.attr
4987db96d56Sopenharmony_ci                raise AttributeError
4997db96d56Sopenharmony_ci
5007db96d56Sopenharmony_ci        a = Boom()
5017db96d56Sopenharmony_ci        b = Boom()
5027db96d56Sopenharmony_ci        a.attr = b
5037db96d56Sopenharmony_ci        b.attr = a
5047db96d56Sopenharmony_ci
5057db96d56Sopenharmony_ci        gc.collect()
5067db96d56Sopenharmony_ci        garbagelen = len(gc.garbage)
5077db96d56Sopenharmony_ci        del a, b
5087db96d56Sopenharmony_ci        # a<->b are in a trash cycle now.  Collection will invoke
5097db96d56Sopenharmony_ci        # Boom.__getattr__ (to see whether a and b have __del__ methods), and
5107db96d56Sopenharmony_ci        # __getattr__ deletes the internal "attr" attributes as a side effect.
5117db96d56Sopenharmony_ci        # That causes the trash cycle to get reclaimed via refcounts falling to
5127db96d56Sopenharmony_ci        # 0, thus mutating the trash graph as a side effect of merely asking
5137db96d56Sopenharmony_ci        # whether __del__ exists.  This used to (before 2.3b1) crash Python.
5147db96d56Sopenharmony_ci        # Now __getattr__ isn't called.
5157db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
5167db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), garbagelen)
5177db96d56Sopenharmony_ci
5187db96d56Sopenharmony_ci    def test_boom2(self):
5197db96d56Sopenharmony_ci        class Boom2:
5207db96d56Sopenharmony_ci            def __init__(self):
5217db96d56Sopenharmony_ci                self.x = 0
5227db96d56Sopenharmony_ci
5237db96d56Sopenharmony_ci            def __getattr__(self, someattribute):
5247db96d56Sopenharmony_ci                self.x += 1
5257db96d56Sopenharmony_ci                if self.x > 1:
5267db96d56Sopenharmony_ci                    del self.attr
5277db96d56Sopenharmony_ci                raise AttributeError
5287db96d56Sopenharmony_ci
5297db96d56Sopenharmony_ci        a = Boom2()
5307db96d56Sopenharmony_ci        b = Boom2()
5317db96d56Sopenharmony_ci        a.attr = b
5327db96d56Sopenharmony_ci        b.attr = a
5337db96d56Sopenharmony_ci
5347db96d56Sopenharmony_ci        gc.collect()
5357db96d56Sopenharmony_ci        garbagelen = len(gc.garbage)
5367db96d56Sopenharmony_ci        del a, b
5377db96d56Sopenharmony_ci        # Much like test_boom(), except that __getattr__ doesn't break the
5387db96d56Sopenharmony_ci        # cycle until the second time gc checks for __del__.  As of 2.3b1,
5397db96d56Sopenharmony_ci        # there isn't a second time, so this simply cleans up the trash cycle.
5407db96d56Sopenharmony_ci        # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
5417db96d56Sopenharmony_ci        # reclaimed this way.
5427db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
5437db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), garbagelen)
5447db96d56Sopenharmony_ci
5457db96d56Sopenharmony_ci    def test_boom_new(self):
5467db96d56Sopenharmony_ci        # boom__new and boom2_new are exactly like boom and boom2, except use
5477db96d56Sopenharmony_ci        # new-style classes.
5487db96d56Sopenharmony_ci
5497db96d56Sopenharmony_ci        class Boom_New(object):
5507db96d56Sopenharmony_ci            def __getattr__(self, someattribute):
5517db96d56Sopenharmony_ci                del self.attr
5527db96d56Sopenharmony_ci                raise AttributeError
5537db96d56Sopenharmony_ci
5547db96d56Sopenharmony_ci        a = Boom_New()
5557db96d56Sopenharmony_ci        b = Boom_New()
5567db96d56Sopenharmony_ci        a.attr = b
5577db96d56Sopenharmony_ci        b.attr = a
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci        gc.collect()
5607db96d56Sopenharmony_ci        garbagelen = len(gc.garbage)
5617db96d56Sopenharmony_ci        del a, b
5627db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
5637db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), garbagelen)
5647db96d56Sopenharmony_ci
5657db96d56Sopenharmony_ci    def test_boom2_new(self):
5667db96d56Sopenharmony_ci        class Boom2_New(object):
5677db96d56Sopenharmony_ci            def __init__(self):
5687db96d56Sopenharmony_ci                self.x = 0
5697db96d56Sopenharmony_ci
5707db96d56Sopenharmony_ci            def __getattr__(self, someattribute):
5717db96d56Sopenharmony_ci                self.x += 1
5727db96d56Sopenharmony_ci                if self.x > 1:
5737db96d56Sopenharmony_ci                    del self.attr
5747db96d56Sopenharmony_ci                raise AttributeError
5757db96d56Sopenharmony_ci
5767db96d56Sopenharmony_ci        a = Boom2_New()
5777db96d56Sopenharmony_ci        b = Boom2_New()
5787db96d56Sopenharmony_ci        a.attr = b
5797db96d56Sopenharmony_ci        b.attr = a
5807db96d56Sopenharmony_ci
5817db96d56Sopenharmony_ci        gc.collect()
5827db96d56Sopenharmony_ci        garbagelen = len(gc.garbage)
5837db96d56Sopenharmony_ci        del a, b
5847db96d56Sopenharmony_ci        self.assertEqual(gc.collect(), 2)
5857db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), garbagelen)
5867db96d56Sopenharmony_ci
5877db96d56Sopenharmony_ci    def test_get_referents(self):
5887db96d56Sopenharmony_ci        alist = [1, 3, 5]
5897db96d56Sopenharmony_ci        got = gc.get_referents(alist)
5907db96d56Sopenharmony_ci        got.sort()
5917db96d56Sopenharmony_ci        self.assertEqual(got, alist)
5927db96d56Sopenharmony_ci
5937db96d56Sopenharmony_ci        atuple = tuple(alist)
5947db96d56Sopenharmony_ci        got = gc.get_referents(atuple)
5957db96d56Sopenharmony_ci        got.sort()
5967db96d56Sopenharmony_ci        self.assertEqual(got, alist)
5977db96d56Sopenharmony_ci
5987db96d56Sopenharmony_ci        adict = {1: 3, 5: 7}
5997db96d56Sopenharmony_ci        expected = [1, 3, 5, 7]
6007db96d56Sopenharmony_ci        got = gc.get_referents(adict)
6017db96d56Sopenharmony_ci        got.sort()
6027db96d56Sopenharmony_ci        self.assertEqual(got, expected)
6037db96d56Sopenharmony_ci
6047db96d56Sopenharmony_ci        got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
6057db96d56Sopenharmony_ci        got.sort()
6067db96d56Sopenharmony_ci        self.assertEqual(got, [0, 0] + list(range(5)))
6077db96d56Sopenharmony_ci
6087db96d56Sopenharmony_ci        self.assertEqual(gc.get_referents(1, 'a', 4j), [])
6097db96d56Sopenharmony_ci
6107db96d56Sopenharmony_ci    def test_is_tracked(self):
6117db96d56Sopenharmony_ci        # Atomic built-in types are not tracked, user-defined objects and
6127db96d56Sopenharmony_ci        # mutable containers are.
6137db96d56Sopenharmony_ci        # NOTE: types with special optimizations (e.g. tuple) have tests
6147db96d56Sopenharmony_ci        # in their own test files instead.
6157db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(None))
6167db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(1))
6177db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(1.0))
6187db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(1.0 + 5.0j))
6197db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(True))
6207db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(False))
6217db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(b"a"))
6227db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked("a"))
6237db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(bytearray(b"a")))
6247db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(type))
6257db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(int))
6267db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(object))
6277db96d56Sopenharmony_ci        self.assertFalse(gc.is_tracked(object()))
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci        class UserClass:
6307db96d56Sopenharmony_ci            pass
6317db96d56Sopenharmony_ci
6327db96d56Sopenharmony_ci        class UserInt(int):
6337db96d56Sopenharmony_ci            pass
6347db96d56Sopenharmony_ci
6357db96d56Sopenharmony_ci        # Base class is object; no extra fields.
6367db96d56Sopenharmony_ci        class UserClassSlots:
6377db96d56Sopenharmony_ci            __slots__ = ()
6387db96d56Sopenharmony_ci
6397db96d56Sopenharmony_ci        # Base class is fixed size larger than object; no extra fields.
6407db96d56Sopenharmony_ci        class UserFloatSlots(float):
6417db96d56Sopenharmony_ci            __slots__ = ()
6427db96d56Sopenharmony_ci
6437db96d56Sopenharmony_ci        # Base class is variable size; no extra fields.
6447db96d56Sopenharmony_ci        class UserIntSlots(int):
6457db96d56Sopenharmony_ci            __slots__ = ()
6467db96d56Sopenharmony_ci
6477db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(gc))
6487db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserClass))
6497db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserClass()))
6507db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserInt()))
6517db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked([]))
6527db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(set()))
6537db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserClassSlots()))
6547db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserFloatSlots()))
6557db96d56Sopenharmony_ci        self.assertTrue(gc.is_tracked(UserIntSlots()))
6567db96d56Sopenharmony_ci
6577db96d56Sopenharmony_ci    def test_is_finalized(self):
6587db96d56Sopenharmony_ci        # Objects not tracked by the always gc return false
6597db96d56Sopenharmony_ci        self.assertFalse(gc.is_finalized(3))
6607db96d56Sopenharmony_ci
6617db96d56Sopenharmony_ci        storage = []
6627db96d56Sopenharmony_ci        class Lazarus:
6637db96d56Sopenharmony_ci            def __del__(self):
6647db96d56Sopenharmony_ci                storage.append(self)
6657db96d56Sopenharmony_ci
6667db96d56Sopenharmony_ci        lazarus = Lazarus()
6677db96d56Sopenharmony_ci        self.assertFalse(gc.is_finalized(lazarus))
6687db96d56Sopenharmony_ci
6697db96d56Sopenharmony_ci        del lazarus
6707db96d56Sopenharmony_ci        gc.collect()
6717db96d56Sopenharmony_ci
6727db96d56Sopenharmony_ci        lazarus = storage.pop()
6737db96d56Sopenharmony_ci        self.assertTrue(gc.is_finalized(lazarus))
6747db96d56Sopenharmony_ci
6757db96d56Sopenharmony_ci    def test_bug1055820b(self):
6767db96d56Sopenharmony_ci        # Corresponds to temp2b.py in the bug report.
6777db96d56Sopenharmony_ci
6787db96d56Sopenharmony_ci        ouch = []
6797db96d56Sopenharmony_ci        def callback(ignored):
6807db96d56Sopenharmony_ci            ouch[:] = [wr() for wr in WRs]
6817db96d56Sopenharmony_ci
6827db96d56Sopenharmony_ci        Cs = [C1055820(i) for i in range(2)]
6837db96d56Sopenharmony_ci        WRs = [weakref.ref(c, callback) for c in Cs]
6847db96d56Sopenharmony_ci        c = None
6857db96d56Sopenharmony_ci
6867db96d56Sopenharmony_ci        gc.collect()
6877db96d56Sopenharmony_ci        self.assertEqual(len(ouch), 0)
6887db96d56Sopenharmony_ci        # Make the two instances trash, and collect again.  The bug was that
6897db96d56Sopenharmony_ci        # the callback materialized a strong reference to an instance, but gc
6907db96d56Sopenharmony_ci        # cleared the instance's dict anyway.
6917db96d56Sopenharmony_ci        Cs = None
6927db96d56Sopenharmony_ci        gc.collect()
6937db96d56Sopenharmony_ci        self.assertEqual(len(ouch), 2)  # else the callbacks didn't run
6947db96d56Sopenharmony_ci        for x in ouch:
6957db96d56Sopenharmony_ci            # If the callback resurrected one of these guys, the instance
6967db96d56Sopenharmony_ci            # would be damaged, with an empty __dict__.
6977db96d56Sopenharmony_ci            self.assertEqual(x, None)
6987db96d56Sopenharmony_ci
6997db96d56Sopenharmony_ci    def test_bug21435(self):
7007db96d56Sopenharmony_ci        # This is a poor test - its only virtue is that it happened to
7017db96d56Sopenharmony_ci        # segfault on Tim's Windows box before the patch for 21435 was
7027db96d56Sopenharmony_ci        # applied.  That's a nasty bug relying on specific pieces of cyclic
7037db96d56Sopenharmony_ci        # trash appearing in exactly the right order in finalize_garbage()'s
7047db96d56Sopenharmony_ci        # input list.
7057db96d56Sopenharmony_ci        # But there's no reliable way to force that order from Python code,
7067db96d56Sopenharmony_ci        # so over time chances are good this test won't really be testing much
7077db96d56Sopenharmony_ci        # of anything anymore.  Still, if it blows up, there's _some_
7087db96d56Sopenharmony_ci        # problem ;-)
7097db96d56Sopenharmony_ci        gc.collect()
7107db96d56Sopenharmony_ci
7117db96d56Sopenharmony_ci        class A:
7127db96d56Sopenharmony_ci            pass
7137db96d56Sopenharmony_ci
7147db96d56Sopenharmony_ci        class B:
7157db96d56Sopenharmony_ci            def __init__(self, x):
7167db96d56Sopenharmony_ci                self.x = x
7177db96d56Sopenharmony_ci
7187db96d56Sopenharmony_ci            def __del__(self):
7197db96d56Sopenharmony_ci                self.attr = None
7207db96d56Sopenharmony_ci
7217db96d56Sopenharmony_ci        def do_work():
7227db96d56Sopenharmony_ci            a = A()
7237db96d56Sopenharmony_ci            b = B(A())
7247db96d56Sopenharmony_ci
7257db96d56Sopenharmony_ci            a.attr = b
7267db96d56Sopenharmony_ci            b.attr = a
7277db96d56Sopenharmony_ci
7287db96d56Sopenharmony_ci        do_work()
7297db96d56Sopenharmony_ci        gc.collect() # this blows up (bad C pointer) when it fails
7307db96d56Sopenharmony_ci
7317db96d56Sopenharmony_ci    @cpython_only
7327db96d56Sopenharmony_ci    @requires_subprocess()
7337db96d56Sopenharmony_ci    def test_garbage_at_shutdown(self):
7347db96d56Sopenharmony_ci        import subprocess
7357db96d56Sopenharmony_ci        code = """if 1:
7367db96d56Sopenharmony_ci            import gc
7377db96d56Sopenharmony_ci            import _testcapi
7387db96d56Sopenharmony_ci            @_testcapi.with_tp_del
7397db96d56Sopenharmony_ci            class X:
7407db96d56Sopenharmony_ci                def __init__(self, name):
7417db96d56Sopenharmony_ci                    self.name = name
7427db96d56Sopenharmony_ci                def __repr__(self):
7437db96d56Sopenharmony_ci                    return "<X %%r>" %% self.name
7447db96d56Sopenharmony_ci                def __tp_del__(self):
7457db96d56Sopenharmony_ci                    pass
7467db96d56Sopenharmony_ci
7477db96d56Sopenharmony_ci            x = X('first')
7487db96d56Sopenharmony_ci            x.x = x
7497db96d56Sopenharmony_ci            x.y = X('second')
7507db96d56Sopenharmony_ci            del x
7517db96d56Sopenharmony_ci            gc.set_debug(%s)
7527db96d56Sopenharmony_ci        """
7537db96d56Sopenharmony_ci        def run_command(code):
7547db96d56Sopenharmony_ci            p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
7557db96d56Sopenharmony_ci                stdout=subprocess.PIPE,
7567db96d56Sopenharmony_ci                stderr=subprocess.PIPE)
7577db96d56Sopenharmony_ci            stdout, stderr = p.communicate()
7587db96d56Sopenharmony_ci            p.stdout.close()
7597db96d56Sopenharmony_ci            p.stderr.close()
7607db96d56Sopenharmony_ci            self.assertEqual(p.returncode, 0)
7617db96d56Sopenharmony_ci            self.assertEqual(stdout, b"")
7627db96d56Sopenharmony_ci            return stderr
7637db96d56Sopenharmony_ci
7647db96d56Sopenharmony_ci        stderr = run_command(code % "0")
7657db96d56Sopenharmony_ci        self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
7667db96d56Sopenharmony_ci                      b"shutdown; use", stderr)
7677db96d56Sopenharmony_ci        self.assertNotIn(b"<X 'first'>", stderr)
7687db96d56Sopenharmony_ci        # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
7697db96d56Sopenharmony_ci        stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
7707db96d56Sopenharmony_ci        self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
7717db96d56Sopenharmony_ci                      b"shutdown", stderr)
7727db96d56Sopenharmony_ci        self.assertTrue(
7737db96d56Sopenharmony_ci            (b"[<X 'first'>, <X 'second'>]" in stderr) or
7747db96d56Sopenharmony_ci            (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
7757db96d56Sopenharmony_ci        # With DEBUG_SAVEALL, no additional message should get printed
7767db96d56Sopenharmony_ci        # (because gc.garbage also contains normally reclaimable cyclic
7777db96d56Sopenharmony_ci        # references, and its elements get printed at runtime anyway).
7787db96d56Sopenharmony_ci        stderr = run_command(code % "gc.DEBUG_SAVEALL")
7797db96d56Sopenharmony_ci        self.assertNotIn(b"uncollectable objects at shutdown", stderr)
7807db96d56Sopenharmony_ci
7817db96d56Sopenharmony_ci    def test_gc_main_module_at_shutdown(self):
7827db96d56Sopenharmony_ci        # Create a reference cycle through the __main__ module and check
7837db96d56Sopenharmony_ci        # it gets collected at interpreter shutdown.
7847db96d56Sopenharmony_ci        code = """if 1:
7857db96d56Sopenharmony_ci            class C:
7867db96d56Sopenharmony_ci                def __del__(self):
7877db96d56Sopenharmony_ci                    print('__del__ called')
7887db96d56Sopenharmony_ci            l = [C()]
7897db96d56Sopenharmony_ci            l.append(l)
7907db96d56Sopenharmony_ci            """
7917db96d56Sopenharmony_ci        rc, out, err = assert_python_ok('-c', code)
7927db96d56Sopenharmony_ci        self.assertEqual(out.strip(), b'__del__ called')
7937db96d56Sopenharmony_ci
7947db96d56Sopenharmony_ci    def test_gc_ordinary_module_at_shutdown(self):
7957db96d56Sopenharmony_ci        # Same as above, but with a non-__main__ module.
7967db96d56Sopenharmony_ci        with temp_dir() as script_dir:
7977db96d56Sopenharmony_ci            module = """if 1:
7987db96d56Sopenharmony_ci                class C:
7997db96d56Sopenharmony_ci                    def __del__(self):
8007db96d56Sopenharmony_ci                        print('__del__ called')
8017db96d56Sopenharmony_ci                l = [C()]
8027db96d56Sopenharmony_ci                l.append(l)
8037db96d56Sopenharmony_ci                """
8047db96d56Sopenharmony_ci            code = """if 1:
8057db96d56Sopenharmony_ci                import sys
8067db96d56Sopenharmony_ci                sys.path.insert(0, %r)
8077db96d56Sopenharmony_ci                import gctest
8087db96d56Sopenharmony_ci                """ % (script_dir,)
8097db96d56Sopenharmony_ci            make_script(script_dir, 'gctest', module)
8107db96d56Sopenharmony_ci            rc, out, err = assert_python_ok('-c', code)
8117db96d56Sopenharmony_ci            self.assertEqual(out.strip(), b'__del__ called')
8127db96d56Sopenharmony_ci
8137db96d56Sopenharmony_ci    def test_global_del_SystemExit(self):
8147db96d56Sopenharmony_ci        code = """if 1:
8157db96d56Sopenharmony_ci            class ClassWithDel:
8167db96d56Sopenharmony_ci                def __del__(self):
8177db96d56Sopenharmony_ci                    print('__del__ called')
8187db96d56Sopenharmony_ci            a = ClassWithDel()
8197db96d56Sopenharmony_ci            a.link = a
8207db96d56Sopenharmony_ci            raise SystemExit(0)"""
8217db96d56Sopenharmony_ci        self.addCleanup(unlink, TESTFN)
8227db96d56Sopenharmony_ci        with open(TESTFN, 'w', encoding="utf-8") as script:
8237db96d56Sopenharmony_ci            script.write(code)
8247db96d56Sopenharmony_ci        rc, out, err = assert_python_ok(TESTFN)
8257db96d56Sopenharmony_ci        self.assertEqual(out.strip(), b'__del__ called')
8267db96d56Sopenharmony_ci
8277db96d56Sopenharmony_ci    def test_get_stats(self):
8287db96d56Sopenharmony_ci        stats = gc.get_stats()
8297db96d56Sopenharmony_ci        self.assertEqual(len(stats), 3)
8307db96d56Sopenharmony_ci        for st in stats:
8317db96d56Sopenharmony_ci            self.assertIsInstance(st, dict)
8327db96d56Sopenharmony_ci            self.assertEqual(set(st),
8337db96d56Sopenharmony_ci                             {"collected", "collections", "uncollectable"})
8347db96d56Sopenharmony_ci            self.assertGreaterEqual(st["collected"], 0)
8357db96d56Sopenharmony_ci            self.assertGreaterEqual(st["collections"], 0)
8367db96d56Sopenharmony_ci            self.assertGreaterEqual(st["uncollectable"], 0)
8377db96d56Sopenharmony_ci        # Check that collection counts are incremented correctly
8387db96d56Sopenharmony_ci        if gc.isenabled():
8397db96d56Sopenharmony_ci            self.addCleanup(gc.enable)
8407db96d56Sopenharmony_ci            gc.disable()
8417db96d56Sopenharmony_ci        old = gc.get_stats()
8427db96d56Sopenharmony_ci        gc.collect(0)
8437db96d56Sopenharmony_ci        new = gc.get_stats()
8447db96d56Sopenharmony_ci        self.assertEqual(new[0]["collections"], old[0]["collections"] + 1)
8457db96d56Sopenharmony_ci        self.assertEqual(new[1]["collections"], old[1]["collections"])
8467db96d56Sopenharmony_ci        self.assertEqual(new[2]["collections"], old[2]["collections"])
8477db96d56Sopenharmony_ci        gc.collect(2)
8487db96d56Sopenharmony_ci        new = gc.get_stats()
8497db96d56Sopenharmony_ci        self.assertEqual(new[0]["collections"], old[0]["collections"] + 1)
8507db96d56Sopenharmony_ci        self.assertEqual(new[1]["collections"], old[1]["collections"])
8517db96d56Sopenharmony_ci        self.assertEqual(new[2]["collections"], old[2]["collections"] + 1)
8527db96d56Sopenharmony_ci
8537db96d56Sopenharmony_ci    def test_freeze(self):
8547db96d56Sopenharmony_ci        gc.freeze()
8557db96d56Sopenharmony_ci        self.assertGreater(gc.get_freeze_count(), 0)
8567db96d56Sopenharmony_ci        gc.unfreeze()
8577db96d56Sopenharmony_ci        self.assertEqual(gc.get_freeze_count(), 0)
8587db96d56Sopenharmony_ci
8597db96d56Sopenharmony_ci    def test_get_objects(self):
8607db96d56Sopenharmony_ci        gc.collect()
8617db96d56Sopenharmony_ci        l = []
8627db96d56Sopenharmony_ci        l.append(l)
8637db96d56Sopenharmony_ci        self.assertTrue(
8647db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=0))
8657db96d56Sopenharmony_ci        )
8667db96d56Sopenharmony_ci        self.assertFalse(
8677db96d56Sopenharmony_ci                any(l is element for element in  gc.get_objects(generation=1))
8687db96d56Sopenharmony_ci        )
8697db96d56Sopenharmony_ci        self.assertFalse(
8707db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=2))
8717db96d56Sopenharmony_ci        )
8727db96d56Sopenharmony_ci        gc.collect(generation=0)
8737db96d56Sopenharmony_ci        self.assertFalse(
8747db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=0))
8757db96d56Sopenharmony_ci        )
8767db96d56Sopenharmony_ci        self.assertTrue(
8777db96d56Sopenharmony_ci                any(l is element for element in  gc.get_objects(generation=1))
8787db96d56Sopenharmony_ci        )
8797db96d56Sopenharmony_ci        self.assertFalse(
8807db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=2))
8817db96d56Sopenharmony_ci        )
8827db96d56Sopenharmony_ci        gc.collect(generation=1)
8837db96d56Sopenharmony_ci        self.assertFalse(
8847db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=0))
8857db96d56Sopenharmony_ci        )
8867db96d56Sopenharmony_ci        self.assertFalse(
8877db96d56Sopenharmony_ci                any(l is element for element in  gc.get_objects(generation=1))
8887db96d56Sopenharmony_ci        )
8897db96d56Sopenharmony_ci        self.assertTrue(
8907db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=2))
8917db96d56Sopenharmony_ci        )
8927db96d56Sopenharmony_ci        gc.collect(generation=2)
8937db96d56Sopenharmony_ci        self.assertFalse(
8947db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=0))
8957db96d56Sopenharmony_ci        )
8967db96d56Sopenharmony_ci        self.assertFalse(
8977db96d56Sopenharmony_ci                any(l is element for element in  gc.get_objects(generation=1))
8987db96d56Sopenharmony_ci        )
8997db96d56Sopenharmony_ci        self.assertTrue(
9007db96d56Sopenharmony_ci                any(l is element for element in gc.get_objects(generation=2))
9017db96d56Sopenharmony_ci        )
9027db96d56Sopenharmony_ci        del l
9037db96d56Sopenharmony_ci        gc.collect()
9047db96d56Sopenharmony_ci
9057db96d56Sopenharmony_ci    def test_get_objects_arguments(self):
9067db96d56Sopenharmony_ci        gc.collect()
9077db96d56Sopenharmony_ci        self.assertEqual(len(gc.get_objects()),
9087db96d56Sopenharmony_ci                         len(gc.get_objects(generation=None)))
9097db96d56Sopenharmony_ci
9107db96d56Sopenharmony_ci        self.assertRaises(ValueError, gc.get_objects, 1000)
9117db96d56Sopenharmony_ci        self.assertRaises(ValueError, gc.get_objects, -1000)
9127db96d56Sopenharmony_ci        self.assertRaises(TypeError, gc.get_objects, "1")
9137db96d56Sopenharmony_ci        self.assertRaises(TypeError, gc.get_objects, 1.234)
9147db96d56Sopenharmony_ci
9157db96d56Sopenharmony_ci    def test_resurrection_only_happens_once_per_object(self):
9167db96d56Sopenharmony_ci        class A:  # simple self-loop
9177db96d56Sopenharmony_ci            def __init__(self):
9187db96d56Sopenharmony_ci                self.me = self
9197db96d56Sopenharmony_ci
9207db96d56Sopenharmony_ci        class Lazarus(A):
9217db96d56Sopenharmony_ci            resurrected = 0
9227db96d56Sopenharmony_ci            resurrected_instances = []
9237db96d56Sopenharmony_ci
9247db96d56Sopenharmony_ci            def __del__(self):
9257db96d56Sopenharmony_ci                Lazarus.resurrected += 1
9267db96d56Sopenharmony_ci                Lazarus.resurrected_instances.append(self)
9277db96d56Sopenharmony_ci
9287db96d56Sopenharmony_ci        gc.collect()
9297db96d56Sopenharmony_ci        gc.disable()
9307db96d56Sopenharmony_ci
9317db96d56Sopenharmony_ci        # We start with 0 resurrections
9327db96d56Sopenharmony_ci        laz = Lazarus()
9337db96d56Sopenharmony_ci        self.assertEqual(Lazarus.resurrected, 0)
9347db96d56Sopenharmony_ci
9357db96d56Sopenharmony_ci        # Deleting the instance and triggering a collection
9367db96d56Sopenharmony_ci        # resurrects the object
9377db96d56Sopenharmony_ci        del laz
9387db96d56Sopenharmony_ci        gc.collect()
9397db96d56Sopenharmony_ci        self.assertEqual(Lazarus.resurrected, 1)
9407db96d56Sopenharmony_ci        self.assertEqual(len(Lazarus.resurrected_instances), 1)
9417db96d56Sopenharmony_ci
9427db96d56Sopenharmony_ci        # Clearing the references and forcing a collection
9437db96d56Sopenharmony_ci        # should not resurrect the object again.
9447db96d56Sopenharmony_ci        Lazarus.resurrected_instances.clear()
9457db96d56Sopenharmony_ci        self.assertEqual(Lazarus.resurrected, 1)
9467db96d56Sopenharmony_ci        gc.collect()
9477db96d56Sopenharmony_ci        self.assertEqual(Lazarus.resurrected, 1)
9487db96d56Sopenharmony_ci
9497db96d56Sopenharmony_ci        gc.enable()
9507db96d56Sopenharmony_ci
9517db96d56Sopenharmony_ci    def test_resurrection_is_transitive(self):
9527db96d56Sopenharmony_ci        class Cargo:
9537db96d56Sopenharmony_ci            def __init__(self):
9547db96d56Sopenharmony_ci                self.me = self
9557db96d56Sopenharmony_ci
9567db96d56Sopenharmony_ci        class Lazarus:
9577db96d56Sopenharmony_ci            resurrected_instances = []
9587db96d56Sopenharmony_ci
9597db96d56Sopenharmony_ci            def __del__(self):
9607db96d56Sopenharmony_ci                Lazarus.resurrected_instances.append(self)
9617db96d56Sopenharmony_ci
9627db96d56Sopenharmony_ci        gc.collect()
9637db96d56Sopenharmony_ci        gc.disable()
9647db96d56Sopenharmony_ci
9657db96d56Sopenharmony_ci        laz = Lazarus()
9667db96d56Sopenharmony_ci        cargo = Cargo()
9677db96d56Sopenharmony_ci        cargo_id = id(cargo)
9687db96d56Sopenharmony_ci
9697db96d56Sopenharmony_ci        # Create a cycle between cargo and laz
9707db96d56Sopenharmony_ci        laz.cargo = cargo
9717db96d56Sopenharmony_ci        cargo.laz = laz
9727db96d56Sopenharmony_ci
9737db96d56Sopenharmony_ci        # Drop the references, force a collection and check that
9747db96d56Sopenharmony_ci        # everything was resurrected.
9757db96d56Sopenharmony_ci        del laz, cargo
9767db96d56Sopenharmony_ci        gc.collect()
9777db96d56Sopenharmony_ci        self.assertEqual(len(Lazarus.resurrected_instances), 1)
9787db96d56Sopenharmony_ci        instance = Lazarus.resurrected_instances.pop()
9797db96d56Sopenharmony_ci        self.assertTrue(hasattr(instance, "cargo"))
9807db96d56Sopenharmony_ci        self.assertEqual(id(instance.cargo), cargo_id)
9817db96d56Sopenharmony_ci
9827db96d56Sopenharmony_ci        gc.collect()
9837db96d56Sopenharmony_ci        gc.enable()
9847db96d56Sopenharmony_ci
9857db96d56Sopenharmony_ci    def test_resurrection_does_not_block_cleanup_of_other_objects(self):
9867db96d56Sopenharmony_ci
9877db96d56Sopenharmony_ci        # When a finalizer resurrects objects, stats were reporting them as
9887db96d56Sopenharmony_ci        # having been collected.  This affected both collect()'s return
9897db96d56Sopenharmony_ci        # value and the dicts returned by get_stats().
9907db96d56Sopenharmony_ci        N = 100
9917db96d56Sopenharmony_ci
9927db96d56Sopenharmony_ci        class A:  # simple self-loop
9937db96d56Sopenharmony_ci            def __init__(self):
9947db96d56Sopenharmony_ci                self.me = self
9957db96d56Sopenharmony_ci
9967db96d56Sopenharmony_ci        class Z(A):  # resurrecting __del__
9977db96d56Sopenharmony_ci            def __del__(self):
9987db96d56Sopenharmony_ci                zs.append(self)
9997db96d56Sopenharmony_ci
10007db96d56Sopenharmony_ci        zs = []
10017db96d56Sopenharmony_ci
10027db96d56Sopenharmony_ci        def getstats():
10037db96d56Sopenharmony_ci            d = gc.get_stats()[-1]
10047db96d56Sopenharmony_ci            return d['collected'], d['uncollectable']
10057db96d56Sopenharmony_ci
10067db96d56Sopenharmony_ci        gc.collect()
10077db96d56Sopenharmony_ci        gc.disable()
10087db96d56Sopenharmony_ci
10097db96d56Sopenharmony_ci        # No problems if just collecting A() instances.
10107db96d56Sopenharmony_ci        oldc, oldnc = getstats()
10117db96d56Sopenharmony_ci        for i in range(N):
10127db96d56Sopenharmony_ci            A()
10137db96d56Sopenharmony_ci        t = gc.collect()
10147db96d56Sopenharmony_ci        c, nc = getstats()
10157db96d56Sopenharmony_ci        self.assertEqual(t, N) # instance objects
10167db96d56Sopenharmony_ci        self.assertEqual(c - oldc, N)
10177db96d56Sopenharmony_ci        self.assertEqual(nc - oldnc, 0)
10187db96d56Sopenharmony_ci
10197db96d56Sopenharmony_ci        # But Z() is not actually collected.
10207db96d56Sopenharmony_ci        oldc, oldnc = c, nc
10217db96d56Sopenharmony_ci        Z()
10227db96d56Sopenharmony_ci        # Nothing is collected - Z() is merely resurrected.
10237db96d56Sopenharmony_ci        t = gc.collect()
10247db96d56Sopenharmony_ci        c, nc = getstats()
10257db96d56Sopenharmony_ci        self.assertEqual(t, 0)
10267db96d56Sopenharmony_ci        self.assertEqual(c - oldc, 0)
10277db96d56Sopenharmony_ci        self.assertEqual(nc - oldnc, 0)
10287db96d56Sopenharmony_ci
10297db96d56Sopenharmony_ci        # Z() should not prevent anything else from being collected.
10307db96d56Sopenharmony_ci        oldc, oldnc = c, nc
10317db96d56Sopenharmony_ci        for i in range(N):
10327db96d56Sopenharmony_ci            A()
10337db96d56Sopenharmony_ci        Z()
10347db96d56Sopenharmony_ci        t = gc.collect()
10357db96d56Sopenharmony_ci        c, nc = getstats()
10367db96d56Sopenharmony_ci        self.assertEqual(t, N)
10377db96d56Sopenharmony_ci        self.assertEqual(c - oldc, N)
10387db96d56Sopenharmony_ci        self.assertEqual(nc - oldnc, 0)
10397db96d56Sopenharmony_ci
10407db96d56Sopenharmony_ci        # The A() trash should have been reclaimed already but the
10417db96d56Sopenharmony_ci        # 2 copies of Z are still in zs (and the associated dicts).
10427db96d56Sopenharmony_ci        oldc, oldnc = c, nc
10437db96d56Sopenharmony_ci        zs.clear()
10447db96d56Sopenharmony_ci        t = gc.collect()
10457db96d56Sopenharmony_ci        c, nc = getstats()
10467db96d56Sopenharmony_ci        self.assertEqual(t, 2)
10477db96d56Sopenharmony_ci        self.assertEqual(c - oldc, 2)
10487db96d56Sopenharmony_ci        self.assertEqual(nc - oldnc, 0)
10497db96d56Sopenharmony_ci
10507db96d56Sopenharmony_ci        gc.enable()
10517db96d56Sopenharmony_ci
10527db96d56Sopenharmony_ci    @unittest.skipIf(ContainerNoGC is None,
10537db96d56Sopenharmony_ci                     'requires ContainerNoGC extension type')
10547db96d56Sopenharmony_ci    def test_trash_weakref_clear(self):
10557db96d56Sopenharmony_ci        # Test that trash weakrefs are properly cleared (bpo-38006).
10567db96d56Sopenharmony_ci        #
10577db96d56Sopenharmony_ci        # Structure we are creating:
10587db96d56Sopenharmony_ci        #
10597db96d56Sopenharmony_ci        #   Z <- Y <- A--+--> WZ -> C
10607db96d56Sopenharmony_ci        #             ^  |
10617db96d56Sopenharmony_ci        #             +--+
10627db96d56Sopenharmony_ci        # where:
10637db96d56Sopenharmony_ci        #   WZ is a weakref to Z with callback C
10647db96d56Sopenharmony_ci        #   Y doesn't implement tp_traverse
10657db96d56Sopenharmony_ci        #   A contains a reference to itself, Y and WZ
10667db96d56Sopenharmony_ci        #
10677db96d56Sopenharmony_ci        # A, Y, Z, WZ are all trash.  The GC doesn't know that Z is trash
10687db96d56Sopenharmony_ci        # because Y does not implement tp_traverse.  To show the bug, WZ needs
10697db96d56Sopenharmony_ci        # to live long enough so that Z is deallocated before it.  Then, if
10707db96d56Sopenharmony_ci        # gcmodule is buggy, when Z is being deallocated, C will run.
10717db96d56Sopenharmony_ci        #
10727db96d56Sopenharmony_ci        # To ensure WZ lives long enough, we put it in a second reference
10737db96d56Sopenharmony_ci        # cycle.  That trick only works due to the ordering of the GC prev/next
10747db96d56Sopenharmony_ci        # linked lists.  So, this test is a bit fragile.
10757db96d56Sopenharmony_ci        #
10767db96d56Sopenharmony_ci        # The bug reported in bpo-38006 is caused because the GC did not
10777db96d56Sopenharmony_ci        # clear WZ before starting the process of calling tp_clear on the
10787db96d56Sopenharmony_ci        # trash.  Normally, handle_weakrefs() would find the weakref via Z and
10797db96d56Sopenharmony_ci        # clear it.  However, since the GC cannot find Z, WR is not cleared and
10807db96d56Sopenharmony_ci        # it can execute during delete_garbage().  That can lead to disaster
10817db96d56Sopenharmony_ci        # since the callback might tinker with objects that have already had
10827db96d56Sopenharmony_ci        # tp_clear called on them (leaving them in possibly invalid states).
10837db96d56Sopenharmony_ci
10847db96d56Sopenharmony_ci        callback = unittest.mock.Mock()
10857db96d56Sopenharmony_ci
10867db96d56Sopenharmony_ci        class A:
10877db96d56Sopenharmony_ci            __slots__ = ['a', 'y', 'wz']
10887db96d56Sopenharmony_ci
10897db96d56Sopenharmony_ci        class Z:
10907db96d56Sopenharmony_ci            pass
10917db96d56Sopenharmony_ci
10927db96d56Sopenharmony_ci        # setup required object graph, as described above
10937db96d56Sopenharmony_ci        a = A()
10947db96d56Sopenharmony_ci        a.a = a
10957db96d56Sopenharmony_ci        a.y = ContainerNoGC(Z())
10967db96d56Sopenharmony_ci        a.wz = weakref.ref(a.y.value, callback)
10977db96d56Sopenharmony_ci        # create second cycle to keep WZ alive longer
10987db96d56Sopenharmony_ci        wr_cycle = [a.wz]
10997db96d56Sopenharmony_ci        wr_cycle.append(wr_cycle)
11007db96d56Sopenharmony_ci        # ensure trash unrelated to this test is gone
11017db96d56Sopenharmony_ci        gc.collect()
11027db96d56Sopenharmony_ci        gc.disable()
11037db96d56Sopenharmony_ci        # release references and create trash
11047db96d56Sopenharmony_ci        del a, wr_cycle
11057db96d56Sopenharmony_ci        gc.collect()
11067db96d56Sopenharmony_ci        # if called, it means there is a bug in the GC.  The weakref should be
11077db96d56Sopenharmony_ci        # cleared before Z dies.
11087db96d56Sopenharmony_ci        callback.assert_not_called()
11097db96d56Sopenharmony_ci        gc.enable()
11107db96d56Sopenharmony_ci
11117db96d56Sopenharmony_ci
11127db96d56Sopenharmony_ciclass GCCallbackTests(unittest.TestCase):
11137db96d56Sopenharmony_ci    def setUp(self):
11147db96d56Sopenharmony_ci        # Save gc state and disable it.
11157db96d56Sopenharmony_ci        self.enabled = gc.isenabled()
11167db96d56Sopenharmony_ci        gc.disable()
11177db96d56Sopenharmony_ci        self.debug = gc.get_debug()
11187db96d56Sopenharmony_ci        gc.set_debug(0)
11197db96d56Sopenharmony_ci        gc.callbacks.append(self.cb1)
11207db96d56Sopenharmony_ci        gc.callbacks.append(self.cb2)
11217db96d56Sopenharmony_ci        self.othergarbage = []
11227db96d56Sopenharmony_ci
11237db96d56Sopenharmony_ci    def tearDown(self):
11247db96d56Sopenharmony_ci        # Restore gc state
11257db96d56Sopenharmony_ci        del self.visit
11267db96d56Sopenharmony_ci        gc.callbacks.remove(self.cb1)
11277db96d56Sopenharmony_ci        gc.callbacks.remove(self.cb2)
11287db96d56Sopenharmony_ci        gc.set_debug(self.debug)
11297db96d56Sopenharmony_ci        if self.enabled:
11307db96d56Sopenharmony_ci            gc.enable()
11317db96d56Sopenharmony_ci        # destroy any uncollectables
11327db96d56Sopenharmony_ci        gc.collect()
11337db96d56Sopenharmony_ci        for obj in gc.garbage:
11347db96d56Sopenharmony_ci            if isinstance(obj, Uncollectable):
11357db96d56Sopenharmony_ci                obj.partner = None
11367db96d56Sopenharmony_ci        del gc.garbage[:]
11377db96d56Sopenharmony_ci        del self.othergarbage
11387db96d56Sopenharmony_ci        gc.collect()
11397db96d56Sopenharmony_ci
11407db96d56Sopenharmony_ci    def preclean(self):
11417db96d56Sopenharmony_ci        # Remove all fluff from the system.  Invoke this function
11427db96d56Sopenharmony_ci        # manually rather than through self.setUp() for maximum
11437db96d56Sopenharmony_ci        # safety.
11447db96d56Sopenharmony_ci        self.visit = []
11457db96d56Sopenharmony_ci        gc.collect()
11467db96d56Sopenharmony_ci        garbage, gc.garbage[:] = gc.garbage[:], []
11477db96d56Sopenharmony_ci        self.othergarbage.append(garbage)
11487db96d56Sopenharmony_ci        self.visit = []
11497db96d56Sopenharmony_ci
11507db96d56Sopenharmony_ci    def cb1(self, phase, info):
11517db96d56Sopenharmony_ci        self.visit.append((1, phase, dict(info)))
11527db96d56Sopenharmony_ci
11537db96d56Sopenharmony_ci    def cb2(self, phase, info):
11547db96d56Sopenharmony_ci        self.visit.append((2, phase, dict(info)))
11557db96d56Sopenharmony_ci        if phase == "stop" and hasattr(self, "cleanup"):
11567db96d56Sopenharmony_ci            # Clean Uncollectable from garbage
11577db96d56Sopenharmony_ci            uc = [e for e in gc.garbage if isinstance(e, Uncollectable)]
11587db96d56Sopenharmony_ci            gc.garbage[:] = [e for e in gc.garbage
11597db96d56Sopenharmony_ci                             if not isinstance(e, Uncollectable)]
11607db96d56Sopenharmony_ci            for e in uc:
11617db96d56Sopenharmony_ci                e.partner = None
11627db96d56Sopenharmony_ci
11637db96d56Sopenharmony_ci    def test_collect(self):
11647db96d56Sopenharmony_ci        self.preclean()
11657db96d56Sopenharmony_ci        gc.collect()
11667db96d56Sopenharmony_ci        # Algorithmically verify the contents of self.visit
11677db96d56Sopenharmony_ci        # because it is long and tortuous.
11687db96d56Sopenharmony_ci
11697db96d56Sopenharmony_ci        # Count the number of visits to each callback
11707db96d56Sopenharmony_ci        n = [v[0] for v in self.visit]
11717db96d56Sopenharmony_ci        n1 = [i for i in n if i == 1]
11727db96d56Sopenharmony_ci        n2 = [i for i in n if i == 2]
11737db96d56Sopenharmony_ci        self.assertEqual(n1, [1]*2)
11747db96d56Sopenharmony_ci        self.assertEqual(n2, [2]*2)
11757db96d56Sopenharmony_ci
11767db96d56Sopenharmony_ci        # Count that we got the right number of start and stop callbacks.
11777db96d56Sopenharmony_ci        n = [v[1] for v in self.visit]
11787db96d56Sopenharmony_ci        n1 = [i for i in n if i == "start"]
11797db96d56Sopenharmony_ci        n2 = [i for i in n if i == "stop"]
11807db96d56Sopenharmony_ci        self.assertEqual(n1, ["start"]*2)
11817db96d56Sopenharmony_ci        self.assertEqual(n2, ["stop"]*2)
11827db96d56Sopenharmony_ci
11837db96d56Sopenharmony_ci        # Check that we got the right info dict for all callbacks
11847db96d56Sopenharmony_ci        for v in self.visit:
11857db96d56Sopenharmony_ci            info = v[2]
11867db96d56Sopenharmony_ci            self.assertTrue("generation" in info)
11877db96d56Sopenharmony_ci            self.assertTrue("collected" in info)
11887db96d56Sopenharmony_ci            self.assertTrue("uncollectable" in info)
11897db96d56Sopenharmony_ci
11907db96d56Sopenharmony_ci    def test_collect_generation(self):
11917db96d56Sopenharmony_ci        self.preclean()
11927db96d56Sopenharmony_ci        gc.collect(2)
11937db96d56Sopenharmony_ci        for v in self.visit:
11947db96d56Sopenharmony_ci            info = v[2]
11957db96d56Sopenharmony_ci            self.assertEqual(info["generation"], 2)
11967db96d56Sopenharmony_ci
11977db96d56Sopenharmony_ci    @cpython_only
11987db96d56Sopenharmony_ci    def test_collect_garbage(self):
11997db96d56Sopenharmony_ci        self.preclean()
12007db96d56Sopenharmony_ci        # Each of these cause two objects to be garbage:
12017db96d56Sopenharmony_ci        Uncollectable()
12027db96d56Sopenharmony_ci        Uncollectable()
12037db96d56Sopenharmony_ci        C1055820(666)
12047db96d56Sopenharmony_ci        gc.collect()
12057db96d56Sopenharmony_ci        for v in self.visit:
12067db96d56Sopenharmony_ci            if v[1] != "stop":
12077db96d56Sopenharmony_ci                continue
12087db96d56Sopenharmony_ci            info = v[2]
12097db96d56Sopenharmony_ci            self.assertEqual(info["collected"], 1)
12107db96d56Sopenharmony_ci            self.assertEqual(info["uncollectable"], 4)
12117db96d56Sopenharmony_ci
12127db96d56Sopenharmony_ci        # We should now have the Uncollectables in gc.garbage
12137db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), 4)
12147db96d56Sopenharmony_ci        for e in gc.garbage:
12157db96d56Sopenharmony_ci            self.assertIsInstance(e, Uncollectable)
12167db96d56Sopenharmony_ci
12177db96d56Sopenharmony_ci        # Now, let our callback handle the Uncollectable instances
12187db96d56Sopenharmony_ci        self.cleanup=True
12197db96d56Sopenharmony_ci        self.visit = []
12207db96d56Sopenharmony_ci        gc.garbage[:] = []
12217db96d56Sopenharmony_ci        gc.collect()
12227db96d56Sopenharmony_ci        for v in self.visit:
12237db96d56Sopenharmony_ci            if v[1] != "stop":
12247db96d56Sopenharmony_ci                continue
12257db96d56Sopenharmony_ci            info = v[2]
12267db96d56Sopenharmony_ci            self.assertEqual(info["collected"], 0)
12277db96d56Sopenharmony_ci            self.assertEqual(info["uncollectable"], 2)
12287db96d56Sopenharmony_ci
12297db96d56Sopenharmony_ci        # Uncollectables should be gone
12307db96d56Sopenharmony_ci        self.assertEqual(len(gc.garbage), 0)
12317db96d56Sopenharmony_ci
12327db96d56Sopenharmony_ci
12337db96d56Sopenharmony_ci    @unittest.skipIf(BUILD_WITH_NDEBUG,
12347db96d56Sopenharmony_ci                     'built with -NDEBUG')
12357db96d56Sopenharmony_ci    def test_refcount_errors(self):
12367db96d56Sopenharmony_ci        self.preclean()
12377db96d56Sopenharmony_ci        # Verify the "handling" of objects with broken refcounts
12387db96d56Sopenharmony_ci
12397db96d56Sopenharmony_ci        # Skip the test if ctypes is not available
12407db96d56Sopenharmony_ci        import_module("ctypes")
12417db96d56Sopenharmony_ci
12427db96d56Sopenharmony_ci        import subprocess
12437db96d56Sopenharmony_ci        code = textwrap.dedent('''
12447db96d56Sopenharmony_ci            from test.support import gc_collect, SuppressCrashReport
12457db96d56Sopenharmony_ci
12467db96d56Sopenharmony_ci            a = [1, 2, 3]
12477db96d56Sopenharmony_ci            b = [a]
12487db96d56Sopenharmony_ci
12497db96d56Sopenharmony_ci            # Avoid coredump when Py_FatalError() calls abort()
12507db96d56Sopenharmony_ci            SuppressCrashReport().__enter__()
12517db96d56Sopenharmony_ci
12527db96d56Sopenharmony_ci            # Simulate the refcount of "a" being too low (compared to the
12537db96d56Sopenharmony_ci            # references held on it by live data), but keeping it above zero
12547db96d56Sopenharmony_ci            # (to avoid deallocating it):
12557db96d56Sopenharmony_ci            import ctypes
12567db96d56Sopenharmony_ci            ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
12577db96d56Sopenharmony_ci
12587db96d56Sopenharmony_ci            # The garbage collector should now have a fatal error
12597db96d56Sopenharmony_ci            # when it reaches the broken object
12607db96d56Sopenharmony_ci            gc_collect()
12617db96d56Sopenharmony_ci        ''')
12627db96d56Sopenharmony_ci        p = subprocess.Popen([sys.executable, "-c", code],
12637db96d56Sopenharmony_ci                             stdout=subprocess.PIPE,
12647db96d56Sopenharmony_ci                             stderr=subprocess.PIPE)
12657db96d56Sopenharmony_ci        stdout, stderr = p.communicate()
12667db96d56Sopenharmony_ci        p.stdout.close()
12677db96d56Sopenharmony_ci        p.stderr.close()
12687db96d56Sopenharmony_ci        # Verify that stderr has a useful error message:
12697db96d56Sopenharmony_ci        self.assertRegex(stderr,
12707db96d56Sopenharmony_ci            br'gcmodule\.c:[0-9]+: gc_decref: Assertion "gc_get_refs\(g\) > 0" failed.')
12717db96d56Sopenharmony_ci        self.assertRegex(stderr,
12727db96d56Sopenharmony_ci            br'refcount is too small')
12737db96d56Sopenharmony_ci        # "address : 0x7fb5062efc18"
12747db96d56Sopenharmony_ci        # "address : 7FB5062EFC18"
12757db96d56Sopenharmony_ci        address_regex = br'[0-9a-fA-Fx]+'
12767db96d56Sopenharmony_ci        self.assertRegex(stderr,
12777db96d56Sopenharmony_ci            br'object address  : ' + address_regex)
12787db96d56Sopenharmony_ci        self.assertRegex(stderr,
12797db96d56Sopenharmony_ci            br'object refcount : 1')
12807db96d56Sopenharmony_ci        self.assertRegex(stderr,
12817db96d56Sopenharmony_ci            br'object type     : ' + address_regex)
12827db96d56Sopenharmony_ci        self.assertRegex(stderr,
12837db96d56Sopenharmony_ci            br'object type name: list')
12847db96d56Sopenharmony_ci        self.assertRegex(stderr,
12857db96d56Sopenharmony_ci            br'object repr     : \[1, 2, 3\]')
12867db96d56Sopenharmony_ci
12877db96d56Sopenharmony_ci
12887db96d56Sopenharmony_ciclass GCTogglingTests(unittest.TestCase):
12897db96d56Sopenharmony_ci    def setUp(self):
12907db96d56Sopenharmony_ci        gc.enable()
12917db96d56Sopenharmony_ci
12927db96d56Sopenharmony_ci    def tearDown(self):
12937db96d56Sopenharmony_ci        gc.disable()
12947db96d56Sopenharmony_ci
12957db96d56Sopenharmony_ci    def test_bug1055820c(self):
12967db96d56Sopenharmony_ci        # Corresponds to temp2c.py in the bug report.  This is pretty
12977db96d56Sopenharmony_ci        # elaborate.
12987db96d56Sopenharmony_ci
12997db96d56Sopenharmony_ci        c0 = C1055820(0)
13007db96d56Sopenharmony_ci        # Move c0 into generation 2.
13017db96d56Sopenharmony_ci        gc.collect()
13027db96d56Sopenharmony_ci
13037db96d56Sopenharmony_ci        c1 = C1055820(1)
13047db96d56Sopenharmony_ci        c1.keep_c0_alive = c0
13057db96d56Sopenharmony_ci        del c0.loop # now only c1 keeps c0 alive
13067db96d56Sopenharmony_ci
13077db96d56Sopenharmony_ci        c2 = C1055820(2)
13087db96d56Sopenharmony_ci        c2wr = weakref.ref(c2) # no callback!
13097db96d56Sopenharmony_ci
13107db96d56Sopenharmony_ci        ouch = []
13117db96d56Sopenharmony_ci        def callback(ignored):
13127db96d56Sopenharmony_ci            ouch[:] = [c2wr()]
13137db96d56Sopenharmony_ci
13147db96d56Sopenharmony_ci        # The callback gets associated with a wr on an object in generation 2.
13157db96d56Sopenharmony_ci        c0wr = weakref.ref(c0, callback)
13167db96d56Sopenharmony_ci
13177db96d56Sopenharmony_ci        c0 = c1 = c2 = None
13187db96d56Sopenharmony_ci
13197db96d56Sopenharmony_ci        # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
13207db96d56Sopenharmony_ci        # generation 2.  The only thing keeping it alive is that c1 points to
13217db96d56Sopenharmony_ci        # it. c1 and c2 are in generation 0, and are in self-loops.  There's a
13227db96d56Sopenharmony_ci        # global weakref to c2 (c2wr), but that weakref has no callback.
13237db96d56Sopenharmony_ci        # There's also a global weakref to c0 (c0wr), and that does have a
13247db96d56Sopenharmony_ci        # callback, and that callback references c2 via c2wr().
13257db96d56Sopenharmony_ci        #
13267db96d56Sopenharmony_ci        #               c0 has a wr with callback, which references c2wr
13277db96d56Sopenharmony_ci        #               ^
13287db96d56Sopenharmony_ci        #               |
13297db96d56Sopenharmony_ci        #               |     Generation 2 above dots
13307db96d56Sopenharmony_ci        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
13317db96d56Sopenharmony_ci        #               |     Generation 0 below dots
13327db96d56Sopenharmony_ci        #               |
13337db96d56Sopenharmony_ci        #               |
13347db96d56Sopenharmony_ci        #            ^->c1   ^->c2 has a wr but no callback
13357db96d56Sopenharmony_ci        #            |  |    |  |
13367db96d56Sopenharmony_ci        #            <--v    <--v
13377db96d56Sopenharmony_ci        #
13387db96d56Sopenharmony_ci        # So this is the nightmare:  when generation 0 gets collected, we see
13397db96d56Sopenharmony_ci        # that c2 has a callback-free weakref, and c1 doesn't even have a
13407db96d56Sopenharmony_ci        # weakref.  Collecting generation 0 doesn't see c0 at all, and c0 is
13417db96d56Sopenharmony_ci        # the only object that has a weakref with a callback.  gc clears c1
13427db96d56Sopenharmony_ci        # and c2.  Clearing c1 has the side effect of dropping the refcount on
13437db96d56Sopenharmony_ci        # c0 to 0, so c0 goes away (despite that it's in an older generation)
13447db96d56Sopenharmony_ci        # and c0's wr callback triggers.  That in turn materializes a reference
13457db96d56Sopenharmony_ci        # to c2 via c2wr(), but c2 gets cleared anyway by gc.
13467db96d56Sopenharmony_ci
13477db96d56Sopenharmony_ci        # We want to let gc happen "naturally", to preserve the distinction
13487db96d56Sopenharmony_ci        # between generations.
13497db96d56Sopenharmony_ci        junk = []
13507db96d56Sopenharmony_ci        i = 0
13517db96d56Sopenharmony_ci        detector = GC_Detector()
13527db96d56Sopenharmony_ci        while not detector.gc_happened:
13537db96d56Sopenharmony_ci            i += 1
13547db96d56Sopenharmony_ci            if i > 10000:
13557db96d56Sopenharmony_ci                self.fail("gc didn't happen after 10000 iterations")
13567db96d56Sopenharmony_ci            self.assertEqual(len(ouch), 0)
13577db96d56Sopenharmony_ci            junk.append([])  # this will eventually trigger gc
13587db96d56Sopenharmony_ci
13597db96d56Sopenharmony_ci        self.assertEqual(len(ouch), 1)  # else the callback wasn't invoked
13607db96d56Sopenharmony_ci        for x in ouch:
13617db96d56Sopenharmony_ci            # If the callback resurrected c2, the instance would be damaged,
13627db96d56Sopenharmony_ci            # with an empty __dict__.
13637db96d56Sopenharmony_ci            self.assertEqual(x, None)
13647db96d56Sopenharmony_ci
13657db96d56Sopenharmony_ci    def test_bug1055820d(self):
13667db96d56Sopenharmony_ci        # Corresponds to temp2d.py in the bug report.  This is very much like
13677db96d56Sopenharmony_ci        # test_bug1055820c, but uses a __del__ method instead of a weakref
13687db96d56Sopenharmony_ci        # callback to sneak in a resurrection of cyclic trash.
13697db96d56Sopenharmony_ci
13707db96d56Sopenharmony_ci        ouch = []
13717db96d56Sopenharmony_ci        class D(C1055820):
13727db96d56Sopenharmony_ci            def __del__(self):
13737db96d56Sopenharmony_ci                ouch[:] = [c2wr()]
13747db96d56Sopenharmony_ci
13757db96d56Sopenharmony_ci        d0 = D(0)
13767db96d56Sopenharmony_ci        # Move all the above into generation 2.
13777db96d56Sopenharmony_ci        gc.collect()
13787db96d56Sopenharmony_ci
13797db96d56Sopenharmony_ci        c1 = C1055820(1)
13807db96d56Sopenharmony_ci        c1.keep_d0_alive = d0
13817db96d56Sopenharmony_ci        del d0.loop # now only c1 keeps d0 alive
13827db96d56Sopenharmony_ci
13837db96d56Sopenharmony_ci        c2 = C1055820(2)
13847db96d56Sopenharmony_ci        c2wr = weakref.ref(c2) # no callback!
13857db96d56Sopenharmony_ci
13867db96d56Sopenharmony_ci        d0 = c1 = c2 = None
13877db96d56Sopenharmony_ci
13887db96d56Sopenharmony_ci        # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
13897db96d56Sopenharmony_ci        # generation 2.  The only thing keeping it alive is that c1 points to
13907db96d56Sopenharmony_ci        # it.  c1 and c2 are in generation 0, and are in self-loops.  There's
13917db96d56Sopenharmony_ci        # a global weakref to c2 (c2wr), but that weakref has no callback.
13927db96d56Sopenharmony_ci        # There are no other weakrefs.
13937db96d56Sopenharmony_ci        #
13947db96d56Sopenharmony_ci        #               d0 has a __del__ method that references c2wr
13957db96d56Sopenharmony_ci        #               ^
13967db96d56Sopenharmony_ci        #               |
13977db96d56Sopenharmony_ci        #               |     Generation 2 above dots
13987db96d56Sopenharmony_ci        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
13997db96d56Sopenharmony_ci        #               |     Generation 0 below dots
14007db96d56Sopenharmony_ci        #               |
14017db96d56Sopenharmony_ci        #               |
14027db96d56Sopenharmony_ci        #            ^->c1   ^->c2 has a wr but no callback
14037db96d56Sopenharmony_ci        #            |  |    |  |
14047db96d56Sopenharmony_ci        #            <--v    <--v
14057db96d56Sopenharmony_ci        #
14067db96d56Sopenharmony_ci        # So this is the nightmare:  when generation 0 gets collected, we see
14077db96d56Sopenharmony_ci        # that c2 has a callback-free weakref, and c1 doesn't even have a
14087db96d56Sopenharmony_ci        # weakref.  Collecting generation 0 doesn't see d0 at all.  gc clears
14097db96d56Sopenharmony_ci        # c1 and c2.  Clearing c1 has the side effect of dropping the refcount
14107db96d56Sopenharmony_ci        # on d0 to 0, so d0 goes away (despite that it's in an older
14117db96d56Sopenharmony_ci        # generation) and d0's __del__ triggers.  That in turn materializes
14127db96d56Sopenharmony_ci        # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
14137db96d56Sopenharmony_ci
14147db96d56Sopenharmony_ci        # We want to let gc happen "naturally", to preserve the distinction
14157db96d56Sopenharmony_ci        # between generations.
14167db96d56Sopenharmony_ci        detector = GC_Detector()
14177db96d56Sopenharmony_ci        junk = []
14187db96d56Sopenharmony_ci        i = 0
14197db96d56Sopenharmony_ci        while not detector.gc_happened:
14207db96d56Sopenharmony_ci            i += 1
14217db96d56Sopenharmony_ci            if i > 10000:
14227db96d56Sopenharmony_ci                self.fail("gc didn't happen after 10000 iterations")
14237db96d56Sopenharmony_ci            self.assertEqual(len(ouch), 0)
14247db96d56Sopenharmony_ci            junk.append([])  # this will eventually trigger gc
14257db96d56Sopenharmony_ci
14267db96d56Sopenharmony_ci        self.assertEqual(len(ouch), 1)  # else __del__ wasn't invoked
14277db96d56Sopenharmony_ci        for x in ouch:
14287db96d56Sopenharmony_ci            # If __del__ resurrected c2, the instance would be damaged, with an
14297db96d56Sopenharmony_ci            # empty __dict__.
14307db96d56Sopenharmony_ci            self.assertEqual(x, None)
14317db96d56Sopenharmony_ci
14327db96d56Sopenharmony_ci
14337db96d56Sopenharmony_ciclass PythonFinalizationTests(unittest.TestCase):
14347db96d56Sopenharmony_ci    def test_ast_fini(self):
14357db96d56Sopenharmony_ci        # bpo-44184: Regression test for subtype_dealloc() when deallocating
14367db96d56Sopenharmony_ci        # an AST instance also destroy its AST type: subtype_dealloc() must
14377db96d56Sopenharmony_ci        # not access the type memory after deallocating the instance, since
14387db96d56Sopenharmony_ci        # the type memory can be freed as well. The test is also related to
14397db96d56Sopenharmony_ci        # _PyAST_Fini() which clears references to AST types.
14407db96d56Sopenharmony_ci        code = textwrap.dedent("""
14417db96d56Sopenharmony_ci            import ast
14427db96d56Sopenharmony_ci            import codecs
14437db96d56Sopenharmony_ci
14447db96d56Sopenharmony_ci            # Small AST tree to keep their AST types alive
14457db96d56Sopenharmony_ci            tree = ast.parse("def f(x, y): return 2*x-y")
14467db96d56Sopenharmony_ci            x = [tree]
14477db96d56Sopenharmony_ci            x.append(x)
14487db96d56Sopenharmony_ci
14497db96d56Sopenharmony_ci            # Put the cycle somewhere to survive until the last GC collection.
14507db96d56Sopenharmony_ci            # Codec search functions are only cleared at the end of
14517db96d56Sopenharmony_ci            # interpreter_clear().
14527db96d56Sopenharmony_ci            def search_func(encoding):
14537db96d56Sopenharmony_ci                return None
14547db96d56Sopenharmony_ci            search_func.a = x
14557db96d56Sopenharmony_ci            codecs.register(search_func)
14567db96d56Sopenharmony_ci        """)
14577db96d56Sopenharmony_ci        assert_python_ok("-c", code)
14587db96d56Sopenharmony_ci
14597db96d56Sopenharmony_ci
14607db96d56Sopenharmony_cidef setUpModule():
14617db96d56Sopenharmony_ci    global enabled, debug
14627db96d56Sopenharmony_ci    enabled = gc.isenabled()
14637db96d56Sopenharmony_ci    gc.disable()
14647db96d56Sopenharmony_ci    assert not gc.isenabled()
14657db96d56Sopenharmony_ci    debug = gc.get_debug()
14667db96d56Sopenharmony_ci    gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
14677db96d56Sopenharmony_ci    gc.collect() # Delete 2nd generation garbage
14687db96d56Sopenharmony_ci
14697db96d56Sopenharmony_ci
14707db96d56Sopenharmony_cidef tearDownModule():
14717db96d56Sopenharmony_ci    gc.set_debug(debug)
14727db96d56Sopenharmony_ci    # test gc.enable() even if GC is disabled by default
14737db96d56Sopenharmony_ci    if verbose:
14747db96d56Sopenharmony_ci        print("restoring automatic collection")
14757db96d56Sopenharmony_ci    # make sure to always test gc.enable()
14767db96d56Sopenharmony_ci    gc.enable()
14777db96d56Sopenharmony_ci    assert gc.isenabled()
14787db96d56Sopenharmony_ci    if not enabled:
14797db96d56Sopenharmony_ci        gc.disable()
14807db96d56Sopenharmony_ci
14817db96d56Sopenharmony_ci
14827db96d56Sopenharmony_ciif __name__ == "__main__":
14837db96d56Sopenharmony_ci    unittest.main()
1484