mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
Add Garbage Collection support to new-style classes (not yet to their
instances). Also added GC support to various auxiliary types: super, property, descriptors, wrappers, dictproxy. (Only type objects have a tp_clear field; the other types are.) One change was necessary to the GC infrastructure. We have statically allocated type objects that don't have a GC header (and can't easily be given one) and heap-allocated type objects that do have a GC header. Giving these different metatypes would be really ugly: I tried, and I had to modify pickle.py, cPickle.c, copy.py, add a new invent a new name for the new metatype and make it a built-in, change affected tests... In short, a mess. So instead, we add a new type slot tp_is_gc, which is a simple Boolean function that determines whether a particular instance has GC headers or not. This slot is only relevant for types that have the (new) GC flag bit set. If the tp_is_gc slot is NULL (by far the most common case), all instances of the type are deemed to have GC headers. This slot is called by the PyObject_IS_GC() macro (which is only used twice, both times in gcmodule.c). I also changed the extern declarations for a bunch of GC-related functions (_PyObject_GC_Del etc.): these always exist but objimpl.h only declared them when WITH_CYCLE_GC was defined, but I needed to be able to reference them without #ifdefs. (When WITH_CYCLE_GC is not defined, they do the same as their non-GC counterparts anyway.)
This commit is contained in:
parent
0481d24dd5
commit
048eb75c2d
5 changed files with 237 additions and 39 deletions
|
@ -7,9 +7,9 @@ def expect(actual, expected, name):
|
|||
raise TestFailed, "test_%s: actual %d, expected %d" % (
|
||||
name, actual, expected)
|
||||
|
||||
def expect_not(actual, expected, name):
|
||||
if actual == expected:
|
||||
raise TestFailed, "test_%s: actual %d unexpected" % (name, actual)
|
||||
def expect_nonzero(actual, name):
|
||||
if actual == 0:
|
||||
raise TestFailed, "test_%s: unexpected zero" % name
|
||||
|
||||
def run_test(name, thunk):
|
||||
if verbose:
|
||||
|
@ -48,7 +48,21 @@ def test_class():
|
|||
A.a = A
|
||||
gc.collect()
|
||||
del A
|
||||
expect_not(gc.collect(), 0, "class")
|
||||
expect_nonzero(gc.collect(), "class")
|
||||
|
||||
def test_staticclass():
|
||||
class A(object):
|
||||
__dynamic__ = 0
|
||||
gc.collect()
|
||||
del A
|
||||
expect_nonzero(gc.collect(), "staticclass")
|
||||
|
||||
def test_dynamicclass():
|
||||
class A(object):
|
||||
__dynamic__ = 1
|
||||
gc.collect()
|
||||
del A
|
||||
expect_nonzero(gc.collect(), "dynamicclass")
|
||||
|
||||
def test_instance():
|
||||
class A:
|
||||
|
@ -57,7 +71,7 @@ def test_instance():
|
|||
a.a = a
|
||||
gc.collect()
|
||||
del a
|
||||
expect_not(gc.collect(), 0, "instance")
|
||||
expect_nonzero(gc.collect(), "instance")
|
||||
|
||||
def test_method():
|
||||
# Tricky: self.__init__ is a bound method, it references the instance.
|
||||
|
@ -67,7 +81,7 @@ def test_method():
|
|||
a = A()
|
||||
gc.collect()
|
||||
del a
|
||||
expect_not(gc.collect(), 0, "method")
|
||||
expect_nonzero(gc.collect(), "method")
|
||||
|
||||
def test_finalizer():
|
||||
# A() is uncollectable if it is part of a cycle, make sure it shows up
|
||||
|
@ -84,7 +98,7 @@ def test_finalizer():
|
|||
gc.collect()
|
||||
del a
|
||||
del b
|
||||
expect_not(gc.collect(), 0, "finalizer")
|
||||
expect_nonzero(gc.collect(), "finalizer")
|
||||
for obj in gc.garbage:
|
||||
if id(obj) == id_a:
|
||||
del obj.a
|
||||
|
@ -153,6 +167,8 @@ def test_all():
|
|||
run_test("dicts", test_dict)
|
||||
run_test("tuples", test_tuple)
|
||||
run_test("classes", test_class)
|
||||
run_test("static classes", test_staticclass)
|
||||
run_test("dynamic classes", test_dynamicclass)
|
||||
run_test("instances", test_instance)
|
||||
run_test("methods", test_method)
|
||||
run_test("functions", test_function)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue