Author: Armin Rigo <[email protected]>
Branch: gc-del
Changeset: r63550:ea7053cee8b3
Date: 2013-04-22 17:32 +0200
http://bitbucket.org/pypy/pypy/changeset/ea7053cee8b3/

Log:    Fix old-style classes for the new model of __del__

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -26,7 +26,7 @@
 class W_Root(object):
     """This is the abstract root class of all wrapped objects that live
     in a 'normal' object space like StdObjSpace."""
-    __slots__ = ()
+    _attrs_ = ()
     _settled_ = True
     user_overridden_class = False
 
@@ -220,7 +220,7 @@
 class Wrappable(W_Root):
     """A subclass of Wrappable is an internal, interpreter-level class
     that can nevertheless be exposed at application-level by space.wrap()."""
-    __slots__ = ()
+    _attrs_ = ()
     _settled_ = True
 
     def __spacebind__(self, space):
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -638,9 +638,6 @@
 def make_weakref_descr(cls):
     """Make instances of the Wrappable subclass 'cls' weakrefable.
     This returns the '__weakref__' desctriptor to use for the TypeDef.
-    Note that if the class also defines a custom '__del__', the
-    __del__ should call self.clear_all_weakrefs() before it clears
-    the resources used by the object.
     """
     # force the interface into the given cls
     def getweakref(self):
diff --git a/pypy/module/__builtin__/interp_classobj.py 
b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -6,7 +6,7 @@
 from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, 
descr_set_dict
 from rpython.rlib.objectmodel import compute_identity_hash
 from rpython.rlib.debug import make_sure_not_resized
-from rpython.rlib import jit
+from rpython.rlib import rgc, jit
 
 
 def raise_type_err(space, argument, expected, w_obj):
@@ -57,10 +57,7 @@
 
     def instantiate(self, space):
         cache = space.fromcache(Cache)
-        if self.lookup(space, '__del__') is not None:
-            w_inst = cache.cls_with_del(space, self)
-        else:
-            w_inst = cache.cls_without_del(space, self)
+        w_inst = cache.class_of_instance(space, self)
         return w_inst
 
     def getdict(self, space):
@@ -152,11 +149,6 @@
             elif name == "__bases__":
                 self.setbases(space, w_value)
                 return
-            elif name == "__del__":
-                if self.lookup(space, name) is None:
-                    msg = ("a __del__ method added to an existing class will "
-                           "not be called")
-                    space.warn(space.wrap(msg), space.w_RuntimeWarning)
         space.setitem(self.w_dict, w_attr, w_value)
 
     def descr_delattr(self, space, w_attr):
@@ -204,11 +196,10 @@
 class Cache:
     def __init__(self, space):
         from pypy.interpreter.typedef import _usersubclswithfeature
