[pypy-svn] r79612 - in pypy/trunk/pypy/module/cpyext: . test
afa at codespeak.net
afa at codespeak.net
Mon Nov 29 00:15:22 CET 2010
Author: afa
Date: Mon Nov 29 00:15:20 2010
New Revision: 79612
Modified:
pypy/trunk/pypy/module/cpyext/cdatetime.py
pypy/trunk/pypy/module/cpyext/state.py
pypy/trunk/pypy/module/cpyext/test/test_arraymodule.py
pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
pypy/trunk/pypy/module/cpyext/test/test_datetime.py
pypy/trunk/pypy/module/cpyext/test/test_typeobject.py
pypy/trunk/pypy/module/cpyext/typeobject.py
Log:
Much saner approach to memory checks in cpyext:
the issue was that the "leakfinder" is run before our teardown function.
Until I see how to fix this, add an explicit call to "cleanup_references()"
in all tests that need it.
Most of the "render_immortal" hacks have disappeared,
the few ones remaining looks really needed...
Modified: pypy/trunk/pypy/module/cpyext/cdatetime.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/cdatetime.py (original)
+++ pypy/trunk/pypy/module/cpyext/cdatetime.py Mon Nov 29 00:15:20 2010
@@ -4,8 +4,7 @@
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, cpython_struct, PyObjectFields)
from pypy.module.cpyext.import_ import PyImport_Import
-from pypy.module.cpyext.typeobject import PyTypeObjectPtr, render_immortal
-from pypy.module.cpyext.state import State
+from pypy.module.cpyext.typeobject import PyTypeObjectPtr
from pypy.interpreter.error import OperationError
from pypy.tool.sourcetools import func_renamer
@@ -25,48 +24,26 @@
datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw',
track_allocation=False)
- if not we_are_translated():
- datetimeAPI_dealloc(space)
- space.fromcache(State).datetimeAPI = datetimeAPI
-
w_datetime = PyImport_Import(space, space.wrap("datetime"))
w_type = space.getattr(w_datetime, space.wrap("date"))
datetimeAPI.c_DateType = rffi.cast(
PyTypeObjectPtr, make_ref(space, w_type))
- render_immortal(datetimeAPI.c_DateType, w_type)
w_type = space.getattr(w_datetime, space.wrap("datetime"))
datetimeAPI.c_DateTimeType = rffi.cast(
PyTypeObjectPtr, make_ref(space, w_type))
- render_immortal(datetimeAPI.c_DateTimeType, w_type)
w_type = space.getattr(w_datetime, space.wrap("time"))
datetimeAPI.c_TimeType = rffi.cast(
PyTypeObjectPtr, make_ref(space, w_type))
- render_immortal(datetimeAPI.c_TimeType, w_type)
w_type = space.getattr(w_datetime, space.wrap("timedelta"))
datetimeAPI.c_DeltaType = rffi.cast(
PyTypeObjectPtr, make_ref(space, w_type))
- render_immortal(datetimeAPI.c_DeltaType, w_type)
return datetimeAPI
-def datetimeAPI_dealloc(space):
- "Used in tests only, to please the refcount checker"
- if we_are_translated():
- return
- datetimeAPI = space.fromcache(State).datetimeAPI
- if datetimeAPI is None:
- return
- space.fromcache(State).datetimeAPI = None
- Py_DecRef(space, rffi.cast(PyObject, datetimeAPI.c_DateType))
- Py_DecRef(space, rffi.cast(PyObject, datetimeAPI.c_DateTimeType))
- Py_DecRef(space, rffi.cast(PyObject, datetimeAPI.c_TimeType))
- Py_DecRef(space, rffi.cast(PyObject, datetimeAPI.c_DeltaType))
- lltype.free(datetimeAPI, flavor='raw')
-
PyDateTime_Date = PyObject
PyDateTime_Time = PyObject
PyDateTime_DateTime = PyObject
Modified: pypy/trunk/pypy/module/cpyext/state.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/state.py (original)
+++ pypy/trunk/pypy/module/cpyext/state.py Mon Nov 29 00:15:20 2010
@@ -5,8 +5,6 @@
class State:
- datetimeAPI = None # used in tests
-
def __init__(self, space):
self.space = space
self.reset()
Modified: pypy/trunk/pypy/module/cpyext/test/test_arraymodule.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_arraymodule.py (original)
+++ pypy/trunk/pypy/module/cpyext/test/test_arraymodule.py Mon Nov 29 00:15:20 2010
@@ -14,6 +14,7 @@
arr.append(4)
assert arr.tolist() == [1, 2, 3, 4]
assert len(arr) == 4
+ self.cleanup_references()
def test_iter(self):
module = self.import_module(name='array')
@@ -22,6 +23,7 @@
for i in arr:
sum += i
assert sum == 6
+ self.cleanup_references()
def test_index(self):
module = self.import_module(name='array')
@@ -32,6 +34,7 @@
assert arr.tolist() == [1,2,4]
arr[2] = 99
assert arr.tolist() == [1,2,99]
+ self.cleanup_references()
def test_slice_get(self):
module = self.import_module(name='array')
@@ -40,3 +43,4 @@
assert arr[1:].tolist() == [2,3,4]
assert arr[:2].tolist() == [1,2]
assert arr[1:3].tolist() == [2,3]
+ self.cleanup_references()
Modified: pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_cpyext.py (original)
+++ pypy/trunk/pypy/module/cpyext/test/test_cpyext.py Mon Nov 29 00:15:20 2010
@@ -1,10 +1,12 @@
import sys
+import weakref
import os.path
import py
from pypy.conftest import gettestobjspace
from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import interp2app
from pypy.rpython.lltypesystem import rffi, lltype, ll2ctypes
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.translator import platform
@@ -79,6 +81,23 @@
self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
class LeakCheckingTest(object):
+ @staticmethod
+ def cleanup_references(space):
+ state = space.fromcache(RefcountState)
+
+ import gc; gc.collect()
+ # Clear all lifelines, objects won't resurrect
+ for w_obj, obj in state.lifeline_dict._dict.items():
+ if w_obj not in state.py_objects_w2r:
+ state.lifeline_dict.set(w_obj, None)
+ del obj
+ import gc; gc.collect()
+
+ for w_obj in state.non_heaptypes_w:
+ Py_DecRef(space, w_obj)
+ state.non_heaptypes_w[:] = []
+ state.reset_borrowed_references()
+
def check_and_print_leaks(self):
# check for sane refcnts
import gc
@@ -89,13 +108,6 @@
lost_objects_w = identity_dict()
lost_objects_w.update((key, None) for key in self.frozen_refcounts.keys())
- # Clear all lifelines, objects won't resurrect
- for w_obj, obj in state.lifeline_dict._dict.items():
- if w_obj not in state.py_objects_w2r:
- state.lifeline_dict.set(w_obj, None)
- del obj
- gc.collect()
-
for w_obj, obj in state.py_objects_w2r.iteritems():
base_refcnt = self.frozen_refcounts.get(w_obj)
delta = obj.c_ob_refcnt
@@ -105,7 +117,12 @@
if delta != 0:
leaking = True
print >>sys.stderr, "Leaking %r: %i references" % (w_obj, delta)
- lifeline = state.lifeline_dict.get(w_obj)
+ try:
+ weakref.ref(w_obj)
+ except TypeError:
+ lifeline = None
+ else:
+ lifeline = state.lifeline_dict.get(w_obj)
if lifeline is not None:
refcnt = lifeline.pyo.c_ob_refcnt
if refcnt > 0:
@@ -137,6 +154,8 @@
state = cls.space.fromcache(RefcountState)
state.non_heaptypes_w[:] = []
+ cls.w_cleanup_references = cls.space.wrap(interp2app(cls.cleanup_references))
+
def compile_module(self, name, **kwds):
"""
Build an extension module linked against the cpyext api library.
@@ -256,13 +275,7 @@
def teardown_method(self, func):
for name in self.imported_module_names:
self.unimport_module(name)
- state = self.space.fromcache(RefcountState)
- for w_obj in state.non_heaptypes_w:
- Py_DecRef(self.space, w_obj)
- state.non_heaptypes_w[:] = []
- state.reset_borrowed_references()
- from pypy.module.cpyext import cdatetime
- cdatetime.datetimeAPI_dealloc(self.space)
+ self.cleanup_references(self.space)
if self.check_and_print_leaks():
assert False, "Test leaks or loses object(s)."
Modified: pypy/trunk/pypy/module/cpyext/test/test_datetime.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_datetime.py (original)
+++ pypy/trunk/pypy/module/cpyext/test/test_datetime.py Mon Nov 29 00:15:20 2010
@@ -92,9 +92,20 @@
PyDateTimeAPI->TimeType,
PyDateTimeAPI->DeltaType);
"""),
+ ("clear_types", "METH_NOARGS",
+ """
+ Py_DECREF(PyDateTimeAPI->DateType);
+ Py_DECREF(PyDateTimeAPI->DateTimeType);
+ Py_DECREF(PyDateTimeAPI->TimeType);
+ Py_DECREF(PyDateTimeAPI->DeltaType);
+ Py_RETURN_NONE;
+ """
+ )
])
import datetime
assert module.get_types() == (datetime.date,
datetime.datetime,
datetime.time,
datetime.timedelta)
+ module.clear_types()
+ self.cleanup_references()
Modified: pypy/trunk/pypy/module/cpyext/test/test_typeobject.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_typeobject.py (original)
+++ pypy/trunk/pypy/module/cpyext/test/test_typeobject.py Mon Nov 29 00:15:20 2010
@@ -20,6 +20,7 @@
assert type(obj) is module.fooType
print "type of obj has type", type(type(obj))
print "type of type of obj has type", type(type(type(obj)))
+ self.cleanup_references()
def test_typeobject_method_descriptor(self):
module = self.import_module(name='foo')
@@ -38,6 +39,7 @@
print obj.foo
assert obj.foo == 42
assert obj.int_member == obj.foo
+ self.cleanup_references()
def test_typeobject_data_member(self):
module = self.import_module(name='foo')
@@ -54,6 +56,7 @@
raises(SystemError, "obj.broken_member = 42")
assert module.fooType.broken_member.__doc__ is None
assert module.fooType.object_member.__doc__ == "A Python object."
+ self.cleanup_references()
def test_typeobject_object_member(self):
module = self.import_module(name='foo')
@@ -74,6 +77,7 @@
obj.set_foo = 32
assert obj.foo == 32
+ self.cleanup_references()
def test_typeobject_string_member(self):
module = self.import_module(name='foo')
@@ -91,6 +95,7 @@
assert obj.char_member == "a"
raises(TypeError, "obj.char_member = 'spam'")
raises(TypeError, "obj.char_member = 42")
+ self.cleanup_references()
def test_staticmethod(self):
module = self.import_module(name="foo")
@@ -98,6 +103,7 @@
assert obj.foo == 42
obj2 = obj.create()
assert obj2.foo == 42
+ self.cleanup_references()
def test_new(self):
module = self.import_module(name='foo')
@@ -118,7 +124,8 @@
return self
assert fuu2(u"abc").baz().escape()
raises(TypeError, module.fooType.object_member.__get__, 1)
-
+ self.cleanup_references()
+
def test_init(self):
module = self.import_module(name="foo")
newobj = module.FuuType()
@@ -137,6 +144,7 @@
newobj = Fuu2()
assert newobj.get_val() == 42
assert newobj.foobar == 32
+ self.cleanup_references()
def test_metatype(self):
module = self.import_module(name='foo')
@@ -145,6 +153,7 @@
assert isinstance(x, type)
assert isinstance(x, module.MetaType)
x()
+ self.cleanup_references()
def test_metaclass_compatible(self):
# metaclasses should not conflict here
@@ -153,7 +162,9 @@
assert type(module.fooType).__mro__ == (type, object)
y = module.MetaType('other', (module.fooType,), {})
assert isinstance(y, module.MetaType)
- y()
+ x = y()
+ del x, y
+ self.cleanup_references()
def test_sre(self):
module = self.import_module(name='_sre')
@@ -172,17 +183,21 @@
assert "groupdict" in dir(m)
re._cache.clear()
re._cache_repl.clear()
+ del prog, m
+ self.cleanup_references()
def test_init_error(self):
module = self.import_module("foo")
raises(ValueError, module.InitErrType)
-
+ self.cleanup_references()
+
def test_cmps(self):
module = self.import_module("comparisons")
cmpr = module.CmpType()
assert cmpr == 3
assert cmpr != 42
-
+ self.cleanup_references()
+
def test_hash(self):
module = self.import_module("comparisons")
cmpr = module.CmpType()
@@ -191,6 +206,7 @@
d[cmpr] = 72
assert d[cmpr] == 72
assert d[3] == 72
+ self.cleanup_references()
def test_descriptor(self):
module = self.import_module("foo")
@@ -205,8 +221,8 @@
assert obj.y == (prop, 2)
del obj.x
assert obj.z == prop
+ self.cleanup_references()
- @py.test.mark.dont_track_allocations
def test_tp_dict(self):
foo = self.import_module("foo")
module = self.import_extension('test', [
@@ -227,6 +243,7 @@
])
obj = foo.new()
assert module.read_tp_dict(obj) == foo.fooType.copy
+ self.cleanup_references()
class TestTypes(BaseApiTest):
Modified: pypy/trunk/pypy/module/cpyext/typeobject.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/typeobject.py (original)
+++ pypy/trunk/pypy/module/cpyext/typeobject.py Mon Nov 29 00:15:20 2010
@@ -433,12 +433,6 @@
finish_type_1(space, pto)
finish_type_2(space, pto, w_type)
- if space.type(w_type).is_cpytype():
- # XXX Types with a C metatype are never freed, try to see why...
- render_immortal(pto, w_type)
- lltype.render_immortal(pto)
- lltype.render_immortal(pto.c_tp_name)
-
pto.c_tp_basicsize = rffi.sizeof(typedescr.basestruct)
if pto.c_tp_base:
if pto.c_tp_base.c_tp_basicsize > pto.c_tp_basicsize:
@@ -544,25 +538,12 @@
w_obj.ready()
finish_type_2(space, py_type, w_obj)
- render_immortal(py_type, w_obj)
state = space.fromcache(RefcountState)
state.non_heaptypes_w.append(w_obj)
return w_obj
-def render_immortal(py_type, w_obj):
- lltype.render_immortal(py_type.c_tp_bases)
- lltype.render_immortal(py_type.c_tp_mro)
-
- assert isinstance(w_obj, W_TypeObject)
- if w_obj.is_cpytype():
- lltype.render_immortal(py_type.c_tp_dict)
- else:
- lltype.render_immortal(py_type.c_tp_name)
- if not w_obj.is_cpytype() and w_obj.is_heaptype():
- lltype.render_immortal(py_type)
-
def finish_type_1(space, pto):
"""
Sets up tp_bases, necessary before creating the interpreter type.
More information about the Pypy-commit
mailing list