Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
cpython
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
cpython
Commits
fef1dcf4
Kaydet (Commit)
fef1dcf4
authored
Nis 06, 2007
tarafından
Collin Winter
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Convert test_gc to use unittest.
üst
e10deca7
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
165 additions
and
205 deletions
+165
-205
test_gc.py
Lib/test/test_gc.py
+165
-205
No files found.
Lib/test/test_gc.py
Dosyayı görüntüle @
fef1dcf4
from
test.test_support
import
verify
,
verbose
,
TestFailed
,
vereq
import
unittest
from
test.test_support
import
verbose
,
run_unittest
import
sys
import
sys
import
gc
import
gc
import
weakref
import
weakref
def
expect
(
actual
,
expected
,
name
):
### Support code
if
actual
!=
expected
:
###############################################################################
raise
TestFailed
,
"test_
%
s: actual
%
r, expected
%
r"
%
(
name
,
actual
,
expected
)
def
expect_nonzero
(
actual
,
name
):
# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
if
actual
==
0
:
# cyclic gc.
raise
TestFailed
,
"test_
%
s: unexpected zero"
%
name
def
run_test
(
name
,
thunk
):
# An instance of C1055820 has a self-loop, so becomes cyclic trash when
if
verbose
:
# unreachable.
print
"testing
%
s..."
%
name
,
class
C1055820
(
object
):
thunk
()
def
__init__
(
self
,
i
):
if
verbose
:
self
.
i
=
i
print
"ok"
self
.
loop
=
self
class
GC_Detector
(
object
):
# Create an instance I. Then gc hasn't happened again so long as
# I.gc_happened is false.
def
__init__
(
self
):
self
.
gc_happened
=
False
def
it_happened
(
ignored
):
self
.
gc_happened
=
True
def
test_list
():
# Create a piece of cyclic trash that triggers it_happened when
# gc collects it.
self
.
wr
=
weakref
.
ref
(
C1055820
(
666
),
it_happened
)
### Tests
###############################################################################
class
GCTests
(
unittest
.
TestCase
):
def
test_list
(
self
):
l
=
[]
l
=
[]
l
.
append
(
l
)
l
.
append
(
l
)
gc
.
collect
()
gc
.
collect
()
del
l
del
l
expect
(
gc
.
collect
(),
1
,
"list"
)
self
.
assertEqual
(
gc
.
collect
(),
1
)
def
test_dict
(
):
def
test_dict
(
self
):
d
=
{}
d
=
{}
d
[
1
]
=
d
d
[
1
]
=
d
gc
.
collect
()
gc
.
collect
()
del
d
del
d
expect
(
gc
.
collect
(),
1
,
"dict"
)
self
.
assertEqual
(
gc
.
collect
(),
1
)
def
test_tuple
(
):
def
test_tuple
(
self
):
# since tuples are immutable we close the loop with a list
# since tuples are immutable we close the loop with a list
l
=
[]
l
=
[]
t
=
(
l
,)
t
=
(
l
,)
...
@@ -41,40 +58,40 @@ def test_tuple():
...
@@ -41,40 +58,40 @@ def test_tuple():
gc
.
collect
()
gc
.
collect
()
del
t
del
t
del
l
del
l
expect
(
gc
.
collect
(),
2
,
"tuple"
)
self
.
assertEqual
(
gc
.
collect
(),
2
)
def
test_class
(
):
def
test_class
(
self
):
class
A
:
class
A
:
pass
pass
A
.
a
=
A
A
.
a
=
A
gc
.
collect
()
gc
.
collect
()
del
A
del
A
expect_nonzero
(
gc
.
collect
(),
"class"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
def
test_newstyleclass
(
):
def
test_newstyleclass
(
self
):
class
A
(
object
):
class
A
(
object
):
pass
pass
gc
.
collect
()
gc
.
collect
()
del
A
del
A
expect_nonzero
(
gc
.
collect
(),
"staticclass"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
def
test_instance
(
):
def
test_instance
(
self
):
class
A
:
class
A
:
pass
pass
a
=
A
()
a
=
A
()
a
.
a
=
a
a
.
a
=
a
gc
.
collect
()
gc
.
collect
()
del
a
del
a
expect_nonzero
(
gc
.
collect
(),
"instance"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
def
test_newinstance
(
):
def
test_newinstance
(
self
):
class
A
(
object
):
class
A
(
object
):
pass
pass
a
=
A
()
a
=
A
()
a
.
a
=
a
a
.
a
=
a
gc
.
collect
()
gc
.
collect
()
del
a
del
a
expect_nonzero
(
gc
.
collect
(),
"newinstance"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
class
B
(
list
):
class
B
(
list
):
pass
pass
class
C
(
B
,
A
):
class
C
(
B
,
A
):
...
@@ -83,15 +100,15 @@ def test_newinstance():
...
@@ -83,15 +100,15 @@ def test_newinstance():
a
.
a
=
a
a
.
a
=
a
gc
.
collect
()
gc
.
collect
()
del
a
del
a
expect_nonzero
(
gc
.
collect
(),
"newinstance(2)"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
del
B
,
C
del
B
,
C
expect_nonzero
(
gc
.
collect
(),
"newinstance(3)"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
A
.
a
=
A
()
A
.
a
=
A
()
del
A
del
A
expect_nonzero
(
gc
.
collect
(),
"newinstance(4)"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
expect
(
gc
.
collect
(),
0
,
"newinstance(5)"
)
self
.
assertEqual
(
gc
.
collect
(),
0
)
def
test_method
(
):
def
test_method
(
self
):
# Tricky: self.__init__ is a bound method, it references the instance.
# Tricky: self.__init__ is a bound method, it references the instance.
class
A
:
class
A
:
def
__init__
(
self
):
def
__init__
(
self
):
...
@@ -99,9 +116,9 @@ def test_method():
...
@@ -99,9 +116,9 @@ def test_method():
a
=
A
()
a
=
A
()
gc
.
collect
()
gc
.
collect
()
del
a
del
a
expect_nonzero
(
gc
.
collect
(),
"method"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
def
test_finalizer
(
):
def
test_finalizer
(
self
):
# A() is uncollectable if it is part of a cycle, make sure it shows up
# A() is uncollectable if it is part of a cycle, make sure it shows up
# in gc.garbage.
# in gc.garbage.
class
A
:
class
A
:
...
@@ -116,16 +133,16 @@ def test_finalizer():
...
@@ -116,16 +133,16 @@ def test_finalizer():
gc
.
collect
()
gc
.
collect
()
del
a
del
a
del
b
del
b
expect_nonzero
(
gc
.
collect
(),
"finalizer"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
for
obj
in
gc
.
garbage
:
for
obj
in
gc
.
garbage
:
if
id
(
obj
)
==
id_a
:
if
id
(
obj
)
==
id_a
:
del
obj
.
a
del
obj
.
a
break
break
else
:
else
:
raise
TestFailed
,
"didn't find obj in garbage (finalizer)"
self
.
fail
(
"didn't find obj in garbage (finalizer)"
)
gc
.
garbage
.
remove
(
obj
)
gc
.
garbage
.
remove
(
obj
)
def
test_finalizer_newclass
(
):
def
test_finalizer_newclass
(
self
):
# A() is uncollectable if it is part of a cycle, make sure it shows up
# A() is uncollectable if it is part of a cycle, make sure it shows up
# in gc.garbage.
# in gc.garbage.
class
A
(
object
):
class
A
(
object
):
...
@@ -140,40 +157,40 @@ def test_finalizer_newclass():
...
@@ -140,40 +157,40 @@ def test_finalizer_newclass():
gc
.
collect
()
gc
.
collect
()
del
a
del
a
del
b
del
b
expect_nonzero
(
gc
.
collect
(),
"finalizer"
)
self
.
assertNotEqual
(
gc
.
collect
(),
0
)
for
obj
in
gc
.
garbage
:
for
obj
in
gc
.
garbage
:
if
id
(
obj
)
==
id_a
:
if
id
(
obj
)
==
id_a
:
del
obj
.
a
del
obj
.
a
break
break
else
:
else
:
raise
TestFailed
,
"didn't find obj in garbage (finalizer)"
self
.
fail
(
"didn't find obj in garbage (finalizer)"
)
gc
.
garbage
.
remove
(
obj
)
gc
.
garbage
.
remove
(
obj
)
def
test_function
(
):
def
test_function
(
self
):
# Tricky: f -> d -> f, code should call d.clear() after the exec to
# Tricky: f -> d -> f, code should call d.clear() after the exec to
# break the cycle.
# break the cycle.
d
=
{}
d
=
{}
exec
(
"def f(): pass
\n
"
)
in
d
exec
(
"def f(): pass
\n
"
)
in
d
gc
.
collect
()
gc
.
collect
()
del
d
del
d
expect
(
gc
.
collect
(),
2
,
"function"
)
self
.
assertEqual
(
gc
.
collect
(),
2
)
def
test_frame
(
):
def
test_frame
(
self
):
def
f
():
def
f
():
frame
=
sys
.
_getframe
()
frame
=
sys
.
_getframe
()
gc
.
collect
()
gc
.
collect
()
f
()
f
()
expect
(
gc
.
collect
(),
1
,
"frame"
)
self
.
assertEqual
(
gc
.
collect
(),
1
)
def
test_saveall
(
):
def
test_saveall
(
self
):
# Verify that cyclic garbage like lists show up in gc.garbage if the
# Verify that cyclic garbage like lists show up in gc.garbage if the
# SAVEALL option is enabled.
# SAVEALL option is enabled.
# First make sure we don't save away other stuff that just happens to
# First make sure we don't save away other stuff that just happens to
# be waiting for collection.
# be waiting for collection.
gc
.
collect
()
gc
.
collect
()
vereq
(
gc
.
garbage
,
[])
# if this fails, someone else created immortal trash
# if this fails, someone else created immortal trash
self
.
assertEqual
(
gc
.
garbage
,
[])
L
=
[]
L
=
[]
L
.
append
(
L
)
L
.
append
(
L
)
...
@@ -185,11 +202,11 @@ def test_saveall():
...
@@ -185,11 +202,11 @@ def test_saveall():
gc
.
collect
()
gc
.
collect
()
gc
.
set_debug
(
debug
)
gc
.
set_debug
(
debug
)
vereq
(
len
(
gc
.
garbage
),
1
)
self
.
assertEqual
(
len
(
gc
.
garbage
),
1
)
obj
=
gc
.
garbage
.
pop
()
obj
=
gc
.
garbage
.
pop
()
vereq
(
id
(
obj
),
id_L
)
self
.
assertEqual
(
id
(
obj
),
id_L
)
def
test_del
(
):
def
test_del
(
self
):
# __del__ methods can trigger collection, make this to happen
# __del__ methods can trigger collection, make this to happen
thresholds
=
gc
.
get_threshold
()
thresholds
=
gc
.
get_threshold
()
gc
.
enable
()
gc
.
enable
()
...
@@ -204,7 +221,7 @@ def test_del():
...
@@ -204,7 +221,7 @@ def test_del():
gc
.
disable
()
gc
.
disable
()
gc
.
set_threshold
(
*
thresholds
)
gc
.
set_threshold
(
*
thresholds
)
def
test_del_newclass
(
):
def
test_del_newclass
(
self
):
# __del__ methods can trigger collection, make this to happen
# __del__ methods can trigger collection, make this to happen
thresholds
=
gc
.
get_threshold
()
thresholds
=
gc
.
get_threshold
()
gc
.
enable
()
gc
.
enable
()
...
@@ -219,30 +236,30 @@ def test_del_newclass():
...
@@ -219,30 +236,30 @@ def test_del_newclass():
gc
.
disable
()
gc
.
disable
()
gc
.
set_threshold
(
*
thresholds
)
gc
.
set_threshold
(
*
thresholds
)
def
test_get_count
(
):
def
test_get_count
(
self
):
gc
.
collect
()
gc
.
collect
()
expect
(
gc
.
get_count
(),
(
0
,
0
,
0
),
"get_count()"
)
self
.
assertEqual
(
gc
.
get_count
(),
(
0
,
0
,
0
)
)
a
=
dict
()
a
=
dict
()
expect
(
gc
.
get_count
(),
(
1
,
0
,
0
),
"get_count()"
)
self
.
assertEqual
(
gc
.
get_count
(),
(
1
,
0
,
0
)
)
def
test_collect_generations
(
):
def
test_collect_generations
(
self
):
gc
.
collect
()
gc
.
collect
()
a
=
dict
()
a
=
dict
()
gc
.
collect
(
0
)
gc
.
collect
(
0
)
expect
(
gc
.
get_count
(),
(
0
,
1
,
0
),
"collect(0)"
)
self
.
assertEqual
(
gc
.
get_count
(),
(
0
,
1
,
0
)
)
gc
.
collect
(
1
)
gc
.
collect
(
1
)
expect
(
gc
.
get_count
(),
(
0
,
0
,
1
),
"collect(1)"
)
self
.
assertEqual
(
gc
.
get_count
(),
(
0
,
0
,
1
)
)
gc
.
collect
(
2
)
gc
.
collect
(
2
)
expect
(
gc
.
get_count
(),
(
0
,
0
,
0
),
"collect(1)"
)
self
.
assertEqual
(
gc
.
get_count
(),
(
0
,
0
,
0
)
)
class
Ouch
:
def
test_trashcan
(
self
):
class
Ouch
:
n
=
0
n
=
0
def
__del__
(
self
):
def
__del__
(
self
):
Ouch
.
n
=
Ouch
.
n
+
1
Ouch
.
n
=
Ouch
.
n
+
1
if
Ouch
.
n
%
17
==
0
:
if
Ouch
.
n
%
17
==
0
:
gc
.
collect
()
gc
.
collect
()
def
test_trashcan
():
# "trashcan" is a hack to prevent stack overflow when deallocating
# "trashcan" is a hack to prevent stack overflow when deallocating
# very deeply nested tuples etc. It works in part by abusing the
# very deeply nested tuples etc. It works in part by abusing the
# type pointer and refcount fields, and that can yield horrible
# type pointer and refcount fields, and that can yield horrible
...
@@ -270,12 +287,12 @@ def test_trashcan():
...
@@ -270,12 +287,12 @@ def test_trashcan():
v
=
{
1
:
v
,
2
:
Ouch
()}
v
=
{
1
:
v
,
2
:
Ouch
()}
gc
.
disable
()
gc
.
disable
()
class
Boom
:
def
test_boom
(
self
):
class
Boom
:
def
__getattr__
(
self
,
someattribute
):
def
__getattr__
(
self
,
someattribute
):
del
self
.
attr
del
self
.
attr
raise
AttributeError
raise
AttributeError
def
test_boom
():
a
=
Boom
()
a
=
Boom
()
b
=
Boom
()
b
=
Boom
()
a
.
attr
=
b
a
.
attr
=
b
...
@@ -284,17 +301,18 @@ def test_boom():
...
@@ -284,17 +301,18 @@ def test_boom():
gc
.
collect
()
gc
.
collect
()
garbagelen
=
len
(
gc
.
garbage
)
garbagelen
=
len
(
gc
.
garbage
)
del
a
,
b
del
a
,
b
# a<->b are in a trash cycle now. Collection will invoke Boom.__getattr__
# a<->b are in a trash cycle now. Collection will invoke
# (to see whether a and b have __del__ methods), and __getattr__ deletes
# Boom.__getattr__ (to see whether a and b have __del__ methods), and
# the internal "attr" attributes as a side effect. That causes the
# __getattr__ deletes the internal "attr" attributes as a side effect.
# trash cycle to get reclaimed via refcounts falling to 0, thus mutating
# That causes the trash cycle to get reclaimed via refcounts falling to
# the trash graph as a side effect of merely asking whether __del__
# 0, thus mutating the trash graph as a side effect of merely asking
# exists. This used to (before 2.3b1) crash Python. Now __getattr__
# whether __del__ exists. This used to (before 2.3b1) crash Python.
# isn't called.
# Now __getattr__ isn't called.
expect
(
gc
.
collect
(),
4
,
"boom"
)
self
.
assertEqual
(
gc
.
collect
(),
4
)
expect
(
len
(
gc
.
garbage
),
garbagelen
,
"boom"
)
self
.
assertEqual
(
len
(
gc
.
garbage
),
garbagelen
)
class
Boom2
:
def
test_boom2
(
self
):
class
Boom2
:
def
__init__
(
self
):
def
__init__
(
self
):
self
.
x
=
0
self
.
x
=
0
...
@@ -304,7 +322,6 @@ class Boom2:
...
@@ -304,7 +322,6 @@ class Boom2:
del
self
.
attr
del
self
.
attr
raise
AttributeError
raise
AttributeError
def
test_boom2
():
a
=
Boom2
()
a
=
Boom2
()
b
=
Boom2
()
b
=
Boom2
()
a
.
attr
=
b
a
.
attr
=
b
...
@@ -316,20 +333,20 @@ def test_boom2():
...
@@ -316,20 +333,20 @@ def test_boom2():
# Much like test_boom(), except that __getattr__ doesn't break the
# Much like test_boom(), except that __getattr__ doesn't break the
# cycle until the second time gc checks for __del__. As of 2.3b1,
# cycle until the second time gc checks for __del__. As of 2.3b1,
# there isn't a second time, so this simply cleans up the trash cycle.
# there isn't a second time, so this simply cleans up the trash cycle.
# We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get reclaimed
# We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
#
this way.
# reclaimed
this way.
expect
(
gc
.
collect
(),
4
,
"boom2"
)
self
.
assertEqual
(
gc
.
collect
(),
4
)
expect
(
len
(
gc
.
garbage
),
garbagelen
,
"boom2"
)
self
.
assertEqual
(
len
(
gc
.
garbage
),
garbagelen
)
# boom__new and boom2_new are exactly like boom and boom2, except use
def
test_boom_new
(
self
):
# new-style classes.
# boom__new and boom2_new are exactly like boom and boom2, except use
# new-style classes.
class
Boom_New
(
object
):
class
Boom_New
(
object
):
def
__getattr__
(
self
,
someattribute
):
def
__getattr__
(
self
,
someattribute
):
del
self
.
attr
del
self
.
attr
raise
AttributeError
raise
AttributeError
def
test_boom_new
():
a
=
Boom_New
()
a
=
Boom_New
()
b
=
Boom_New
()
b
=
Boom_New
()
a
.
attr
=
b
a
.
attr
=
b
...
@@ -338,10 +355,11 @@ def test_boom_new():
...
@@ -338,10 +355,11 @@ def test_boom_new():
gc
.
collect
()
gc
.
collect
()
garbagelen
=
len
(
gc
.
garbage
)
garbagelen
=
len
(
gc
.
garbage
)
del
a
,
b
del
a
,
b
expect
(
gc
.
collect
(),
4
,
"boom_new"
)
self
.
assertEqual
(
gc
.
collect
(),
4
)
expect
(
len
(
gc
.
garbage
),
garbagelen
,
"boom_new"
)
self
.
assertEqual
(
len
(
gc
.
garbage
),
garbagelen
)
class
Boom2_New
(
object
):
def
test_boom2_new
(
self
):
class
Boom2_New
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
x
=
0
self
.
x
=
0
...
@@ -351,7 +369,6 @@ class Boom2_New(object):
...
@@ -351,7 +369,6 @@ class Boom2_New(object):
del
self
.
attr
del
self
.
attr
raise
AttributeError
raise
AttributeError
def
test_boom2_new
():
a
=
Boom2_New
()
a
=
Boom2_New
()
b
=
Boom2_New
()
b
=
Boom2_New
()
a
.
attr
=
b
a
.
attr
=
b
...
@@ -360,57 +377,33 @@ def test_boom2_new():
...
@@ -360,57 +377,33 @@ def test_boom2_new():
gc
.
collect
()
gc
.
collect
()
garbagelen
=
len
(
gc
.
garbage
)
garbagelen
=
len
(
gc
.
garbage
)
del
a
,
b
del
a
,
b
expect
(
gc
.
collect
(),
4
,
"boom2_new"
)
self
.
assertEqual
(
gc
.
collect
(),
4
)
expect
(
len
(
gc
.
garbage
),
garbagelen
,
"boom2_new"
)
self
.
assertEqual
(
len
(
gc
.
garbage
),
garbagelen
)
def
test_get_referents
(
):
def
test_get_referents
(
self
):
alist
=
[
1
,
3
,
5
]
alist
=
[
1
,
3
,
5
]
got
=
gc
.
get_referents
(
alist
)
got
=
gc
.
get_referents
(
alist
)
got
.
sort
()
got
.
sort
()
expect
(
got
,
alist
,
"get_referents"
)
self
.
assertEqual
(
got
,
alist
)
atuple
=
tuple
(
alist
)
atuple
=
tuple
(
alist
)
got
=
gc
.
get_referents
(
atuple
)
got
=
gc
.
get_referents
(
atuple
)
got
.
sort
()
got
.
sort
()
expect
(
got
,
alist
,
"get_referents"
)
self
.
assertEqual
(
got
,
alist
)
adict
=
{
1
:
3
,
5
:
7
}
adict
=
{
1
:
3
,
5
:
7
}
expected
=
[
1
,
3
,
5
,
7
]
expected
=
[
1
,
3
,
5
,
7
]
got
=
gc
.
get_referents
(
adict
)
got
=
gc
.
get_referents
(
adict
)
got
.
sort
()
got
.
sort
()
expect
(
got
,
expected
,
"get_referents"
)
self
.
assertEqual
(
got
,
expected
)
got
=
gc
.
get_referents
([
1
,
2
],
{
3
:
4
},
(
0
,
0
,
0
))
got
=
gc
.
get_referents
([
1
,
2
],
{
3
:
4
},
(
0
,
0
,
0
))
got
.
sort
()
got
.
sort
()
expect
(
got
,
[
0
,
0
]
+
range
(
5
),
"get_referents"
)
self
.
assertEqual
(
got
,
[
0
,
0
]
+
range
(
5
)
)
expect
(
gc
.
get_referents
(
1
,
'a'
,
4
j
),
[],
"get_referents"
)
self
.
assertEqual
(
gc
.
get_referents
(
1
,
'a'
,
4
j
),
[]
)
# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
def
test_bug1055820b
(
self
):
# cyclic gc.
# An instance of C1055820 has a self-loop, so becomes cyclic trash when
# unreachable.
class
C1055820
(
object
):
def
__init__
(
self
,
i
):
self
.
i
=
i
self
.
loop
=
self
class
GC_Detector
(
object
):
# Create an instance I. Then gc hasn't happened again so long as
# I.gc_happened is false.
def
__init__
(
self
):
self
.
gc_happened
=
False
def
it_happened
(
ignored
):
self
.
gc_happened
=
True
# Create a piece of cyclic trash that triggers it_happened when
# gc collects it.
self
.
wr
=
weakref
.
ref
(
C1055820
(
666
),
it_happened
)
def
test_bug1055820b
():
# Corresponds to temp2b.py in the bug report.
# Corresponds to temp2b.py in the bug report.
ouch
=
[]
ouch
=
[]
...
@@ -422,20 +415,28 @@ def test_bug1055820b():
...
@@ -422,20 +415,28 @@ def test_bug1055820b():
c
=
None
c
=
None
gc
.
collect
()
gc
.
collect
()
expect
(
len
(
ouch
),
0
,
"bug1055820b"
)
self
.
assertEqual
(
len
(
ouch
),
0
)
# Make the two instances trash, and collect again. The bug was that
# Make the two instances trash, and collect again. The bug was that
# the callback materialized a strong reference to an instance, but gc
# the callback materialized a strong reference to an instance, but gc
# cleared the instance's dict anyway.
# cleared the instance's dict anyway.
Cs
=
None
Cs
=
None
gc
.
collect
()
gc
.
collect
()
expect
(
len
(
ouch
),
2
,
"bug1055820b"
)
# else the callbacks didn't run
self
.
assertEqual
(
len
(
ouch
),
2
)
# else the callbacks didn't run
for
x
in
ouch
:
for
x
in
ouch
:
# If the callback resurrected one of these guys, the instance
# If the callback resurrected one of these guys, the instance
# would be damaged, with an empty __dict__.
# would be damaged, with an empty __dict__.
expect
(
x
,
None
,
"bug1055820b"
)
self
.
assertEqual
(
x
,
None
)
class
GCTogglingTests
(
unittest
.
TestCase
):
def
setUp
(
self
):
gc
.
enable
()
def
test_bug1055820c
():
def
tearDown
(
self
):
# Corresponds to temp2c.py in the bug report. This is pretty elaborate.
gc
.
disable
()
def
test_bug1055820c
(
self
):
# Corresponds to temp2c.py in the bug report. This is pretty
# elaborate.
c0
=
C1055820
(
0
)
c0
=
C1055820
(
0
)
# Move c0 into generation 2.
# Move c0 into generation 2.
...
@@ -458,11 +459,11 @@ def test_bug1055820c():
...
@@ -458,11 +459,11 @@ def test_bug1055820c():
c0
=
c1
=
c2
=
None
c0
=
c1
=
c2
=
None
# What we've set up: c0, c1, and c2 are all trash now. c0 is in
# What we've set up: c0, c1, and c2 are all trash now. c0 is in
# generation 2. The only thing keeping it alive is that c1 points to it.
# generation 2. The only thing keeping it alive is that c1 points to
# c1 and c2 are in generation 0, and are in self-loops. There's a global
# it. c1 and c2 are in generation 0, and are in self-loops. There's a
# weakref to c2 (c2wr), but that weakref has no callback. There's also
# global weakref to c2 (c2wr), but that weakref has no callback.
# a global weakref to c0 (c0wr), and that does have a callback, and that
# There's also a global weakref to c0 (c0wr), and that does have a
#
callback references c2 via c2wr().
# callback, and that
callback references c2 via c2wr().
#
#
# c0 has a wr with callback, which references c2wr
# c0 has a wr with callback, which references c2wr
# ^
# ^
...
@@ -476,14 +477,14 @@ def test_bug1055820c():
...
@@ -476,14 +477,14 @@ def test_bug1055820c():
# | | | |
# | | | |
# <--v <--v
# <--v <--v
#
#
# So this is the nightmare: when generation 0 gets collected, we see that
# So this is the nightmare: when generation 0 gets collected, we see
# c2 has a callback-free weakref, and c1 doesn't even have a weakref.
# that c2 has a callback-free weakref, and c1 doesn't even have a
# Collecting generation 0 doesn't see c0 at all, and c0 is the only object
# weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
# that has a weakref with a callback. gc clears c1 and c2. Clearing
c1
# the only object that has a weakref with a callback. gc clears
c1
# has the side effect of dropping the refcount on c0 to 0, so c0 goes
# and c2. Clearing c1 has the side effect of dropping the refcount on
# away (despite that it's in an older generation) and c0's wr callback
# c0 to 0, so c0 goes away (despite that it's in an older generation)
# triggers. That in turn materializes a reference to c2 via c2wr(), but
# and c0's wr callback triggers. That in turn materializes a reference
#
c2 gets cleared anyway by gc.
# to c2 via c2wr(), but
c2 gets cleared anyway by gc.
# We want to let gc happen "naturally", to preserve the distinction
# We want to let gc happen "naturally", to preserve the distinction
# between generations.
# between generations.
...
@@ -493,17 +494,17 @@ def test_bug1055820c():
...
@@ -493,17 +494,17 @@ def test_bug1055820c():
while
not
detector
.
gc_happened
:
while
not
detector
.
gc_happened
:
i
+=
1
i
+=
1
if
i
>
10000
:
if
i
>
10000
:
raise
TestFailed
(
"gc didn't happen after 10000 iterations"
)
self
.
fail
(
"gc didn't happen after 10000 iterations"
)
expect
(
len
(
ouch
),
0
,
"bug1055820c"
)
self
.
assertEqual
(
len
(
ouch
),
0
)
junk
.
append
([])
# this will eventually trigger gc
junk
.
append
([])
# this will eventually trigger gc
expect
(
len
(
ouch
),
1
,
"bug1055820c"
)
# else the callback wasn't invoked
self
.
assertEqual
(
len
(
ouch
),
1
)
# else the callback wasn't invoked
for
x
in
ouch
:
for
x
in
ouch
:
# If the callback resurrected c2, the instance would be damaged,
# If the callback resurrected c2, the instance would be damaged,
# with an empty __dict__.
# with an empty __dict__.
expect
(
x
,
None
,
"bug1055820c"
)
self
.
assertEqual
(
x
,
None
)
def
test_bug1055820d
(
):
def
test_bug1055820d
(
self
):
# Corresponds to temp2d.py in the bug report. This is very much like
# Corresponds to temp2d.py in the bug report. This is very much like
# test_bug1055820c, but uses a __del__ method instead of a weakref
# test_bug1055820c, but uses a __del__ method instead of a weakref
# callback to sneak in a resurrection of cyclic trash.
# callback to sneak in a resurrection of cyclic trash.
...
@@ -527,10 +528,10 @@ def test_bug1055820d():
...
@@ -527,10 +528,10 @@ def test_bug1055820d():
d0
=
c1
=
c2
=
None
d0
=
c1
=
c2
=
None
# What we've set up: d0, c1, and c2 are all trash now. d0 is in
# What we've set up: d0, c1, and c2 are all trash now. d0 is in
# generation 2. The only thing keeping it alive is that c1 points to it.
# generation 2. The only thing keeping it alive is that c1 points to
# c1 and c2 are in generation 0, and are in self-loops. There's a global
# it. c1 and c2 are in generation 0, and are in self-loops. There's
# weakref to c2 (c2wr), but that weakref has no callback. There are no
# a global weakref to c2 (c2wr), but that weakref has no callback.
#
other weakrefs.
# There are no
other weakrefs.
#
#
# d0 has a __del__ method that references c2wr
# d0 has a __del__ method that references c2wr
# ^
# ^
...
@@ -544,13 +545,13 @@ def test_bug1055820d():
...
@@ -544,13 +545,13 @@ def test_bug1055820d():
# | | | |
# | | | |
# <--v <--v
# <--v <--v
#
#
# So this is the nightmare: when generation 0 gets collected, we see that
# So this is the nightmare: when generation 0 gets collected, we see
# c2 has a callback-free weakref, and c1 doesn't even have a weakref.
# that c2 has a callback-free weakref, and c1 doesn't even have a
# Collecting generation 0 doesn't see d0 at all. gc clears c1 and c2.
# weakref. Collecting generation 0 doesn't see d0 at all. gc clears
# Clearing c1 has the side effect of dropping the refcount on d0 to 0, so
# c1 and c2. Clearing c1 has the side effect of dropping the refcount
# d0 goes away (despite that it's in an older generation) and d0's __del__
# on d0 to 0, so d0 goes away (despite that it's in an older
# triggers. That in turn materializes a reference to c2 via c2wr(), but
# generation) and d0's __del__ triggers. That in turn materializes
#
c2 gets cleared anyway by gc.
# a reference to c2 via c2wr(), but
c2 gets cleared anyway by gc.
# We want to let gc happen "naturally", to preserve the distinction
# We want to let gc happen "naturally", to preserve the distinction
# between generations.
# between generations.
...
@@ -560,67 +561,26 @@ def test_bug1055820d():
...
@@ -560,67 +561,26 @@ def test_bug1055820d():
while
not
detector
.
gc_happened
:
while
not
detector
.
gc_happened
:
i
+=
1
i
+=
1
if
i
>
10000
:
if
i
>
10000
:
raise
TestFailed
(
"gc didn't happen after 10000 iterations"
)
self
.
fail
(
"gc didn't happen after 10000 iterations"
)
expect
(
len
(
ouch
),
0
,
"bug1055820d"
)
self
.
assertEqual
(
len
(
ouch
),
0
)
junk
.
append
([])
# this will eventually trigger gc
junk
.
append
([])
# this will eventually trigger gc
expect
(
len
(
ouch
),
1
,
"bug1055820d"
)
# else __del__ wasn't invoked
self
.
assertEqual
(
len
(
ouch
),
1
)
# else __del__ wasn't invoked
for
x
in
ouch
:
for
x
in
ouch
:
# If __del__ resurrected c2, the instance would be damaged, with an
# If __del__ resurrected c2, the instance would be damaged, with an
# empty __dict__.
# empty __dict__.
expect
(
x
,
None
,
"bug1055820d"
)
self
.
assertEqual
(
x
,
None
)
def
test_all
():
gc
.
collect
()
# Delete 2nd generation garbage
run_test
(
"lists"
,
test_list
)
run_test
(
"dicts"
,
test_dict
)
run_test
(
"tuples"
,
test_tuple
)
run_test
(
"classes"
,
test_class
)
run_test
(
"new style classes"
,
test_newstyleclass
)
run_test
(
"instances"
,
test_instance
)
run_test
(
"new instances"
,
test_newinstance
)
run_test
(
"methods"
,
test_method
)
run_test
(
"functions"
,
test_function
)
run_test
(
"frames"
,
test_frame
)
run_test
(
"finalizers"
,
test_finalizer
)
run_test
(
"finalizers (new class)"
,
test_finalizer_newclass
)
run_test
(
"__del__"
,
test_del
)
run_test
(
"__del__ (new class)"
,
test_del_newclass
)
run_test
(
"get_count()"
,
test_get_count
)
run_test
(
"collect(n)"
,
test_collect_generations
)
run_test
(
"saveall"
,
test_saveall
)
run_test
(
"trashcan"
,
test_trashcan
)
run_test
(
"boom"
,
test_boom
)
run_test
(
"boom2"
,
test_boom2
)
run_test
(
"boom_new"
,
test_boom_new
)
run_test
(
"boom2_new"
,
test_boom2_new
)
run_test
(
"get_referents"
,
test_get_referents
)
run_test
(
"bug1055820b"
,
test_bug1055820b
)
gc
.
enable
()
try
:
run_test
(
"bug1055820c"
,
test_bug1055820c
)
finally
:
gc
.
disable
()
gc
.
enable
()
def
test_main
():
try
:
run_test
(
"bug1055820d"
,
test_bug1055820d
)
finally
:
gc
.
disable
()
def
test
():
if
verbose
:
print
"disabling automatic collection"
enabled
=
gc
.
isenabled
()
enabled
=
gc
.
isenabled
()
gc
.
disable
()
gc
.
disable
()
verify
(
not
gc
.
isenabled
()
)
assert
not
gc
.
isenabled
(
)
debug
=
gc
.
get_debug
()
debug
=
gc
.
get_debug
()
gc
.
set_debug
(
debug
&
~
gc
.
DEBUG_LEAK
)
# this test is supposed to leak
gc
.
set_debug
(
debug
&
~
gc
.
DEBUG_LEAK
)
# this test is supposed to leak
try
:
try
:
test_all
()
gc
.
collect
()
# Delete 2nd generation garbage
run_unittest
(
GCTests
,
GCTogglingTests
)
finally
:
finally
:
gc
.
set_debug
(
debug
)
gc
.
set_debug
(
debug
)
# test gc.enable() even if GC is disabled by default
# test gc.enable() even if GC is disabled by default
...
@@ -628,9 +588,9 @@ def test():
...
@@ -628,9 +588,9 @@ def test():
print
"restoring automatic collection"
print
"restoring automatic collection"
# make sure to always test gc.enable()
# make sure to always test gc.enable()
gc
.
enable
()
gc
.
enable
()
verify
(
gc
.
isenabled
()
)
assert
gc
.
isenabled
(
)
if
not
enabled
:
if
not
enabled
:
gc
.
disable
()
gc
.
disable
()
if
__name__
==
"__main__"
:
test
()
test_main
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment