[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