-        # evil
-        self.cls_without_del = _usersubclswithfeature(
+        # evil: "class_of_instance" is a subclass of W_InstanceObject
+        # which has additionally the "dict" and "weakref" capabilities
+        self.class_of_instance = _usersubclswithfeature(
                 space.config, W_InstanceObject, "dict", "weakref")
-        self.cls_with_del = _usersubclswithfeature(
-                space.config, self.cls_without_del, "del")
 
 
 def class_descr_call(space, w_self, __args__):
@@ -321,6 +312,8 @@
         self.user_setup(space, space.gettypeobject(self.typedef))
         assert isinstance(w_class, W_ClassObject)
         self.w_class = w_class
+        if self.getattr_from_class(space, '__del__') is not None:
+            rgc.register_finalizer(self.finalizer)
 
     def user_setup(self, space, w_subtype):
         self.space = space
@@ -391,13 +384,8 @@
             if name == '__class__':
                 self.set_oldstyle_class(space, w_value)
                 return
-            if name == '__del__' and w_meth is None:
-                cache = space.fromcache(Cache)
-                if (not isinstance(self, cache.cls_with_del)
-                    and self.getdictvalue(space, '__del__') is None):
-                    msg = ("a __del__ method added to an instance with no "
-                           "__del__ in the class will not be called")
-                    space.warn(space.wrap(msg), space.w_RuntimeWarning)
+            if name == '__del__':
+                rgc.register_finalizer(self.finalizer)
         if w_meth is not None:
             space.call_function(w_meth, w_name, w_value)
         else:
@@ -700,9 +688,8 @@
                                  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.
+    def finalizer(self):
+        space = self.space
         w_func = self.getdictvalue(space, '__del__')
         if w_func is None:
             w_func = self.getattr_from_class(space, '__del__')
@@ -783,7 +770,6 @@
     __pow__ = interp2app(W_InstanceObject.descr_pow),
     __rpow__ = interp2app(W_InstanceObject.descr_rpow),
     next = interp2app(W_InstanceObject.descr_next),
-    __del__ = interp2app(W_InstanceObject.descr_del),
     __exit__ = interp2app(W_InstanceObject.descr_exit),
     __dict__ = dict_descr,
     **rawdict
diff --git a/pypy/module/__builtin__/test/test_classobj.py 
b/pypy/module/__builtin__/test/test_classobj.py
--- a/pypy/module/__builtin__/test/test_classobj.py
+++ b/pypy/module/__builtin__/test/test_classobj.py
@@ -670,6 +670,22 @@
         gc.collect()
         gc.collect()
         assert l == [1, 1]
+        class C:
+            pass
+        c = C()
+        c.__del__ = lambda: l.append(2)
+        c = None
+        gc.collect()
+        gc.collect()
+        gc.collect()
+        assert l == [1, 1, 2]
+        d = A()
+        d.__del__ = lambda: l.append(3)
+        d = None
+        gc.collect()
+        gc.collect()
+        gc.collect()
+        assert l == [1, 1, 2, 3]
 
     def test_catch_attributeerror_of_descriptor(self):
         def booh(self):
@@ -745,21 +761,16 @@
         assert Y() != X()
 
     def test_assignment_to_del(self):
-        import sys
-        if not hasattr(sys, 'pypy_objspaceclass'):
-            skip("assignment to __del__ doesn't give a warning in CPython")
-
+        # assignment to __del__ no longer gives a warning
         import warnings
-        
         warnings.simplefilter('error', RuntimeWarning)
         try:
             class X:
                 pass
-            raises(RuntimeWarning, "X.__del__ = lambda self: None")
+            X.__del__ = lambda self: None
             class Y:
                 pass
-            raises(RuntimeWarning, "Y().__del__ = lambda self: None")
-            # but the following works
+            Y().__del__ = lambda self: None
             class Z:
                 def __del__(self):
                     pass
diff --git a/pypy/module/_cffi_backend/cdataobj.py 
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -318,7 +318,6 @@
         cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
         W_CData.__init__(self, space, cdata, ctype)
 
-    @rgc.must_be_light_finalizer
     def __del__(self):
         lltype.free(self._cdata, flavor='raw')
 
diff --git a/pypy/module/_cffi_backend/libraryobj.py 
b/pypy/module/_cffi_backend/libraryobj.py
--- a/pypy/module/_cffi_backend/libraryobj.py
+++ b/pypy/module/_cffi_backend/libraryobj.py
@@ -7,6 +7,7 @@
 
 from rpython.rtyper.lltypesystem import rffi
 from rpython.rlib.rdynload import DLLHANDLE, dlopen, dlsym, dlclose, 
DLOpenError
+from rpython.rlib import rgc
 
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypeobj import W_CType
@@ -28,8 +29,9 @@
                                       "cannot load library %s: %s",
                                       filename, e.msg)
         self.name = filename
+        rgc.register_finalizer(self.finalizer)
 
-    def __del__(self):
+    def finalizer(self):
         h = self.handle
         if h != rffi.cast(DLLHANDLE, 0):
             self.handle = rffi.cast(DLLHANDLE, 0)
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -2,7 +2,7 @@
 import os
 import stat
 import errno
-from rpython.rlib import streamio
+from rpython.rlib import streamio, rgc
 from rpython.rlib.rarithmetic import r_longlong
 from rpython.rlib.rstring import StringBuilder
 from pypy.module._file.interp_stream import W_AbstractStream, StreamErrors
@@ -38,7 +38,7 @@
 
     def __init__(self, space):
         self.space = space
-        self.register_finalizer()
+        rgc.register_finalizer(self.finalizer)
 
     def finalizer(self):
         try:
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to