Author: Amaury Forgeot d'Arc <amaur...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit