[pypy-svn] r57205 - in pypy/dist/pypy: interpreter module/__builtin__ module/gc/test

arigo at codespeak.net arigo at codespeak.net
Tue Aug 12 13:52:28 CEST 2008


Author: arigo
Date: Tue Aug 12 13:52:28 2008
New Revision: 57205

Modified:
   pypy/dist/pypy/interpreter/baseobjspace.py
   pypy/dist/pypy/interpreter/typedef.py
   pypy/dist/pypy/module/__builtin__/interp_classobj.py
   pypy/dist/pypy/module/gc/test/test_gc.py
Log:
Fix the __del__ handling of old-style instances
to match the __del__ handling of new-style instances,
in particular regarding the usage of a queue to avoid
crashes if the app-level method is called immediately.


Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py	Tue Aug 12 13:52:28 2008
@@ -138,6 +138,23 @@
             self.setweakref(lifeline.space, None)
             lifeline.clear_all_weakrefs()
 
+    __already_enqueued_for_destruction = False
+
+    def _enqueue_for_destruction(self, space):
+        """Put the object in the destructor queue of the space.
+        At a later, safe point in time, UserDelAction will use
+        space.userdel() to call the object's app-level __del__ method.
+        """
+        # this function always resurect the object, so when
+        # running on top of CPython we must manually ensure that
+        # we enqueue it only once
+        if not we_are_translated():
+            if self.__already_enqueued_for_destruction:
+                return
+            self.__already_enqueued_for_destruction = True
+        self.clear_all_weakrefs()
+        space.user_del_action.register_dying_object(self)
+
     def _call_builtin_destructor(self):
         pass     # method overridden in typedef.py
 

Modified: pypy/dist/pypy/interpreter/typedef.py
==============================================================================
--- pypy/dist/pypy/interpreter/typedef.py	(original)
+++ pypy/dist/pypy/interpreter/typedef.py	Tue Aug 12 13:52:28 2008
@@ -9,7 +9,7 @@
     DescrMismatch
 from pypy.interpreter.error import OperationError
 from pypy.tool.sourcetools import compile2, func_with_new_name
-from pypy.rlib.objectmodel import instantiate, we_are_translated
+from pypy.rlib.objectmodel import instantiate
 from pypy.rlib.rarithmetic import intmask
 
 class TypeDef:
@@ -248,17 +248,8 @@
 
     if "del" in features:
         class Proto(object):
-            _del_was_called = False
             def __del__(self):
-                # the logic below always resurect the objects, so when
-                # running on top of CPython we must manually ensure that
-                # we do it only once
-                if not we_are_translated():
-                    if self._del_was_called:
-                        return
-                    self._del_was_called = True
-                self.clear_all_weakrefs()
-                self.space.user_del_action.register_dying_object(self)
+                self._enqueue_for_destruction(self.space)
         # if the base class needs its own interp-level __del__,
         # we override the _call_builtin_destructor() method to invoke it
         # after the app-level destructor.

Modified: pypy/dist/pypy/module/__builtin__/interp_classobj.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/interp_classobj.py	(original)
+++ pypy/dist/pypy/module/__builtin__/interp_classobj.py	Tue Aug 12 13:52:28 2008
@@ -630,6 +630,13 @@
                                  space.wrap("instance has no next() method"))
         return space.call_function(w_func)
 
+    def descr_del(self, space):
+        # Note that this is called from executioncontext.UserDelAction
+        # via the space.userdel() method.
+        w_func = self.getattr(space, space.wrap('__del__'), False)
+        if w_func is not None:
+            space.call_function(w_func)
+
 rawdict = {}
 
 # unary operations
@@ -719,20 +726,11 @@
     next = interp2app(W_InstanceObject.descr_next,
                       unwrap_spec=['self', ObjSpace]),
     __weakref__ = make_weakref_descr(W_InstanceObject),
+    __del__ = interp2app(W_InstanceObject.descr_del,
+                         unwrap_spec=['self', ObjSpace]),
     **rawdict
 )
 
 class W_InstanceObjectWithDel(W_InstanceObject):
     def __del__(self):
-        self.clear_all_weakrefs()
-        try:
-            self.descr_del()
-        except OperationError, e:
-            e.write_unraisable(self.space, 'method __del__ of ', self)
-            e.clear(self.space)   # break up reference cycles
-
-    def descr_del(self):
-        space = self.space
-        w_func = self.getattr(space, space.wrap('__del__'), False)
-        if w_func is not None:
-            space.call_function(w_func)
+        self._enqueue_for_destruction(self.space)

Modified: pypy/dist/pypy/module/gc/test/test_gc.py
==============================================================================
--- pypy/dist/pypy/module/gc/test/test_gc.py	(original)
+++ pypy/dist/pypy/module/gc/test/test_gc.py	Tue Aug 12 13:52:28 2008
@@ -5,6 +5,7 @@
 
     def test_disable_finalizers(self):
         import gc
+
         class X(object):
             created = 0
             deleted = 0
@@ -12,18 +13,31 @@
                 X.created += 1
             def __del__(self):
                 X.deleted += 1
+
+        class OldX:
+            created = 0
+            deleted = 0
+            def __init__(self):
+                OldX.created += 1
+            def __del__(self):
+                OldX.deleted += 1
+
         def runtest(should_be_enabled):
+            runtest1(should_be_enabled, X)
+            runtest1(should_be_enabled, OldX)
+
+        def runtest1(should_be_enabled, Cls):
             gc.collect()
             if should_be_enabled:
-                assert X.deleted == X.created
+                assert Cls.deleted == Cls.created
             else:
-                old_deleted = X.deleted
-            X(); X(); X()
+                old_deleted = Cls.deleted
+            Cls(); Cls(); Cls()
             gc.collect()
             if should_be_enabled:
-                assert X.deleted == X.created
+                assert Cls.deleted == Cls.created
             else:
-                assert X.deleted == old_deleted
+                assert Cls.deleted == old_deleted
 
         runtest(True)
         gc.disable_finalizers()



More information about the Pypy-commit mailing list