[pypy-commit] pypy default: cpyext: Correctly invalidate the type cache when tp_dict is updated directly.

amauryfa noreply at buildbot.pypy.org
Mon Mar 11 22:33:56 CET 2013


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: 
Changeset: r62297:4e520ab34e44
Date: 2013-03-11 22:33 +0100
http://bitbucket.org/pypy/pypy/changeset/4e520ab34e44/

Log:	cpyext: Correctly invalidate the type cache when tp_dict is updated
	directly. Fixes issue1106.

diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -85,6 +85,8 @@
     """Base class for all cpyext tests."""
     spaceconfig = dict(usemodules=['cpyext', 'thread', '_rawffi', 'array',
                                    'itertools', 'rctime', 'binascii'])
+    spaceconfig['std.withmethodcache'] = True
+
     enable_leak_checking = True
 
     @staticmethod
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -288,7 +288,38 @@
         class C(object):
             pass
         assert module.name_by_heaptype(C) == "C"
-        
+
+    def test_type_dict(self):
+        foo = self.import_module("foo")
+        module = self.import_extension('test', [
+           ("hack_tp_dict", "METH_O",
+            '''
+                 PyTypeObject *type = args->ob_type;
+                 PyObject *a1 = PyLong_FromLong(1);
+                 PyObject *a2 = PyLong_FromLong(2);
+                 PyObject *value;
+
+                 if (PyDict_SetItemString(type->tp_dict, "a",
+                         a1) < 0)
+                     return NULL;
+                 Py_DECREF(a1);
+                 PyType_Modified(type);
+                 value = PyObject_GetAttrString(type, "a");
+                 Py_DECREF(value);
+
+                 if (PyDict_SetItemString(type->tp_dict, "a",
+                         a2) < 0)
+                     return NULL;
+                 Py_DECREF(a2);
+                 PyType_Modified(type);
+                 value = PyObject_GetAttrString(type, "a");
+                 return value;
+             '''
+             )
+            ])
+        obj = foo.new()
+        assert module.hack_tp_dict(obj) == 2
+
 
 class TestTypes(BaseApiTest):
     def test_type_attributes(self, space, api):
@@ -326,7 +357,7 @@
         w_obj = api._PyType_Lookup(w_type, space.wrap("__invalid"))
         assert w_obj is None
         assert api.PyErr_Occurred() is None
-    
+
 class AppTestSlots(AppTestCpythonExtensionBase):
     def test_some_slots(self):
         module = self.import_extension('foo', [
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -729,6 +729,9 @@
     subtypes.  This function must be called after any manual
     modification of the attributes or base classes of the type.
     """
-    # PyPy already takes care of direct modifications to type.__dict__
-    # (which is a W_DictProxyObject).
-    pass
+    # Invalidate the type cache in case of a builtin type.
+    if not isinstance(w_obj, W_TypeObject):
+        return
+    if w_obj.is_cpytype():
+        w_obj.mutated(None)
+    
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -54,10 +54,11 @@
                 raise
             if not w_type.is_cpytype():
                 raise
-            # xxx obscure workaround: allow cpyext to write to type->tp_dict
-            # xxx even in the case of a builtin type.
-            # xxx like CPython, we assume that this is only done early after
-            # xxx the type is created, and we don't invalidate any cache.
+            # Allow cpyext to write to type->tp_dict even in the case
+            # of a builtin type.
+            # Like CPython, we assume that this is only done early
+            # after the type is created, and we don't invalidate any
+            # cache.  User code shoud call PyType_Modified().
             w_type.dict_w[key] = w_value
 
     def setdefault(self, w_dict, w_key, w_default):
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -161,7 +161,7 @@
         generic mutation.
         """
         space = w_self.space
-        assert w_self.is_heaptype()
+        assert w_self.is_heaptype() or w_self.is_cpytype()
         if (not space.config.objspace.std.withtypeversion and
             not space.config.objspace.std.getattributeshortcut and
             not space.config.objspace.std.withidentitydict and


More information about the pypy-commit mailing list