python-2.5.2/win32/Lib/test/test_gc.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 from test.test_support import verify, verbose, TestFailed, vereq
       
     2 import sys
       
     3 import gc
       
     4 import weakref
       
     5 
       
     6 def expect(actual, expected, name):
       
     7     if actual != expected:
       
     8         raise TestFailed, "test_%s: actual %r, expected %r" % (
       
     9             name, actual, expected)
       
    10 
       
    11 def expect_nonzero(actual, name):
       
    12     if actual == 0:
       
    13         raise TestFailed, "test_%s: unexpected zero" % name
       
    14 
       
    15 def run_test(name, thunk):
       
    16     if verbose:
       
    17         print "testing %s..." % name,
       
    18     thunk()
       
    19     if verbose:
       
    20         print "ok"
       
    21 
       
    22 def test_list():
       
    23     l = []
       
    24     l.append(l)
       
    25     gc.collect()
       
    26     del l
       
    27     expect(gc.collect(), 1, "list")
       
    28 
       
    29 def test_dict():
       
    30     d = {}
       
    31     d[1] = d
       
    32     gc.collect()
       
    33     del d
       
    34     expect(gc.collect(), 1, "dict")
       
    35 
       
    36 def test_tuple():
       
    37     # since tuples are immutable we close the loop with a list
       
    38     l = []
       
    39     t = (l,)
       
    40     l.append(t)
       
    41     gc.collect()
       
    42     del t
       
    43     del l
       
    44     expect(gc.collect(), 2, "tuple")
       
    45 
       
    46 def test_class():
       
    47     class A:
       
    48         pass
       
    49     A.a = A
       
    50     gc.collect()
       
    51     del A
       
    52     expect_nonzero(gc.collect(), "class")
       
    53 
       
    54 def test_newstyleclass():
       
    55     class A(object):
       
    56         pass
       
    57     gc.collect()
       
    58     del A
       
    59     expect_nonzero(gc.collect(), "staticclass")
       
    60 
       
    61 def test_instance():
       
    62     class A:
       
    63         pass
       
    64     a = A()
       
    65     a.a = a
       
    66     gc.collect()
       
    67     del a
       
    68     expect_nonzero(gc.collect(), "instance")
       
    69 
       
    70 def test_newinstance():
       
    71     class A(object):
       
    72         pass
       
    73     a = A()
       
    74     a.a = a
       
    75     gc.collect()
       
    76     del a
       
    77     expect_nonzero(gc.collect(), "newinstance")
       
    78     class B(list):
       
    79         pass
       
    80     class C(B, A):
       
    81         pass
       
    82     a = C()
       
    83     a.a = a
       
    84     gc.collect()
       
    85     del a
       
    86     expect_nonzero(gc.collect(), "newinstance(2)")
       
    87     del B, C
       
    88     expect_nonzero(gc.collect(), "newinstance(3)")
       
    89     A.a = A()
       
    90     del A
       
    91     expect_nonzero(gc.collect(), "newinstance(4)")
       
    92     expect(gc.collect(), 0, "newinstance(5)")
       
    93 
       
    94 def test_method():
       
    95     # Tricky: self.__init__ is a bound method, it references the instance.
       
    96     class A:
       
    97         def __init__(self):
       
    98             self.init = self.__init__
       
    99     a = A()
       
   100     gc.collect()
       
   101     del a
       
   102     expect_nonzero(gc.collect(), "method")
       
   103 
       
   104 def test_finalizer():
       
   105     # A() is uncollectable if it is part of a cycle, make sure it shows up
       
   106     # in gc.garbage.
       
   107     class A:
       
   108         def __del__(self): pass
       
   109     class B:
       
   110         pass
       
   111     a = A()
       
   112     a.a = a
       
   113     id_a = id(a)
       
   114     b = B()
       
   115     b.b = b
       
   116     gc.collect()
       
   117     del a
       
   118     del b
       
   119     expect_nonzero(gc.collect(), "finalizer")
       
   120     for obj in gc.garbage:
       
   121         if id(obj) == id_a:
       
   122             del obj.a
       
   123             break
       
   124     else:
       
   125         raise TestFailed, "didn't find obj in garbage (finalizer)"
       
   126     gc.garbage.remove(obj)
       
   127 
       
   128 def test_finalizer_newclass():
       
   129     # A() is uncollectable if it is part of a cycle, make sure it shows up
       
   130     # in gc.garbage.
       
   131     class A(object):
       
   132         def __del__(self): pass
       
   133     class B(object):
       
   134         pass
       
   135     a = A()
       
   136     a.a = a
       
   137     id_a = id(a)
       
   138     b = B()
       
   139     b.b = b
       
   140     gc.collect()
       
   141     del a
       
   142     del b
       
   143     expect_nonzero(gc.collect(), "finalizer")
       
   144     for obj in gc.garbage:
       
   145         if id(obj) == id_a:
       
   146             del obj.a
       
   147             break
       
   148     else:
       
   149         raise TestFailed, "didn't find obj in garbage (finalizer)"
       
   150     gc.garbage.remove(obj)
       
   151 
       
   152 def test_function():
       
   153     # Tricky: f -> d -> f, code should call d.clear() after the exec to
       
   154     # break the cycle.
       
   155     d = {}
       
   156     exec("def f(): pass\n") in d
       
   157     gc.collect()
       
   158     del d
       
   159     expect(gc.collect(), 2, "function")
       
   160 
       
   161 def test_frame():
       
   162     def f():
       
   163         frame = sys._getframe()
       
   164     gc.collect()
       
   165     f()
       
   166     expect(gc.collect(), 1, "frame")
       
   167 
       
   168 
       
   169 def test_saveall():
       
   170     # Verify that cyclic garbage like lists show up in gc.garbage if the
       
   171     # SAVEALL option is enabled.
       
   172 
       
   173     # First make sure we don't save away other stuff that just happens to
       
   174     # be waiting for collection.
       
   175     gc.collect()
       
   176     vereq(gc.garbage, []) # if this fails, someone else created immortal trash
       
   177 
       
   178     L = []
       
   179     L.append(L)
       
   180     id_L = id(L)
       
   181 
       
   182     debug = gc.get_debug()
       
   183     gc.set_debug(debug | gc.DEBUG_SAVEALL)
       
   184     del L
       
   185     gc.collect()
       
   186     gc.set_debug(debug)
       
   187 
       
   188     vereq(len(gc.garbage), 1)
       
   189     obj = gc.garbage.pop()
       
   190     vereq(id(obj), id_L)
       
   191 
       
   192 def test_del():
       
   193     # __del__ methods can trigger collection, make this to happen
       
   194     thresholds = gc.get_threshold()
       
   195     gc.enable()
       
   196     gc.set_threshold(1)
       
   197 
       
   198     class A:
       
   199         def __del__(self):
       
   200             dir(self)
       
   201     a = A()
       
   202     del a
       
   203 
       
   204     gc.disable()
       
   205     gc.set_threshold(*thresholds)
       
   206 
       
   207 def test_del_newclass():
       
   208     # __del__ methods can trigger collection, make this to happen
       
   209     thresholds = gc.get_threshold()
       
   210     gc.enable()
       
   211     gc.set_threshold(1)
       
   212 
       
   213     class A(object):
       
   214         def __del__(self):
       
   215             dir(self)
       
   216     a = A()
       
   217     del a
       
   218 
       
   219     gc.disable()
       
   220     gc.set_threshold(*thresholds)
       
   221 
       
   222 def test_get_count():
       
   223     gc.collect()
       
   224     expect(gc.get_count(), (0, 0, 0), "get_count()")
       
   225     a = dict()
       
   226     expect(gc.get_count(), (1, 0, 0), "get_count()")
       
   227 
       
   228 def test_collect_generations():
       
   229     gc.collect()
       
   230     a = dict()
       
   231     gc.collect(0)
       
   232     expect(gc.get_count(), (0, 1, 0), "collect(0)")
       
   233     gc.collect(1)
       
   234     expect(gc.get_count(), (0, 0, 1), "collect(1)")
       
   235     gc.collect(2)
       
   236     expect(gc.get_count(), (0, 0, 0), "collect(1)")
       
   237 
       
   238 class Ouch:
       
   239     n = 0
       
   240     def __del__(self):
       
   241         Ouch.n = Ouch.n + 1
       
   242         if Ouch.n % 17 == 0:
       
   243             gc.collect()
       
   244 
       
   245 def test_trashcan():
       
   246     # "trashcan" is a hack to prevent stack overflow when deallocating
       
   247     # very deeply nested tuples etc.  It works in part by abusing the
       
   248     # type pointer and refcount fields, and that can yield horrible
       
   249     # problems when gc tries to traverse the structures.
       
   250     # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
       
   251     # most likely die via segfault.
       
   252 
       
   253     # Note:  In 2.3 the possibility for compiling without cyclic gc was
       
   254     # removed, and that in turn allows the trashcan mechanism to work
       
   255     # via much simpler means (e.g., it never abuses the type pointer or
       
   256     # refcount fields anymore).  Since it's much less likely to cause a
       
   257     # problem now, the various constants in this expensive (we force a lot
       
   258     # of full collections) test are cut back from the 2.2 version.
       
   259     gc.enable()
       
   260     N = 150
       
   261     for count in range(2):
       
   262         t = []
       
   263         for i in range(N):
       
   264             t = [t, Ouch()]
       
   265         u = []
       
   266         for i in range(N):
       
   267             u = [u, Ouch()]
       
   268         v = {}
       
   269         for i in range(N):
       
   270             v = {1: v, 2: Ouch()}
       
   271     gc.disable()
       
   272 
       
   273 class Boom:
       
   274     def __getattr__(self, someattribute):
       
   275         del self.attr
       
   276         raise AttributeError
       
   277 
       
   278 def test_boom():
       
   279     a = Boom()
       
   280     b = Boom()
       
   281     a.attr = b
       
   282     b.attr = a
       
   283 
       
   284     gc.collect()
       
   285     garbagelen = len(gc.garbage)
       
   286     del a, b
       
   287     # a<->b are in a trash cycle now.  Collection will invoke Boom.__getattr__
       
   288     # (to see whether a and b have __del__ methods), and __getattr__ deletes
       
   289     # the internal "attr" attributes as a side effect.  That causes the
       
   290     # trash cycle to get reclaimed via refcounts falling to 0, thus mutating
       
   291     # the trash graph as a side effect of merely asking whether __del__
       
   292     # exists.  This used to (before 2.3b1) crash Python.  Now __getattr__
       
   293     # isn't called.
       
   294     expect(gc.collect(), 4, "boom")
       
   295     expect(len(gc.garbage), garbagelen, "boom")
       
   296 
       
   297 class Boom2:
       
   298     def __init__(self):
       
   299         self.x = 0
       
   300 
       
   301     def __getattr__(self, someattribute):
       
   302         self.x += 1
       
   303         if self.x > 1:
       
   304             del self.attr
       
   305         raise AttributeError
       
   306 
       
   307 def test_boom2():
       
   308     a = Boom2()
       
   309     b = Boom2()
       
   310     a.attr = b
       
   311     b.attr = a
       
   312 
       
   313     gc.collect()
       
   314     garbagelen = len(gc.garbage)
       
   315     del a, b
       
   316     # Much like test_boom(), except that __getattr__ doesn't break the
       
   317     # cycle until the second time gc checks for __del__.  As of 2.3b1,
       
   318     # there isn't a second time, so this simply cleans up the trash cycle.
       
   319     # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get reclaimed
       
   320     # this way.
       
   321     expect(gc.collect(), 4, "boom2")
       
   322     expect(len(gc.garbage), garbagelen, "boom2")
       
   323 
       
   324 # boom__new and boom2_new are exactly like boom and boom2, except use
       
   325 # new-style classes.
       
   326 
       
   327 class Boom_New(object):
       
   328     def __getattr__(self, someattribute):
       
   329         del self.attr
       
   330         raise AttributeError
       
   331 
       
   332 def test_boom_new():
       
   333     a = Boom_New()
       
   334     b = Boom_New()
       
   335     a.attr = b
       
   336     b.attr = a
       
   337 
       
   338     gc.collect()
       
   339     garbagelen = len(gc.garbage)
       
   340     del a, b
       
   341     expect(gc.collect(), 4, "boom_new")
       
   342     expect(len(gc.garbage), garbagelen, "boom_new")
       
   343 
       
   344 class Boom2_New(object):
       
   345     def __init__(self):
       
   346         self.x = 0
       
   347 
       
   348     def __getattr__(self, someattribute):
       
   349         self.x += 1
       
   350         if self.x > 1:
       
   351             del self.attr
       
   352         raise AttributeError
       
   353 
       
   354 def test_boom2_new():
       
   355     a = Boom2_New()
       
   356     b = Boom2_New()
       
   357     a.attr = b
       
   358     b.attr = a
       
   359 
       
   360     gc.collect()
       
   361     garbagelen = len(gc.garbage)
       
   362     del a, b
       
   363     expect(gc.collect(), 4, "boom2_new")
       
   364     expect(len(gc.garbage), garbagelen, "boom2_new")
       
   365 
       
   366 def test_get_referents():
       
   367     alist = [1, 3, 5]
       
   368     got = gc.get_referents(alist)
       
   369     got.sort()
       
   370     expect(got, alist, "get_referents")
       
   371 
       
   372     atuple = tuple(alist)
       
   373     got = gc.get_referents(atuple)
       
   374     got.sort()
       
   375     expect(got, alist, "get_referents")
       
   376 
       
   377     adict = {1: 3, 5: 7}
       
   378     expected = [1, 3, 5, 7]
       
   379     got = gc.get_referents(adict)
       
   380     got.sort()
       
   381     expect(got, expected, "get_referents")
       
   382 
       
   383     got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
       
   384     got.sort()
       
   385     expect(got, [0, 0] + range(5), "get_referents")
       
   386 
       
   387     expect(gc.get_referents(1, 'a', 4j), [], "get_referents")
       
   388 
       
   389 # Bug 1055820 has several tests of longstanding bugs involving weakrefs and
       
   390 # cyclic gc.
       
   391 
       
   392 # An instance of C1055820 has a self-loop, so becomes cyclic trash when
       
   393 # unreachable.
       
   394 class C1055820(object):
       
   395     def __init__(self, i):
       
   396         self.i = i
       
   397         self.loop = self
       
   398 
       
   399 class GC_Detector(object):
       
   400     # Create an instance I.  Then gc hasn't happened again so long as
       
   401     # I.gc_happened is false.
       
   402 
       
   403     def __init__(self):
       
   404         self.gc_happened = False
       
   405 
       
   406         def it_happened(ignored):
       
   407             self.gc_happened = True
       
   408 
       
   409         # Create a piece of cyclic trash that triggers it_happened when
       
   410         # gc collects it.
       
   411         self.wr = weakref.ref(C1055820(666), it_happened)
       
   412 
       
   413 def test_bug1055820b():
       
   414     # Corresponds to temp2b.py in the bug report.
       
   415 
       
   416     ouch = []
       
   417     def callback(ignored):
       
   418         ouch[:] = [wr() for wr in WRs]
       
   419 
       
   420     Cs = [C1055820(i) for i in range(2)]
       
   421     WRs = [weakref.ref(c, callback) for c in Cs]
       
   422     c = None
       
   423 
       
   424     gc.collect()
       
   425     expect(len(ouch), 0, "bug1055820b")
       
   426     # Make the two instances trash, and collect again.  The bug was that
       
   427     # the callback materialized a strong reference to an instance, but gc
       
   428     # cleared the instance's dict anyway.
       
   429     Cs = None
       
   430     gc.collect()
       
   431     expect(len(ouch), 2, "bug1055820b")  # else the callbacks didn't run
       
   432     for x in ouch:
       
   433         # If the callback resurrected one of these guys, the instance
       
   434         # would be damaged, with an empty __dict__.
       
   435         expect(x, None, "bug1055820b")
       
   436 
       
   437 def test_bug1055820c():
       
   438     # Corresponds to temp2c.py in the bug report.  This is pretty elaborate.
       
   439 
       
   440     c0 = C1055820(0)
       
   441     # Move c0 into generation 2.
       
   442     gc.collect()
       
   443 
       
   444     c1 = C1055820(1)
       
   445     c1.keep_c0_alive = c0
       
   446     del c0.loop # now only c1 keeps c0 alive
       
   447 
       
   448     c2 = C1055820(2)
       
   449     c2wr = weakref.ref(c2) # no callback!
       
   450 
       
   451     ouch = []
       
   452     def callback(ignored):
       
   453         ouch[:] = [c2wr()]
       
   454 
       
   455     # The callback gets associated with a wr on an object in generation 2.
       
   456     c0wr = weakref.ref(c0, callback)
       
   457 
       
   458     c0 = c1 = c2 = None
       
   459 
       
   460     # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
       
   461     # generation 2.  The only thing keeping it alive is that c1 points to it.
       
   462     # c1 and c2 are in generation 0, and are in self-loops.  There's a global
       
   463     # weakref to c2 (c2wr), but that weakref has no callback.  There's also
       
   464     # a global weakref to c0 (c0wr), and that does have a callback, and that
       
   465     # callback references c2 via c2wr().
       
   466     #
       
   467     #               c0 has a wr with callback, which references c2wr
       
   468     #               ^
       
   469     #               |
       
   470     #               |     Generation 2 above dots
       
   471     #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
       
   472     #               |     Generation 0 below dots
       
   473     #               |
       
   474     #               |
       
   475     #            ^->c1   ^->c2 has a wr but no callback
       
   476     #            |  |    |  |
       
   477     #            <--v    <--v
       
   478     #
       
   479     # So this is the nightmare:  when generation 0 gets collected, we see that
       
   480     # c2 has a callback-free weakref, and c1 doesn't even have a weakref.
       
   481     # Collecting generation 0 doesn't see c0 at all, and c0 is the only object
       
   482     # that has a weakref with a callback.  gc clears c1 and c2.  Clearing c1
       
   483     # has the side effect of dropping the refcount on c0 to 0, so c0 goes
       
   484     # away (despite that it's in an older generation) and c0's wr callback
       
   485     # triggers.  That in turn materializes a reference to c2 via c2wr(), but
       
   486     # c2 gets cleared anyway by gc.
       
   487 
       
   488     # We want to let gc happen "naturally", to preserve the distinction
       
   489     # between generations.
       
   490     junk = []
       
   491     i = 0
       
   492     detector = GC_Detector()
       
   493     while not detector.gc_happened:
       
   494         i += 1
       
   495         if i > 10000:
       
   496             raise TestFailed("gc didn't happen after 10000 iterations")
       
   497         expect(len(ouch), 0, "bug1055820c")
       
   498         junk.append([])  # this will eventually trigger gc
       
   499 
       
   500     expect(len(ouch), 1, "bug1055820c")  # else the callback wasn't invoked
       
   501     for x in ouch:
       
   502         # If the callback resurrected c2, the instance would be damaged,
       
   503         # with an empty __dict__.
       
   504         expect(x, None, "bug1055820c")
       
   505 
       
   506 def test_bug1055820d():
       
   507     # Corresponds to temp2d.py in the bug report.  This is very much like
       
   508     # test_bug1055820c, but uses a __del__ method instead of a weakref
       
   509     # callback to sneak in a resurrection of cyclic trash.
       
   510 
       
   511     ouch = []
       
   512     class D(C1055820):
       
   513         def __del__(self):
       
   514             ouch[:] = [c2wr()]
       
   515 
       
   516     d0 = D(0)
       
   517     # Move all the above into generation 2.
       
   518     gc.collect()
       
   519 
       
   520     c1 = C1055820(1)
       
   521     c1.keep_d0_alive = d0
       
   522     del d0.loop # now only c1 keeps d0 alive
       
   523 
       
   524     c2 = C1055820(2)
       
   525     c2wr = weakref.ref(c2) # no callback!
       
   526 
       
   527     d0 = c1 = c2 = None
       
   528 
       
   529     # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
       
   530     # generation 2.  The only thing keeping it alive is that c1 points to it.
       
   531     # c1 and c2 are in generation 0, and are in self-loops.  There's a global
       
   532     # weakref to c2 (c2wr), but that weakref has no callback.  There are no
       
   533     # other weakrefs.
       
   534     #
       
   535     #               d0 has a __del__ method that references c2wr
       
   536     #               ^
       
   537     #               |
       
   538     #               |     Generation 2 above dots
       
   539     #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
       
   540     #               |     Generation 0 below dots
       
   541     #               |
       
   542     #               |
       
   543     #            ^->c1   ^->c2 has a wr but no callback
       
   544     #            |  |    |  |
       
   545     #            <--v    <--v
       
   546     #
       
   547     # So this is the nightmare:  when generation 0 gets collected, we see that
       
   548     # c2 has a callback-free weakref, and c1 doesn't even have a weakref.
       
   549     # Collecting generation 0 doesn't see d0 at all.  gc clears c1 and c2.
       
   550     # Clearing c1 has the side effect of dropping the refcount on d0 to 0, so
       
   551     # d0 goes away (despite that it's in an older generation) and d0's __del__
       
   552     # triggers.  That in turn materializes a reference to c2 via c2wr(), but
       
   553     # c2 gets cleared anyway by gc.
       
   554 
       
   555     # We want to let gc happen "naturally", to preserve the distinction
       
   556     # between generations.
       
   557     detector = GC_Detector()
       
   558     junk = []
       
   559     i = 0
       
   560     while not detector.gc_happened:
       
   561         i += 1
       
   562         if i > 10000:
       
   563             raise TestFailed("gc didn't happen after 10000 iterations")
       
   564         expect(len(ouch), 0, "bug1055820d")
       
   565         junk.append([])  # this will eventually trigger gc
       
   566 
       
   567     expect(len(ouch), 1, "bug1055820d")  # else __del__ wasn't invoked
       
   568     for x in ouch:
       
   569         # If __del__ resurrected c2, the instance would be damaged, with an
       
   570         # empty __dict__.
       
   571         expect(x, None, "bug1055820d")
       
   572 
       
   573 
       
   574 def test_all():
       
   575     gc.collect() # Delete 2nd generation garbage
       
   576     run_test("lists", test_list)
       
   577     run_test("dicts", test_dict)
       
   578     run_test("tuples", test_tuple)
       
   579     run_test("classes", test_class)
       
   580     run_test("new style classes", test_newstyleclass)
       
   581     run_test("instances", test_instance)
       
   582     run_test("new instances", test_newinstance)
       
   583     run_test("methods", test_method)
       
   584     run_test("functions", test_function)
       
   585     run_test("frames", test_frame)
       
   586     run_test("finalizers", test_finalizer)
       
   587     run_test("finalizers (new class)", test_finalizer_newclass)
       
   588     run_test("__del__", test_del)
       
   589     run_test("__del__ (new class)", test_del_newclass)
       
   590     run_test("get_count()", test_get_count)
       
   591     run_test("collect(n)", test_collect_generations)
       
   592     run_test("saveall", test_saveall)
       
   593     run_test("trashcan", test_trashcan)
       
   594     run_test("boom", test_boom)
       
   595     run_test("boom2", test_boom2)
       
   596     run_test("boom_new", test_boom_new)
       
   597     run_test("boom2_new", test_boom2_new)
       
   598     run_test("get_referents", test_get_referents)
       
   599     run_test("bug1055820b", test_bug1055820b)
       
   600 
       
   601     gc.enable()
       
   602     try:
       
   603         run_test("bug1055820c", test_bug1055820c)
       
   604     finally:
       
   605         gc.disable()
       
   606 
       
   607     gc.enable()
       
   608     try:
       
   609         run_test("bug1055820d", test_bug1055820d)
       
   610     finally:
       
   611         gc.disable()
       
   612 
       
   613 def test():
       
   614     if verbose:
       
   615         print "disabling automatic collection"
       
   616     enabled = gc.isenabled()
       
   617     gc.disable()
       
   618     verify(not gc.isenabled())
       
   619     debug = gc.get_debug()
       
   620     gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
       
   621 
       
   622     try:
       
   623         test_all()
       
   624     finally:
       
   625         gc.set_debug(debug)
       
   626         # test gc.enable() even if GC is disabled by default
       
   627         if verbose:
       
   628             print "restoring automatic collection"
       
   629         # make sure to always test gc.enable()
       
   630         gc.enable()
       
   631         verify(gc.isenabled())
       
   632         if not enabled:
       
   633             gc.disable()
       
   634 
       
   635 
       
   636 test()